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

We have our frame of animation but the red and blue bytes are swapped. To solve this problem, we jump to our speedy flipIt(…) code. Remember, data is a pointer to a variable that receives a pointer to the location of the DIB's bit values. What that means is that after we call DrawDibDraw, data will point to the resized (256*256) / modified (24 bit) bitmap data.

Originally I was updating the texture by recreating it for each frame of animation. I received a few emails suggesting that I use glTexSubImage2D(). After flipping through the OpenGL Red Book, I stumbled across the following quote: "Creating a texture may be more computationally expensive than modifying an existing one. In OpenGL Release 1.1, there are new routines to replace all or part of a texture image with new information. This can be helpful for certain applications, such as using real-time, captured video images as texture images. For that application, it makes sense to create a single texture and use glTexSubImage2D() to repeatedly replace the texture data with new video images".

I personally didn't notice a huge speed increase, but on slower cards you might! The parameters for glTexSubImage2D() are as follows: Our target, which is a 2D texture (GL_TEXTURE_2D). The detail level (0), used for mipmapping. The x (0) and y (0) offset which tells OpenGL where to start copying to (0,0 is the lower left corner of the texture). The we have the width of the image we wish to copy which is 256 pixels wide and 256 pixels tall. GL_RGB is the format of our data. We are copying unsigned bytes. Finally… The pointer to our data which is represented by data. Very simple!

Kevin Rogers Adds: I just wanted to point out another important reason to use glTexSubImage2D. Not only is it faster on many OpenGL implementations, but the target area does not need to be a power of 2. This is especially handy for video playback since the typical dimensions for a frame are rarely powers of 2 (often something like 320×200). This gives you the flexibility to play the video stream at its original aspect, rather than distorting / clipping each frame to fit your texture dimensions.

It's important to note that you can NOT update a texture if you have not created the texture in the first place! We create the texture in the Initialize() code!

I also wanted to mention… If you planned to use more than one texture in your project, make sure you bind the texture you want to update. If you don't bind the texture you may end up updating textures you didn't want updated!

 flipIt(data); // Swap The Red And Blue Bytes (GL Compatability)

 // Update The Texture

 glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 256, 256, GL_RGB, GL_UNSIGNED_BYTE, data);

}

The following section of code is called when the program exits. We close our DrawDib DC, and free allocated resources. We then release the AVI GetFrame resources. Finally we release the stream and then the file.

void CloseAVI(void) // Properly Closes The Avi File

{

 DeleteObject(hBitmap); // Delete The Device Dependant Bitmap Object

 DrawDibClose(hdd); // Closes The DrawDib Device Context

 AVIStreamGetFrameClose(pgf); // Deallocates The GetFrame Resources

 AVIStreamRelease(pavi); // Release The Stream

 AVIFileExit(); // Release The File

}

Initialization is pretty straight forward. We set the starting angle to 0. We then open the DrawDib library (which grabs a DC). If everything goes well, hdd becomes a handle to the newly created device context.

Our clear screen color is black, depth testing is enabled, etc.

We then create a new quadric. quadratic is the pointer to our new object. We set up smooth normals, and enable texture coordinate generation for our quadric.

BOOL Initialize (GL_Window* window, Keys* keys) // Any GL Init Code & User Initialiazation Goes Here

{

 g_window = window;

 g_keys = keys;

 // Start Of User Initialization

 angle = 0.0f; // Set Starting Angle To Zero

 hdd = DrawDibOpen(); // Grab A Device Context For Our Dib

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

 glClearDepth (1.0f); // Depth Buffer Setup

 glDepthFunc (GL_LEQUAL); // The Type Of Depth Testing (Less Or Equal)

 glEnable(GL_DEPTH_TEST); // Enable Depth Testing

 glShadeModel (GL_SMOOTH); // Select Smooth Shading

 glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Set Perspective Calculations To Most Accurate

 quadratic=gluNewQuadric(); // Create A Pointer To The Quadric Object

 gluQuadricNormals(quadratic, GLU_SMOOTH); // Create Smooth Normals

 gluQuadricTexture(quadratic, GL_TRUE); // Create Texture Coords

In the next bit of code, we enable 2D texture mapping, we set the texture filters to GL_NEAREST (fast, but rough looking) and we set up sphere mapping (to create the environment mapping effect). Play around with the filters. If you have the power, try out GL_LINEAR for a smoother looking animation.

After setting up our texture and sphere mapping, we open the .AVI file. I tried to keep things simple… can you tell :) The file we are going to open is called face2.avi… it's located in the data directory.

The last thing we have to do is create our initial texture. We need to do this in order to use glTexSubImage2D() to update our texture in GrabAVIFrame().

 glEnable(GL_TEXTURE_2D); // Enable Texture Mapping

 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);// Set Texture Max Filter

 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);// Set Texture Min Filter

 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // Set The Texture Generation Mode For S To Sphere Mapping

 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // Set The Texture Generation Mode For T To Sphere Mapping

 OpenAVI("data/face2.avi"); // Open The AVI File

 // Create The Texture

 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

 return TRUE; // Return TRUE (Initialization Successful)

}

When shutting down, we call CloseAVI(). This properly closes the AVI file, and releases any used resources.

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

{

 CloseAVI(); // Close The AVI File

}

This is where we check for key presses and update our rotation (angle) based on time passed. By now I shouldn't have to explain the code in detail. We check to see if the space bar is pressed. If it is, we increase the effect. We have three effect (cube, sphere, cylinder) and when the 4th effect is selected (effect=3) nothing is drawn… showing just the background scene! If we are on the 4th effect and space is pressed, we reset back to the first effect (effect=0). Yeah, I know I should have called it OBJECT :)

We then check to see if the 'B' key is pressed if it is, we toggle the background (bg) from ON to OFF or from OFF to ON.

Environment mapping is done the same way. We check to see if 'E' is pressed. If it is, we toggle env from TRUE to FALSE or from FALSE to TRUE. Turning environment mapping off or on!

The angle is increased by a tiny fraction each time Update() is called. I divide the time passed by 60.0f to slow things down a little.

void Update (DWORD milliseconds) // Perform Motion Updates Here