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

There is probably a better way to sort structures, but qsort() works… It's quick, convenient and easy to use!

It's important to note, that if you wanted to use the glAlphaFunc() and glEnable(GL_ALPHA_TEST), sorting is not necessary. However, using the Alpha Function you are restricted to completely transparent or completely opaque blending, there is no in between. Sorting and using the Blendfunc() is a little more work, but it allows for semi-transparent objects.

 // Sort Objects By Distance: Beginning Address Of Our object Array *** MSDN CODE MODIFIED FOR THIS TUT ***

 // Number Of Elements To Sort

 // Size Of Each Element

 // Pointer To Our Compare Function

 qsort((void *) &object, level, sizeof(struct objects), (compfn)Compare );

}

The init code is same as always. The first two lines grab information about our window and our keyboard handler. We then use srand() to create a more random game based on the time. After that we load our TGA images and convert them to textures using LoadTGA(). The first 5 images are objects that will streak across the screen. Explode is our explosion animation, ground and sky make up the background scene, crosshair is the crosshair you see on the screen representing your current mouse location, and finally, the font image is the font used to display the score, title, and morale. If any of the images fail to load FALSE is returned, and the program shuts down. It's important to note that this base code will not return an INIT FAILED error message.

BOOL Initialize (GL_Window* window, Keys* keys) // Any OpenGL Initialization Goes Here

{

 g_window = window;

 g_keys = keys;

 srand((unsigned)time(NULL)); // Randomize Things

 if ((!LoadTGA(&textures[0], "Data/BlueFace.tga")) || // Load The BlueFace Texture

  (!LoadTGA(&textures[1], "Data/Bucket.tga")) || // Load The Bucket Texture

  (!LoadTGA(&textures[2], "Data/Target.tga")) || // Load The Target Texture

  (!LoadTGA(&textures[3], "Data/Coke.tga")) || // Load The Coke Texture

  (!LoadTGA(&textures[4], "Data/Vase.tga")) || // Load The Vase Texture

  (!LoadTGA(&textures[5], "Data/Explode.tga")) || // Load The Explosion Texture

  (!LoadTGA(&textures[6], "Data/Ground.tga")) || // Load The Ground Texture

  (!LoadTGA(&textures[7], "Data/Sky.tga")) || // Load The Sky Texture

  (!LoadTGA(&textures[8], "Data/Crosshair.tga")) || // Load The Crosshair Texture

  (!LoadTGA(&textures[9], "Data/Font.tga"))) // Load The Crosshair Texture

 {

  return FALSE; // If Loading Failed, Return False

 }

If all of the images loaded and were successfully turned into textures, we can continue with initialization. The font texture is loaded, so it's safe to build our font. We do this by jumping to BuildFont().

We then set up OpenGL. The background color is set to black, the alpha is also set to 0.0f. The depth buffer is set up and enabled with less than or equal testing.

The glBlendFunc() is a VERY important line of code. We set the blend function to (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). This blends the object with whats on the screen using the alpha values stored in the objects texture. After setting the blend mode, we enable blending. We then enable 2D texture mapping, and finally, we enable GL_CULL_FACE. This removes the back face from each object ( no point in wasting cycles drawing something we can't see ). We draw all of our quads with a counter clockwise winding so the proper face is culled.

Earlier in the tutorial I talked about using the glAlphaFunc() instead of alpha blending. If you want to use the Alpha Function, comment out the 2 lines of blending code and uncomment the 2 lines under glEnable(GL_BLEND). You can also comment out the qsort() function in the InitObject() section of code.

The program should run ok, but the sky texture will not be there. The reason is because the sky texture has an alpha value of 0.5f. When I was talking about the Alpha Function earlier on, I mentioned that it only works with alpha values of 0 or 1. You will have to modify the alpha channel for the sky texture if you want it to appear! Again, if you decide to use the Alpha Function instead, you don't have to sort the objects. Both methods have the good points! Below is a quick quote from the SGI site:

"The alpha function discards fragments instead of drawing them into the frame buffer. Therefore sorting of the primitives is not necessary (unless some other mode like alpha blending is enabled). The disadvantage is that pixels must be completely opaque or completely transparent" .

 BuildFont(); // Build Our Font Display List

 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background

 glClearDepth(1.0f); // Depth Buffer Setup

 glDepthFunc(GL_LEQUAL); // Type Of Depth Testing

 glEnable(GL_DEPTH_TEST); // Enable Depth Testing

 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Enable Alpha Blending (disable alpha testing)

 glEnable(GL_BLEND); // Enable Blending (disable alpha testing)

 // glAlphaFunc(GL_GREATER,0.1f); // Set Alpha Testing (disable blending)

 // glEnable(GL_ALPHA_TEST); // Enable Alpha Testing (disable blending)

 glEnable(GL_TEXTURE_2D); // Enable Texture Mapping

 glEnable(GL_CULL_FACE); // Remove Back Face

At this point in the program, none of the objects have been defined. So we loop through all thirty objects calling InitObject() for each object.

 for (int loop=0; loop<30; loop++) // Loop Through 30 Objects

  InitObject(loop); // Initialize Each Object

 return TRUE; // Return TRUE (Initialization Successful)

}

In our init code, we called BuildFont() which builds our 95 display lists. The following line of code deletes all 95 display lists before the program quits.

void Deinitialize(void) // Any User DeInitialization Goes Here

{

 glDeleteLists(base, 95); // Delete All 95 Font Display Lists

}

Now for the tricky stuff… The code that does the actual selecting of the objects. The first line of code below allocates a buffer that we can use to store information about our selected objects into. The variable hits will hold the number of hits detected while in selection mode.

void Selection(void) // This Is Where Selection Is Done

{

 GLuint buffer[512]; // Set Up A Selection Buffer

 GLint hits; // The Number Of Objects That We Selected

In the code below, we check to see if the game is over (FALSE). If it is, there is no point in selecting anything, so we return (exit). If the game is still active (TRUE), we play a gunshot sound using the Playsound() command. The only time Selection() is called is when the mouse button has been pressed, and every time the button is pressed, we want to play the gunshot sound. The sound is played in async mode so that it doesn't halt the program while the sound is playing.

 if (game) // Is Game Over?

  return; // If So, Don't Bother Checking For Hits