We loop through all the vertices. If there are not enough vertices, an error might occur, so make sure the text at the beginning of the file "Vertices: {some number}" is actually the number of vertices in the file. Meaning if the top line of the file says "Vertices: 10", there had better be 10 Verticies (x, y and z values)!
After reading in all of the verticies we close the file, and check to see if the variable ver is greater than the variable maxver. If ver is greater than maxver, we set maxver to equal ver. That way if we read in one object and it has 20 verticies, maxver will become 20. If we read in another object, and it has 40 verticies, maxver will become 40. That way we know how many vertices our largest object has.
k->points[i].x = rx; // Sets Objects (k) points.x Value To rx
k->points[i].y = ry; // Sets Objects (k) points.y Value To ry
k->points[i].z = rz; // Sets Objects (k) points.z Value To rz
}
fclose(filein); // Close The File
if (ver>maxver) maxver=ver; // If ver Is Greater Than maxver Set maxver Equal To ver
} // Keeps Track Of Highest Number Of Vertices Used
The next bit of code may look a little intimidating… it's NOT :) I'll explain it so clearly you'll laugh when you next look at it.
What the code below does is calculates a new position for each point when morphing is enabled. The number of the point to calculate is stored in i. The results will be returned in the VERTEX calculate.
The first variable we create is a VERTEX called a. This will give a an x, y and z value.
Lets look at the first line. The x value of the VERTEX a equals the x value of point[i] (point[i].x) in our SOURCE object minus the x value of point[i] (point[i].x) in our DESTINATION object divided by steps.
So lets plug in some numbers. Lets say our source objects first x value is 40 and our destination objects first x value is 20. We already know that steps is equal to 200! So that means that a.x=(40-20)/200… a.x=(20)/200… a.x=0.1.
What this means is that in order to move from 40 to 20 in 200 steps, we need to move by 0.1 units each calculation. To prove this calculation, multiply 0.1 by 200, and you get 20. 40-20=20 :)
We do the same thing to calculate how many units to move on both the y axis and the z axis for each point. If you increase the value of steps the movements will be even more fine (smooth), but it will take longer to morph from one position to another.
VERTEX calculate(int i) // Calculates Movement Of Points During Morphing
{
VERTEX a; // Temporary Vertex Called a
a.x=(sour->points[i].x-dest->points[i].x)/steps; // a.x Value Equals Source x – Destination x Divided By Steps
a.y=(sour->points[i].y-dest->points[i].y)/steps; // a.y Value Equals Source y – Destination y Divided By Steps
a.z=(sour->points[i].z-dest->points[i].z)/steps; // a.z Value Equals Source z – Destination z Divided By Steps
return a; // Return The Results
} // This Makes Points Move At A Speed So They All Get To Their
The ReSizeGLScene() code hasn't changed so we'll skip over it.
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window
In the code below we set blending for translucency. This allows us to create neat looking trails when the points are moving.
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Set The Blending Function For Translucency
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black
glClearDepth(1.0); // Enables Clearing Of The Depth Buffer
glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
We set the maxver variable to 0 to start off. We haven't read in any objects so we don't know what the maximum amount of vertices will be.
Next well load in 3 objects. The first object is a sphere. The data for the sphere is stored in the file sphere.txt. The data will be loaded into the object named morph1. We also load a torus, and a tube into objects morph2 and morph3.
maxver=0; // Sets Max Vertices To 0 By Default
objload("data/sphere.txt", &morph1); // Load The First Object Into morph1 From File sphere.txt
objload("data/torus.txt", &morph2); // Load The Second Object Into morph2 From File torus.txt
objload("data/tube.txt", &morph3); // Load The Third Object Into morph3 From File tube.txt
The 4th object isn't read from a file. It's a bunch of dots randomly scattered around the screen. Because we're not reading the data from a file, we have to manually allocate the memory by calling objallocate(&morph4,468). 468 means we want to allocate enough space to hold 468 vertices (the same amount of vertices the other 3 objects have).
After allocating the space, we create a loop that assigns a random x, y and z value to each point. The random value will be a floating point value from +7 to –7. (14000/1000=14… minus 7 gives us a max value of +7… if the random number is 0, we have a minimum value of 0-7 or –7).
objallocate(&morph4, 486); // Manually Reserver Ram For A 4th 468 Vertice Object (morph4)
for(int i=0; i<486; i++) // Loop Through All 468 Vertices
{
morph4.points[i].x = ((float)(rand()%14000)/1000)-7; // morph4 x Point Becomes A Random Float Value From –7 to 7
morph4.points[i].y = ((float)(rand()%14000)/1000)-7; // morph4 y Point Becomes A Random Float Value From –7 to 7
morph4.points[i].z = ((float)(rand()%14000)/1000)-7; // morph4 z Point Becomes A Random Float Value From –7 to 7
}
We then load the sphere.txt as a helper object. We never want to modify the object data in morph{1/2/3/4} directly. We modify the helper data to make it become one of the 4 shapes. Because we start out displaying morph1 (a sphere) we start the helper out as a sphere as well.
After all of the objects are loaded, we set the source and destination objects (sour and dest) to equal morph1, which is the sphere. This way everything starts out as a sphere.
objload("data/sphere.txt", &helper); // Load sphere.txt Object Into Helper (Used As Starting Point)
sour=dest=&morph1; // Source & Destination Are Set To Equal First Object (morph1)
return TRUE; // Initialization Went OK
}
Now for the fun stuff. The actual rendering code :)
We start off normal. Clear the screen, depth buffer and reset the modelview matrix. Then we position the object on the screen using the values stored in cx, cy and cz.
Rotations are done using xrot, yrot and zrot.
The rotation angle is increased based on xpseed, yspeed and zspeed.
Finally 3 temporary variables are created tx, ty and tz, along with a new VERTEX called q.
void 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
glLoadIdentity(); // Reset The View
glTranslatef(cx,cy,cz); // Translate The The Current Position To Start Drawing