Lesson
11
|
|
|
|
|
Well
greetings all. For those of you that want to see what we are doing here,
you can check it out at the end of my demo/hack Worthless! I am bosco and
I will do my best to teach you guys how to do the animated, sine-wave picture.
This tutorial is based on NeHe's tutorial #6 and you should have at least
that much knowledge. You should download the source package and place the
bitmap I've included in a directory called data where your source code
is. Or use your own texture if it's an appropriate size to be used as a
texture with OpenGL.
First things
first. Open Tutorial #6 in Visual C++ and add the following include statement
right after the other #include statements. The #include below allows us
to work with complex math such as sine and cosine. |
|
|
|
|
#include <math.h> // For The Sin() Function
|
|
|
|
|
We'll
use the array points to store the individual x, y & z coordinates
of our grid. The grid is 45 points by 45 points, which in turn makes 44
quads x 44 quads. wiggle_count will be used to keep track of how
fast the texture waves. Every three frames looks pretty good, and the variable
hold
will store a floating point value to smooth out the waving of the flag.
These lines can be added at the top of the program, somewhere under the
last #include line, and before the GLuint texture[1] line. |
|
|
|
|
float points[ 45 ][ 45 ][3]; // The Array For The Points On The Grid Of Our "Wave"
int wiggle_count = 0; // Counter Used To Control How Fast Flag Waves
GLfloat hold; // Temporarily Holds A Floating Point Value
|
|
|
|
|
Move
down the the LoadGLTextures() procedure. We want to use the texture called
Tim.bmp. Find LoadBMP("Data/NeHe.bmp") and replace it with LoadBMP("Data/Tim.bmp"). |
|
|
|
|
if (TextureImage[0]=LoadBMP("Data/Tim.bmp")) // Load The Bitmap
|
|
|
|
|
Now
add the following code to the bottom of the InitGL() function before return
TRUE. |
|
|
|
|
glPolygonMode( GL_BACK, GL_FILL ); // Back Face Is Filled In
glPolygonMode( GL_FRONT, GL_LINE ); // Front Face Is Drawn With Lines
|
|
|
|
|
These
simply specify that we want back facing polygons to be filled completely
and that we want front facing polygons to be outlined only. Mostly personal
preference at this point. Has to do with the orientation of the polygon
or the direction of the vertices. See the Red Book for more information
on this. Incidentally, while I'm at it, let me plug the book by saying
it's one of the driving forces behind me learning OpenGL, not to mention
NeHe's site! Thanks NeHe. Buy The Programmer's Guide to OpenGL from Addison-Wesley.
It's an invaluable resource as far as I'm concerned. Ok, back to the tutorial.
Right below
the code above, and above return TRUE, add the following lines. |
|
|
|
|
// Loop Through The X Plane
for(int x=0; x<45; x++)
{
// Loop Through The Y Plane
for(int y=0; y<45; y++)
{
// Apply The Wave To Our Mesh
points[x][y][0]=float((x/5.0f)-4.5f);
points[x][y][1]=float((y/5.0f)-4.5f);
points[x][y][2]=float(sin((((x/5.0f)*40.0f)/360.0f)*3.141592654*2.0f));
}
}
|
|
|
|
|
Thanks
to Graham Gibbons for suggesting an integer loop to get rid of the spike
in the ripple.
The two
loops above initialize the points on our grid. I initialize variables in
my loop to localize them in my mind as merely loop variables. Not sure
it's kosher. We use integer loops to prevent odd graphical glitches that
appear when floating point calculations are used. We divide the x and y
variables by 5 ( i.e. 45 / 9 = 5 ) and subtract 4.5 from each of them to
center the "wave". The same effect could be accomplished with a translate,
but I prefer this method.
The final
value points[x][y][2] statement is our sine value. The sin() function requires
radians. We take our degree value, which is our float_x multiplied by 40.0f.
Once we have that, to convert to radians we take the degree, divide by
360.0f, multiply by pi, or an approximation and then multiply by 2.0f.
I'm going
to re-write the DrawGLScene function from scratch so clean it out and it
replace with the following code. |
|
|
|
|
int DrawGLScene(GLvoid) // Draw Our GL Scene
{
int x, y; // Loop Variables
float float_x, float_y, float_xb, float_yb; // Used To Break The Flag Into Tiny Quads
|
|
|
|
|
Different
variables used for controlling the loops. See the code below but most of
these serve no "specific" purpose other than controlling loops and storing
temporary values. |
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Matrix
glTranslatef(0.0f,0.0f,-12.0f); // Translate 17 Units Into The Screen
glRotatef(xrot,1.0f,0.0f,0.0f); // Rotate On The X Axis
glRotatef(yrot,0.0f,1.0f,0.0f); // Rotate On The Y Axis
glRotatef(zrot,0.0f,0.0f,1.0f); // Rotate On The Z Axis
glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Texture
|
|
|
|
|
You've
seen all of this before as well. Same as in tutorial #6 except I merely
push my scene back away from the camera a bit more. |
|
|
|
|
glBegin(GL_QUADS); // Start Drawing Our Quads
for( x = 0; x < 44; x++ ) // Loop Through The X Plane 0-44 (45 Points)
{
for( y = 0; y < 44; y++ ) // Loop Through The Y Plane 0-44 (45 Points)
{
|
|
|
|
|
Merely
starts the loop to draw our polygons. I use integers here to keep from
having to use the int() function as I did earlier to get the array reference
returned as an integer. |
|
|
|
|
float_x = float(x)/44.0f; // Create A Floating Point X Value
float_y = float(y)/44.0f; // Create A Floating Point Y Value
float_xb = float(x+1)/44.0f; // Create A Floating Point Y Value+0.0227f
float_yb = float(y+1)/44.0f; // Create A Floating Point Y Value+0.0227f
|
|
|
|
|
We
use the four variables above for the texture coordinates. Each of our polygons
(square in the grid), has a 1/44 x 1/44 section of the texture mapped on
it. The loops will specify the lower left vertex and then we just add to
it accordingly to get the other three ( i.e. x+1 or y+1 ). |
|
|
|
|
glTexCoord2f( float_x, float_y); // First Texture Coordinate (Bottom Left)
glVertex3f( points[x][y][0], points[x][y][1], points[x][y][2] );
glTexCoord2f( float_x, float_yb ); // Second Texture Coordinate (Top Left)
glVertex3f( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2] );
glTexCoord2f( float_xb, float_yb ); // Third Texture Coordinate (Top Right)
glVertex3f( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2] );
glTexCoord2f( float_xb, float_y ); // Fourth Texture Coordinate (Bottom Right)
glVertex3f( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2] );
}
}
glEnd(); // Done Drawing Our Quads
|
|
|
|
|
The
lines above merely make the OpenGL calls to pass all the data we talked
about. Four separate calls to each glTexCoord2f() and glVertex3f(). Continue
with the following. Notice the quads are drawn clockwise. This means the
face you see initially will be the back. The back is filled in. The front
is made up of lines.
If you drew
in a counter clockwise order the face you'd initially see would be the
front face, meaning you would see the grid type texture instead of the
filled in face. |
|
|
|
|
if( wiggle_count == 2 ) // Used To Slow Down The Wave (Every 2nd Frame Only)
{
|
|
|
|
|
If
we've drawn two scenes, then we want to cycle our sine values giving us
"motion". |
|
|
|
|
for( y = 0; y < 45; y++ ) // Loop Through The Y Plane
{
hold=points[0][y][2]; // Store Current Value One Left Side Of Wave
for( x = 0; x < 44; x++) // Loop Through The X Plane
{
// Current Wave Value Equals Value To The Right
points[x][y][2] = points[x+1][y][2];
}
points[44][y][2]=hold; // Last Value Becomes The Far Left Stored Value
}
wiggle_count = 0; // Set Counter Back To Zero
}
wiggle_count++; // Increase The Counter
|
|
|
|
|
What
we do here is store the first value of each line, we then move the wave
to the left one, causing the image to wave. The value we stored is then
added to the end to create a never ending wave across the face of the texture.
Then we reset the counter wiggle_count to keep our animation going.
The above
code was modified by NeHe (Feb 2000), to fix a flaw in the ripple going
across the surface of the texture. The ripple is now smooth. |
|
|
|
|
xrot+=0.3f; // Increase The X Rotation Variable
yrot+=0.2f; // Increase The Y Rotation Variable
zrot+=0.4f; // Increase The Z Rotation Variable
return TRUE; // Jump Back
}