📄 cacti.cpp
字号:
/*****************************************************************************
KillWindow()
Deletes the DC, RC, and Window, and restores the original display.
*****************************************************************************/
BOOL KillWindow()
{
// restore the original display if we're in fullscreen mode
if (g_isFullscreen)
{
ChangeDisplaySettings(NULL, 0);
ShowCursor(TRUE);
}
// if we have an RC, release it
if (g_hrc)
{
// release the RC
if (!wglMakeCurrent(NULL,NULL))
{
MessageBox(NULL, "Unable to release rendering context", "Error", MB_OK | MB_ICONINFORMATION);
}
// delete the RC
if (!wglDeleteContext(g_hrc))
{
MessageBox(NULL, "Unable to delete rendering context", "Error", MB_OK | MB_ICONINFORMATION);
}
g_hrc = NULL;
}
// release the DC if we have one
if (g_hdc && !ReleaseDC(g_hwnd, g_hdc))
{
MessageBox(NULL, "Unable to release device context", "Error", MB_OK | MB_ICONINFORMATION);
g_hdc = NULL;
}
// destroy the window if we have a valid handle
if (g_hwnd && !DestroyWindow(g_hwnd))
{
MessageBox(NULL, "Unable to destroy window", "Error", MB_OK | MB_ICONINFORMATION);
g_hwnd = NULL;
}
// unregister our class so we can create a new one if we need to
if (!UnregisterClass(WND_CLASS_NAME, g_hInstance))
{
MessageBox(NULL, "Unable to unregister window class", "Error", MB_OK | MB_ICONINFORMATION);
g_hInstance = NULL;
}
return TRUE;
} // end KillWindow()
/*****************************************************************************
ResizeScene()
Called once when the application starts and again every time the window is
resized by the user.
*****************************************************************************/
GLvoid ResizeScene(GLsizei width, GLsizei height)
{
// avoid divide by zero
if (height==0)
{
height=1;
}
// set the viewport to the new dimensions
glViewport(0, 0, width, height);
// select the projection matrix and clear it out
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// set the perspective with the appropriate aspect ratio
gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 0.1f, 1000.0f);
// select modelview matrix
glMatrixMode(GL_MODELVIEW);
} // end ResizeScene()
/*****************************************************************************
InitializeScene()
Performs one-time application-specific setup. Returns FALSE on any failure.
*****************************************************************************/
BOOL InitializeScene()
{
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_CULL_FACE);
glCullFace(GL_CCW);
LoadTexture("cactus.bmp", g_cactus);
LoadTexture("sand.bmp", g_sand);
// make the background look like the sky
float blue[4] = { 0.5, 0.5, 1.0, 0.0 };
glClearColor(0.5, 0.5, 1.0, 0.0);
// use a distant fog for a haze effect
glEnable(GL_FOG);
glFogfv(GL_FOG_COLOR, blue);
glFogf(GL_FOG_MODE, GL_EXP2);
glFogf(GL_FOG_START, 200);
glFogf(GL_FOG_END, 1000);
glFogf(GL_FOG_DENSITY, 0.002f);
// load terrain arrays
InitializeTerrain();
// check for the compiled array extensions
char *extList = (char *) glGetString(GL_EXTENSIONS);
if (extList && strstr(extList, "GL_EXT_compiled_vertex_array"))
{
// get the address of the compiled array extensions
glLockArraysEXT = (PFNGLLOCKARRAYSEXTPROC) wglGetProcAddress("glLockArraysEXT");
glUnlockArraysEXT = (PFNGLUNLOCKARRAYSEXTPROC) wglGetProcAddress("glUnlockArraysEXT");
}
return TRUE;
} // end InitializeScene()
/*****************************************************************************
InitializeTerrain()
Create vertex arrays containing terrain data
*****************************************************************************/
void InitializeTerrain()
{
// used to track current entry in the index array
int index = 0;
int currentVertex;
// loop through all of the heightfield points, randomly generating height values
for (int z = 0; z < MAP_Z; z++)
{
for (int x = 0; x < MAP_X; x++)
{
// set the world coordinates of this point
g_terrain[x + z * MAP_X][0] = float(x)*MAP_SCALE;
g_terrain[x + z * MAP_X][1] = 5.0f + FRAND * 5.0f;
g_terrain[x + z * MAP_X][2] = -float(z)*MAP_SCALE;
// vertices are numbered left to right, top to bottom
currentVertex = z * MAP_X + x;
// set the values in the color array
g_colorArray[currentVertex][0] = g_colorArray[currentVertex][1] =
g_colorArray[currentVertex][2] = g_terrain[x + z * MAP_X][1] / 20.0f + 0.5f;
// set the values in the texture coordinate array. since the texture
// is tiled over each "square", we can use texture wrapping
g_texcoordArray[currentVertex][0] = (float) x;
g_texcoordArray[currentVertex][1] = (float) z;
}
}
// loop over all vertices in the terrain map
for (z = 0; z < MAP_Z-1; z++)
{
for (int x = 0; x < MAP_X; x++)
{
// add next two elements to the triangle strip
currentVertex = z * MAP_X + x;
g_indexArray[index++] = currentVertex + MAP_X;
g_indexArray[index++] = currentVertex;
}
}
// enable the vertex arrays being used
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// pass the pointers to OpenGL
glVertexPointer(3, GL_FLOAT, 0, g_terrain);
glColorPointer(3, GL_FLOAT, 0, g_colorArray);
glTexCoordPointer(2, GL_FLOAT, 0, g_texcoordArray);
} // end InitializeArrays()
/*****************************************************************************
DisplayScene()
The work of the application is done here. This is called every frame, and
handles the actual rendering of the scene.
*****************************************************************************/
BOOL DisplayScene()
{
// used to track the orientation of the viewer
static GLfloat s_eye[] = { MAP_X * MAP_SCALE * 0.5, 8.0, -MAP_Z * MAP_SCALE * 0.5};
static GLfloat s_at[] = { 0.0, 0.0, 0.0 };
static GLfloat s_angle = -90.0;
float speed = 0.3f;
// check for rotation
if (g_keys[VK_LEFT])
{
s_angle -= 2.0;
}
if (g_keys[VK_RIGHT])
{
s_angle += 2.0;
}
// run if the shift key is pressed
if (KEY_DOWN(VK_SHIFT))
speed = speed * 2;
float rad = float(PI*s_angle/180.0f);
// check for forward and backward motion
if (g_keys[VK_UP])
{
s_eye[2] += (float)sin(rad) * speed;
s_eye[0] += (float)cos(rad) * speed;
}
if (g_keys[VK_DOWN])
{
s_eye[2] -= (float)sin(rad) * speed;
s_eye[0] -= (float)cos(rad) * speed;
}
// do bound's checking to make sure they don't leave the map
if (s_eye[0] < MAP_SCALE)
s_eye[0] = MAP_SCALE;
if (s_eye[0] > (MAP_X - 2) * MAP_SCALE)
s_eye[0] = (MAP_X - 2) * MAP_SCALE;
if (s_eye[2] < -(MAP_Z - 2) * MAP_SCALE)
s_eye[2] = -(MAP_Z - 2) * MAP_SCALE;
if (s_eye[2] > - MAP_SCALE)
s_eye[2] = -MAP_SCALE;
// set the eye position in relation to the ground
s_eye[1] = GetHeight(s_eye[0], s_eye[2]) + 2.0f;
//set the look at point to be at eye level in the direction the viewer is headed
s_at[0] = float(s_eye[0] + 100*cos(rad));
s_at[2] = float(s_eye[2] + 100*sin(rad));
s_at[1] = s_eye[1];
// set up the modelview matrix according to this viewer orientation
glLoadIdentity();
gluLookAt(s_eye[0], s_eye[1], s_eye[2],
s_at[0], s_at[1], s_at[2],
0.0, 1.0, 0.0
);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DrawSand();
DrawCacti();
return TRUE;
} // end DisplayScene()
/*****************************************************************************
Cleanup()
Called at the end of successful program execution.
*****************************************************************************/
BOOL Cleanup()
{
if (g_sand)
glDeleteTextures(1, &g_sand);
if (g_cactus)
glDeleteTextures(1, &g_cactus);
return TRUE;
} // end Cleanup()
/*****************************************************************************
DrawCacti()
Draw cacti as billboarded quads.
*****************************************************************************/
void DrawCacti()
{
// make sure the random numbers we generate are the same every time
srand(100);
// make sure the transparent part of the texture isn't drawn
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0);
// get the modelview matrix
float mat[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mat);
// get the right and up vectors
vector3_t right(mat[0], mat[4], mat[8]);
vector3_t up(mat[1], mat[5], mat[9]);
// select the cactus texture
glBindTexture(GL_TEXTURE_2D, g_cactus);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// draw all cacti
glBegin(GL_QUADS);
for (int n = 0; n < NUM_CACTI; n++)
{
// randomly size the cactus
float size = 5.0f + FRAND + 3.0f;
// pick a random position on the map
vector3_t pos(RAND_COORD((MAP_X - 1) * MAP_SCALE), 0.0, -RAND_COORD((MAP_Z - 1) * MAP_SCALE));
pos.y = GetHeight(pos.x, pos.z) + size - 0.5f;
// bottom left corner
glTexCoord2f(0.0, 0.0); glVertex3fv((pos + (right + up) * -size).v);
// bottom right corner
glTexCoord2f(1.0, 0.0); glVertex3fv((pos + (right - up) * size).v);
// top right corner
glTexCoord2f(1.0, 1.0); glVertex3fv((pos + (right + up) * size).v);
// top left corner
glTexCoord2f(0.0, 1.0); glVertex3fv((pos + (up - right) * size).v);
}
glEnd();
glDisable(GL_ALPHA);
glDisable(GL_BLEND);
} // end DrawCacti()
/****************************************************************************
DrawSand()
Draw the terrain
*****************************************************************************/
void DrawSand()
{
// select the sand texture
glBindTexture(GL_TEXTURE_2D, g_sand);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
if (glLockArraysEXT)
glLockArraysEXT(0, MAP_X * MAP_Z * 6);
// loop through all the triangle strips
for (int z = 0; z < MAP_Z-1; z++)
{
// draw the triangles in this strip
glDrawElements(GL_TRIANGLE_STRIP, MAP_X * 2, GL_UNSIGNED_INT, &g_indexArray[z * MAP_X * 2]);
}
// if the compiled arrays extension is available, unlock the arrays
if (glUnlockArraysEXT)
glUnlockArraysEXT();
} // end DrawSand()
/****************************************************************************
LoadTexture()
Loads the texture from the specified file and stores it in iTexture.
*****************************************************************************/
void LoadTexture(char *filename, GLuint &texture)
{
// get a texture object
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// load the bitmap
BITMAPINFOHEADER bitmapInfoHeader;
unsigned char *buffer = LoadBitmapFileWithAlpha(filename, &bitmapInfoHeader);
// set up the texture
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 4, bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
gluBuild2DMipmaps(GL_TEXTURE_2D, 4, bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// we're done with the bitmap data
free(buffer);
} // end LoadTexture()
/*****************************************************************************
GetHeight()
Returns the height of the terrain at (x, z)
*****************************************************************************/
float GetHeight(float x, float z)
{
// divide by the grid-spacing if it is not 1
float projCameraX = x / MAP_SCALE;
float projCameraZ = -z / MAP_SCALE;
// compute the height field coordinates (hflCol0, hflRow0) and
// (hflCol1, hflRow1) that identify the height field cell directly below the camera.
int hflCol0 = int(projCameraX);
int hflRow0 = int(projCameraZ);
int hflCol1 = hflCol0 + 1;
int hflRow1 = hflRow0 + 1;
// get the four corner heights of the cell from the height field
float h00 = g_terrain[hflCol0 + hflRow0*MAP_X][1];
float h01 = g_terrain[hflCol1 + hflRow0*MAP_X][1];
float h11 = g_terrain[hflCol1 + hflRow1*MAP_X][1];
float h10 = g_terrain[hflCol0 + hflRow1*MAP_X][1];
// calculate the position of the camera relative to the cell.
// note, that 0 <= tx, ty <= 1.
float tx = projCameraX - float(hflCol0);
float ty = projCameraZ - float(hflRow0);
// the next step is to perform a bilinear interpolation to compute the height
// of the terrain directly below the object.
float txty = tx * ty;
return h00 * (1.0f - ty - tx + txty)
+ h01 * (tx - txty)
+ h11 * txty
+ h10 * (ty - txty);
} // end GetHeight()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -