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

The last line GL_COLOR_MATERIAL lets us add color to texture maps. If we don't enable material coloring, the textures will always be their original color. glColor3f(r,g,b) will have no affect on the coloring. So it's important to enable this.

 glEnable(GL_LIGHT0); // Quick And Dirty Lighting (Assumes Light0 Is Set Up)

 glEnable(GL_LIGHTING); // Enable Lighting

 glEnable(GL_COLOR_MATERIAL); // Enable Material Coloring

Finally we set the perspective correction to look nice, and we return TRUE letting our program know that initialization went OK.

 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Nice Perspective Correction

 return TRUE; // Initialization Went OK

}

Now for the drawing code. As usual, I got a little crazy with the math. No SIN, and COS, but it's still a little strange :) We start off as usual by clearing the screen and depth buffer.

Then we bind a texture to the cube. I could have added this line inside the display list code, but by leaving it outside the display list, I can change the texture whenever I want. If I added the line glBindTexture(GL_TEXTURE_2D, texture[0]) inside the display list code, the display list would be built with whatever texture I selected permanently mapped onto it.

int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing

{

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

 glBindTexture(GL_TEXTURE_2D, texture[0]); // Select The Texture

Now for the fun stuff. We have a loop called yloop. This loop is used to position the cubes on the Y axis (up and down). We want 5 rows of cubes up and down, so we make a loop from 1 to less than 6 (which is 5).

 for (yloop=1; yloop<6; yloop++) // Loop Through The Y Plane

 {

We have another loop called xloop. It's used to position the cubes on the X axis (left to right). The number of cubes drawn left to right depends on what row we're on. If we're on the top row, xloop will only go from 0 to 1 (drawing one cube). the next row xloop will go from 0 to 2 (drawing 2 cubes), etc.

  for (xloop=0;xloop<yloop;xloop++) // Loop Through The X Plane

  {

We reset our view with glLoadIdentity().

   glLoadIdentity(); // Reset The View

The next line translates to a specific spot on the screen. It looks confussing, but it's actually not. On the X axis, the following happens:

We move to the right 1.4 units so that the pyramid is in the center of the screen. Then we multiply xloop by 2.8 and add the 1.4 to it. (we multiply by 2.8 so that the cubes are not on top of eachother (2.8 is roughly the width of the cubes when they're rotated 45 degrees). Finally we subtract yloop*1.4. This moves the cubes left depending on what row we're on. If we didn't move to the left, the pyramid would line up on the left side (wouldn't really look a pyramid would it).

On the Y axis we subtract yloop from 6 otherwise the pyramid would be built upside down. Then we multiply the result by 2.4. Otherwise the cubes would be on top of eachother on the y axis (2.4 is roughly the height of each cube). Then we subtract 7 so that the pyramid starts at the bottom of the screen and is built upwards.

Finally, on the Z axis we move into the screen 20 units. That way the pyramid fits nicely on the screen.

   // Position The Cubes On The Screen

   glTranslatef(1.4f+(float(xloop)*2.8f)-(float(yloop)*1.4f), ((6.0f-float(yloop))*2.4f)-7.0f, -20.0f);

Now we rotate on the x axis. We'll tilt the cube towards the view by 45 degrees minus 2 multiplied by yloop. Perspective mode tilts the cubes automatically, so I subtract to compensate for the tilt. Not the best way to do it, but it works :)

Finally we add xrot. This gives us keyboard control over the angle. (fun to play around with).

After we've rotated on the x axis, we rotate 45 degrees on the y axis, and add yrot so we have keyboard control on the y axis.

   glRotatef(45.0f-(2.0f*yloop)+xrot,1.0f,0.0f,0.0f); // Tilt The Cubes Up And Down

   glRotatef(45.0f+yrot,0.0f,1.0f,0.0f); // Spin Cubes Left And Right

Next we select a box color (bright) before we actually draw the box portion of the cube. Notice we're using glColor3fv(). What this does is loads all three values (red, green, blue) from inside the {}'s at once and sets the color. 3fv stands for 3 values, floating point, v is a pointer to an array. The color we select is yloop-1 which gives us a different color for each row of the cubes. If we used xloop-1 we'd get a different color for each column.

   glColor3fv(boxcol[yloop-1]); // Select A Box Color

Now that the color is set, all we have to do is draw our box. Instead of writing out all the code to draw a box, all we do is call our display list. We do this with the command glCallList(box). box tells OpenGL to select the box display list. The box display list is the cube without its top.

The box will be drawn using the color we selected with glColor3fv(), at the position we translated to.

   glCallList(box); // Draw The Box

Now we select a top color (darker) before we draw the top of the box. If you actually wanted to make Q-Bert, you'd change this color whenever Q-Bert jumped on the box. The color depends on the row (yloop-1).

   glColor3fv(topcol[yloop-1]); // Select The Top Color

Finally, the only thing left to do is draw the top display list. This will add a darker colored lid to the box. That's it. Very easy!

   glCallList(top); // Draw The Top

  }

 }

 return TRUE; // Jump Back

}

The remaining changes have all been made in WinMain(). The code has been added right after our SwapBuffers(hDC) line. It check to see if we are pressing left, right, up or down, and moves the cubes accordingly.

  SwapBuffers(hDC); // Swap Buffers (Double Buffering)

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

  {

   yrot-=0.2f; // If So Spin Cubes Left

  }

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

  {

   yrot+=0.2f; // If So Spin Cubes Right

  }

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

  {

   xrot-=0.2f; // If So Tilt Cubes Up

  }

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

  {

   xrot+=0.2f; // If So Tilt Cubes Down

  }

Like all the previous tutorials, make sure the title at the top of the window is correct.

  if (keys[VK_F1]) // Is F1 Being Pressed?

  {

   keys[VK_F1]=FALSE; // If So Make Key FALSE

   KillGLWindow(); // Kill Our Current Window

   fullscreen=!fullscreen; // Toggle Fullscreen / Windowed Mode

   // Recreate Our OpenGL Window

   if (!CreateGLWindow("NeHe's Display List Tutorial", 640, 480, 16, fullscreen))  {

    return 0; // Quit If Window Was Not Created

   }

  }

 }

}

By the end of this tutorial you should have a good understanding of how display lists work, how to create them, and how to display them on the screen. Display lists are great. Not only do they simplify coding complex projects, they also give you that little bit of extra speed required to maintain high framerates.