Выбрать главу

1. Rotate and translate the camera position according to user commands

2. Rotate the world around the origin in the opposite direction of the camera rotation (giving the illusion that the camera has been rotated)

3. Translate the world in the opposite manner that the camera has been translated (again, giving the illusion that the camera has moved)

This is pretty simple to implement. Let's start with the first stage (Rotation and translation of the camera).

if (keys[VK_RIGHT]) // Is The Right Arrow Being Pressed?

{

 yrot –= 1.5f; // Rotate The Scene To The Left

}

if (keys[VK_LEFT]) // Is The Left Arrow Being Pressed?

{

 yrot += 1.5f; // Rotate The Scene To The Right

}

if (keys[VK_UP]) // Is The Up Arrow Being Pressed?

{

 xpos –= (float)sin(heading*piover180) * 0.05f; // Move On The X-Plane Based On Player Direction

 zpos –= (float)cos(heading*piover180) * 0.05f; // Move On The Z-Plane Based On Player Direction

 if (walkbiasangle >= 359.0f) // Is walkbiasangle>=359?

 {

  walkbiasangle = 0.0f; // Make walkbiasangle Equal 0

 } else // Otherwise

 {

  walkbiasangle+= 10; // If walkbiasangle < 359 Increase It By 10

 }

 walkbias = (float)sin(walkbiasangle * piover180)/20.0f; // Causes The Player To Bounce

}

if (keys[VK_DOWN]) // Is The Down Arrow Being Pressed?

{

 xpos += (float)sin(heading*piover180) * 0.05f; // Move On The X-Plane Based On Player Direction

 zpos += (float)cos(heading*piover180) * 0.05f; // Move On The Z-Plane Based On Player Direction

 if (walkbiasangle <= 1.0f) // Is walkbiasangle<=1?

 {

  walkbiasangle = 359.0f; // Make walkbiasangle Equal 359

 } else // Otherwise

 {

  walkbiasangle-= 10; // If walkbiasangle > 1 Decrease It By 10

 }

 walkbias = (float)sin(walkbiasangle * piover180)/20.0f; // Causes The Player To Bounce

}

That was fairly simple. When either the left or right cursor key is pressed, the rotation variable yrot is incremented or decremented appropriatly. When the forward or backwards cursor key is pressed, a new location for the camera is calculated using the sine and cosine calculations (some trigonometry required :-). Piover180 is simply a conversion factor for converting between degrees and radians.

Next you ask me: What is this walkbias? It's a word I invented :-) It's basically an offset that occurs when a person walks around (head bobbing up and down like a buoy. It simply adjusts the camera's Y position with a sine wave. I had to put this in, as simply moving forwards and backwards didn't look to great.

Now that we have these variables down, we can proceed with steps two and three. This will be done in the display loop, as our program isn't complicated enough to merit a seperate function.

int DrawGLScene(GLvoid) // Draw The OpenGL Scene

{

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer

 glLoadIdentity(); // Reset The Current Matrix

 GLfloat x_m, y_m, z_m, u_m, v_m; // Floating Point For Temp X, Y, Z, U And V Vertices

 GLfloat xtrans = –xpos; // Used For Player Translation On The X Axis

 GLfloat ztrans = –zpos; // Used For Player Translation On The Z Axis

 GLfloat ytrans = –walkbias-0.25f; // Used For Bouncing Motion Up And Down

 GLfloat sceneroty = 360.0f – yrot; // 360 Degree Angle For Player Direction

 int numtriangles; // Integer To Hold The Number Of Triangles

 glRotatef(lookupdown,1.0f,0,0); // Rotate Up And Down To Look Up And Down

 glRotatef(sceneroty,0,1.0f,0); // Rotate Depending On Direction Player Is Facing

 glTranslatef(xtrans, ytrans, ztrans); // Translate The Scene Based On Player Position

 glBindTexture(GL_TEXTURE_2D, texture[filter]); // Select A Texture Based On filter

 numtriangles = sector1.numtriangles; // Get The Number Of Triangles In Sector 1

 // Process Each Triangle

 for (int loop_m = 0; loop_m < numtriangles; loop_m++) // Loop Through All The Triangles

 {

  glBegin(GL_TRIANGLES); // Start Drawing Triangles

   glNormal3f( 0.0f, 0.0f, 1.0f); // Normal Pointing Forward

   x_m = sector1.triangle[loop_m].vertex[0].x; // X Vertex Of 1st Point

   y_m = sector1.triangle[loop_m].vertex[0].y; // Y Vertex Of 1st Point

   z_m = sector1.triangle[loop_m].vertex[0].z; // Z Vertex Of 1st Point

   u_m = sector1.triangle[loop_m].vertex[0].u; // U Texture Coord Of 1st Point

   v_m = sector1.triangle[loop_m].vertex[0].v; // V Texture Coord Of 1st Point

   glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); // Set The TexCoord And Vertice

   x_m = sector1.triangle[loop_m].vertex[1].x; // X Vertex Of 2nd Point

   y_m = sector1.triangle[loop_m].vertex[1].y; // Y Vertex Of 2nd Point

   z_m = sector1.triangle[loop_m].vertex[1].z; // Z Vertex Of 2nd Point

   u_m = sector1.triangle[loop_m].vertex[1].u; // U Texture Coord Of 2nd Point

   v_m = sector1.triangle[loop_m].vertex[1].v; // V Texture Coord Of 2nd Point

   glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); // Set The TexCoord And Vertice

   x_m = sector1.triangle[loop_m].vertex[2].x; // X Vertex Of 3rd Point

   y_m = sector1.triangle[loop_m].vertex[2].y; // Y Vertex Of 3rd Point

   z_m = sector1.triangle[loop_m].vertex[2].z; // Z Vertex Of 3rd Point

   u_m = sector1.triangle[loop_m].vertex[2].u; // U Texture Coord Of 3rd Point

   v_m = sector1.triangle[loop_m].vertex[2].v; // V Texture Coord Of 3rd Point

   glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); // Set The TexCoord And Vertice

  glEnd(); // Done Drawing Triangles

 }

 return TRUE; // Jump Back

}

And voilà! We have drawn our first frame. This isn't exactly Quake but hey, we aren't exactly Carmack's or Abrash's. While running the program, you may want to press F, B, PgUp and PgDown to see added effects. PgUp/Down simply tilts the camera up and down (the same process as panning from side to side.) The texture included is simply a mud texture with a bumpmap of my school ID picture; that is, if NeHe decided to keep it :-).

So now you're probably thinking where to go next. Don't even consider using this code to make a full-blown 3D engine, since that's not what it's designed for. You'll probably want more than one sector in your game, especially if you're going to implement portals. You'll also want to have polygons with more than 3 vertices, again, essential for portal engines. My current implementation of this code allows for multiple sector loading and does backface culling (not drawing polygons that face away from the camera). I'll write a tutorial on that soon, but as it uses alot of math, I'm going to write a tutorial on matrices first.