#include #include #include #include "GluCylinders.h" // There's no real need to comment includes. // What a include supplies normally is obvious from the file name void animate(float t, float&, float&, float&); void renderCylinder(float x1, float y1, float z1, float x2,float y2, float z2, float radius, int subdivisions, GLUquadricObj *quadric); void renderCylinder_convenient(float x1, float y1, float z1, float x2,float y2, float z2, float radius,int subdivisions); // The next global variable controls the animation's state and speed. float RotateAngle = 0.0f; // Angle in degrees of rotation around y-axis float Azimuth = 0.0; // Rotated up or down by this amount float AngleStepSize = 3.0f; // Step three degrees at a time const float AngleStepMax = 10.0f; const float AngleStepMin = 0.1f; int WireFrameOn = 1; // == 1 for wire frame mode //points // I would not call this geometry but control points... float Geometry[9][3] = { { 4,2.0,0}, { 4,2.0,0}, // Point1 {-1.5,0.2,0}, {-2,0.3,0}, {-2.5,0.6,0}, {-3,0.8,0}, {-3.5,1.0,0}, { -5.5,1.8,0}, //point 2 { -5.5,1.8,0} }; int x2[5],y2[5],z2[5]; // LOD? Level of Detail? Length of Delay? // This is a case where a comment makes sense: Explaining abbreviations. // unsigned int LOD=20; void myKeyboardFunc( unsigned char key, int x, int y ) { switch ( key ) { case 'w': WireFrameOn = 1-WireFrameOn; glutPostRedisplay(); break; case 'R': AngleStepSize *= 1.5; if (AngleStepSize>AngleStepMax ) { AngleStepSize = AngleStepMax; } break; case 'r': AngleStepSize /= 1.5; if (AngleStepSize80.0f ) { Azimuth = 80.0f; } break; case GLUT_KEY_DOWN: Azimuth -= AngleStepSize; if ( Azimuth < -80.0f ) { Azimuth = -80.0f; } break; case GLUT_KEY_LEFT: RotateAngle += AngleStepSize; if ( RotateAngle > 180.0f ) { RotateAngle -= 360.0f; } break; case GLUT_KEY_RIGHT: RotateAngle -= AngleStepSize; if ( RotateAngle < -180.0f ) { RotateAngle += 360.0f; } break; } glutPostRedisplay(); } /* * drawScene() handles the animation and the redrawing of the * graphics window contents. */ void drawScene(void) { // Those should not be a drawScene static, but part of // a scene management structure. Which your code lacks, // unfortunately, and I'm not gonna introduce one. static float animation_time = 0.; static int j = 1; // why 1 indexed? This is not Lua if ( WireFrameOn ) { glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE ); // Just show wireframes } else { glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL ); // Show solid polygons } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glTranslatef( -0.5, 0.0, -35.0 ); glRotatef( RotateAngle, 0.0, 1.0, 0.0 ); glRotatef( Azimuth, 1.0, 0.0, 0.0 ); // Please, don't /code/ your geometry, it's ugly and unmaintainable. I omit that code here // (...) // Okay, I can just guess here, but it looks like you're intending to draw // a trace of the sphere as it moves around. I have no idea what that j // loop is meant for, though. But I guess you want to show the control points, // one after another. This another case _FOR_ comments: Explaining the idea of // the overall scheme. Don't write in a comment what a single statement does. I // can see that from the statement. // use the parametric time value 0 to current animation time. float x3, y3, z3; // first draw the trace glBegin(GL_LINE_STRIP); for(float t = 0; t < animation_time; t+=0.05) { animate(t, x3, y3, z3); glVertex3f( x3,y3,z3 ); } glEnd(); // Then draw the control point markers glColor3f(1,1,0); for(float t = 0; t < animation_time; t+=0.05) { animate(t, x3, y3, z3); renderCylinder_convenient( Geometry[j][0],Geometry[j][1],Geometry[j][2], x3,y3,z3, 0.3, 32); } // Last but not least draw sphere at the position for animation_time. animate(animation_time, x3, y3, z3); glutDrawSphere(...); // now, we want to play that animation a few times in // succession, but with different control points visible // or so I presume so after each animation time has reached 1 // we increment the control point counter. And after that hit // its maximum we stop advancing the animation if(j<3) { if(animation_time < 1.0) { // Animation step should be a measured // value. However in case of your simple // graphics and in the case of v-synced // buffer swap, and the prevalence of LCD // screens updating at 60Hz we can make this // a constant: // One frame is 1/60th of a second and we // want to take the animation 5 seconds to // play: float animation_step = (1./60.) / (5.); animation_time += animation_step; } else { animation_time = 0.; j++; } } glutSwapBuffers(); } // Called when the window is resized // w, h - width and height of the window in pixels. void resizeWindow(int w, int h) { double aspectRatio; // Technically all this should be done in the display function! // Define the portion of the window used for OpenGL rendering. glViewport( 0, 0, w, h ); // View port uses whole window // Set up the projection view matrix: perspective projection // Determine the min and max values for x and y that should appear in the window. // The complication is that the aspect ratio of the window may not match the // aspect ratio of the scene we want to view. w = (w==0) ? 1 : w; h = (h==0) ? 1 : h; aspectRatio = (double)w / (double)h; glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 15.0, aspectRatio, 25.0, 45.0 ); } // Main routine // Set up OpenGL, define the callbacks and start the main loop int main( int argc, char** argv ) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); glutInitWindowPosition( 10, 60 ); glutInitWindowSize( 360, 360 ); glutCreateWindow( "GluCylinders" ); glutKeyboardFunc( myKeyboardFunc ); glutSpecialFunc( mySpecialKeyFunc ); glutReshapeFunc( resizeWindow ); glutIdleFunc( glutPostRedisplay ); // when idle -> redraw glutDisplayFunc( drawScene ); fprintf(stdout, "Arrow keys control viewpoint.\n"); fprintf(stdout, "Press \"w\" to toggle wireframe mode.\n"); fprintf(stdout, "Press \"R\" or \"r\" to increase or decrease rate of movement (respectively).\n"); // Start the main loop. glutMainLoop never returns. glutMainLoop( ); return 0; } GLUquadricObj* myReusableQuadric = 0; void drawGluCylinder( double height, double radius, int slices, int stacks ) { drawGluSlantCylinder( height, radius, radius, slices, stacks ); } void drawGluSlantCylinder( double height, double radiusBase, double radiusTop, int slices, int stacks ) { if ( ! myReusableQuadric ) { myReusableQuadric = gluNewQuadric(); // Should (but don't) check if pointer is still null --- to catch memory allocation errors. gluQuadricNormals( myReusableQuadric, GL_TRUE ); } // Draw the cylinder. gluCylinder( myReusableQuadric, radiusBase, radiusTop, height, slices, stacks ); } void drawGluCylinderWithCaps( double height, double radius, int slices, int stacks ) { drawGluSlantCylinderWithCaps( height, radius, radius, slices, stacks ); } // animate shall calculate the new position for the next // render iteration. Don't do delays, busy loops and drawing // stuff here. void animate(float t, float &x3, float &y3, float &z3) { // I don't see it in this mess, but I // assume this is some kind of spline // interpolation, right? x3 = 0.5*((-Geometry[j-1][0] + 3*Geometry[j][0] -3*Geometry[j+1][0] + Geometry[j+2][0])*t*t*t + (2*Geometry[j-1][0] -5*Geometry[j][0] +4*Geometry[j+1][0] -Geometry[j+2][0])*t*t + (-Geometry[j-1][0] + Geometry[j+1][0])*t + 2*Geometry[j][0]); y3 = 0.5*((-Geometry[j-1][1] + 3*Geometry[j][1] -3*Geometry[j+1][1] + Geometry[j+2][1])*t*t*t+ (2*Geometry[j-1][1] -5*Geometry[j][1] +4*Geometry[j+1][1] -Geometry[j+2][1])*t*t +(-Geometry[j-1][1] + Geometry[j+1][1])*t + 2*Geometry[j][1]); z3 = 0.5*((-Geometry[j-1][2] + 3*Geometry[j][2] -3*Geometry[j+1][2] + Geometry[j+2][2])*t*t*t + (2*Geometry[j-1][2] -5*Geometry[j][2] +4*Geometry[j+1][2] -Geometry[j+2][2])*t*t +(-Geometry[j-1][2] + Geometry[j+1][2])*t + 2*Geometry[j][2]); } void renderCylinder(float x1, float y1, float z1, float x2,float y2, float z2, float radius,int subdivisions,GLUquadricObj *quadric) { float v,rx,ry,ax,vx = x2-x1; float vy = y2-y1; float vz = z2-z1; //handle the degenerate case of z1 == z2 with an approximation if(vz == 0) vz =.00000001; v = sqrt( vx*vx + vy*vy + vz*vz ); ax = 57.2957795*acos( vz/v ); if ( vz < 0.0 ) ax = -ax; rx = -vy*vz; ry = vx*vz; glPushMatrix(); //draw the cylinder body glTranslatef( x1,y1,z1 ); glRotatef(ax, rx, ry, 0.0); gluQuadricOrientation(quadric,GLU_OUTSIDE); gluCylinder(quadric, radius, radius, v, subdivisions, 1); //draw the first cap gluQuadricOrientation(quadric,GLU_INSIDE); gluDisk( quadric, 0.0, radius, subdivisions, 1); glTranslatef( 0,0,v ); //draw the second cap gluQuadricOrientation(quadric,GLU_OUTSIDE); gluDisk( quadric, 0.0, radius, subdivisions, 1); glPopMatrix(); } void renderCylinder_convenient( float x1, float y1, float z1, float x2,float y2, float z2, float radius,int subdivisions) { //the same quadric can be re-used for drawing many cylinders // Instead of quadrics and coding your geometry you should // just import a 3D model and render that. It's easier and // allows for more artistic freedom. Yes, Yes, I know, your // teachers assigment; seriously, I'd tell a teacher demanding // this to f*** himself. GLUquadricObj *quadric=gluNewQuadric(); gluQuadricNormals(quadric, GLU_SMOOTH); renderCylinder(x1,y1,z1,x2,y2,z2,radius,subdivisions,quadric); gluDeleteQuadric(quadric);} void drawGluSlantCylinderWithCaps( double height, double radiusBase, double radiusTop, int slices, int stacks ) { // First draw the cylinder drawGluSlantCylinder( height, radiusBase, radiusTop, slices, stacks ); // Draw the top disk cap glPushMatrix(); glTranslated(0.0, 0.0, height); gluDisk( myReusableQuadric, 0.0, radiusTop, slices, stacks ); glPopMatrix(); // Draw the bottom disk cap glPushMatrix(); glRotated(180.0, 1.0, 0.0, 0.0); gluDisk( myReusableQuadric, 0.0, radiusBase, slices, stacks ); glPopMatrix(); }