/* Asn3 - due June 10 make waves + compute normals for triangulated frog model */ #define FLAT // use smooth shading if this is not defined #include #include #include #include #include /* Constants */ #define WINDOW_X 1024 #define WINDOW_Y 768 #define PI 3.14159265358979323846 /* Global state variables */ int mouse_button, mouse_state; int viewport_x, viewport_y ; int go = 1 ; // for the timer callback float cam_r = 20, cam_theta = PI, cam_phi = -PI/4.0 ; // radius, longitude, lat float cam_dtheta = 0, cam_dphi = 0, cam_dr = 0, cam_d = PI/64.0 ; int ticks = 0 ; // used when animating the frog char draw_axes_flag = 1 ; typedef struct { // the structure of a trianguated object surface int nvertices ; float **vertices ; int nvnormals; float **vnormals ; int ntriangles ; int **triangles ; } surface; surface s; GLuint froglist ; // used for the display list mechanism float length(float a[3]) { // magnitude) of a 3 component isotropic vector float len; // 2A. XXXXX compute the length of vector a; return(len); } void normalize(float a[3]) { // normalize this vector a[]'s magnitude to 1 // 2A. XXXXX put the normalized a[] back into a[] } void subtract(float a[3], float b[3], float c[3]) { // diff vector in c // 2B. XXXXX compute vector c[] as a difference of 2 points a[],b[] } void cross(float a[3], float b[3], float c[3]) { // c vect perp to a & b // 2C. XXXXX comput cross product of a[] x b[] in vector c[] } void draw_string(float x, float y, float z, char *s) { glColor3f(0,1,1); glRasterPos3f(x, y, z) ; // place left baseline text start position while(*s) glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *s++); } void draw_axes(void) { float k = 5, nzero = 0.1 ; char sbuf[256]; // local string buffer glDisable(GL_LIGHTING) ; glLineWidth(3) ; glBegin(GL_LINES) ; glColor3f(1,0,0) ; glVertex3f(nzero,nzero,nzero) ; glVertex3f(k,nzero,nzero) ; glColor3f(0,1,0) ; glVertex3f(nzero,nzero,nzero) ; glVertex3f(nzero,k,nzero) ; glColor3f(0,0,1) ; glVertex3f(nzero,nzero,nzero) ; glVertex3f(nzero,nzero,k) ; glEnd() ; glLineWidth(1) ; glColor3f(1,1,1) ; glRasterPos3f(k+1,0,0) ; glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'X') ; glRasterPos3f(0,k+1,0) ; glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'Y') ; glRasterPos3f(0,0,k+1) ; glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'Z') ; sprintf(sbuf, "camera %f %f %f ", cam_r, cam_theta, cam_phi); draw_string(0, 0.5, k+1, sbuf); glEnable(GL_LIGHTING) ; } void draw_xy_plane(void) { float x,y, r = 60, delta = 1 ; glColor3f(1,1,0) ; glNormal3f(0,0,1) ; glColor3f(0,0,0.4) ; glBegin(GL_QUADS) ; // use quads rather than triangles // to make it easier to generate the regular XY pattern below for( y = -r ; y < r ; y += delta ) { for( x = -r ; x < r ; x += delta ) { // 1. XXXXX make animated waves on the water surface (see draw_frog below) glVertex3f(x,y,0) ; glVertex3f(x+delta,y,0) ; glVertex3f(x+delta,y+delta,0) ; glVertex3f(x,y+delta,0) ; } } glEnd() ; } int nfrogs = 1; void draw_frog(void) { // Frogs jumping around circular path int i; #define LOOP 1000 // set to give nice result - nothing more complicated float a = (float)(ticks % LOOP) / LOOP; // timed fraction of full circle float r = 9 + 0.4*nfrogs; // variable space in circle radius for(i = 0; i < nfrogs; i++) { a += 1./(nfrogs); glPushMatrix(); glRotatef(90, 1, 0, 0); // align to ground plane glTranslatef(r*cos(2*PI*a), // observed nice speed fabs(sin(17*PI*a)), // 17 hops around circ -r*sin(2*PI*a)); glRotatef(360*a, 0, 1, 0); // keep head aligned to circ if(draw_axes_flag) draw_axes(); glEnable(GL_LIGHTING) ; glEnable(GL_LIGHT0) ; if(i == 0) // color on froglist takes priority!!! glColor3f(0,1,0) ; else glColor3f(1,1,0) ; glCallList(froglist) ; glDisable(GL_LIGHTING) ; glPopMatrix(); } } void setup_light(void) { float lightpos[4] = { 0, 0, 3, 1 } ; // w = 1 means the light // position is accounted for in lighting calculations, try w = 0 glLightfv(GL_LIGHT0,GL_POSITION, lightpos) ; glDisable(GL_LIGHTING) ; glPointSize(10) ; glColor3f(1,1,1) ; glBegin(GL_POINTS) ; glVertex3fv( lightpos ) ; glEnd() ; glEnable(GL_LIGHTING) ; } void draw(void) { float fromx, fromy, fromz ; float upx, upy, upz ; fromx = cam_r * sin(cam_phi) * cos(cam_theta) ; fromy = cam_r * sin(cam_phi) * sin(cam_theta) ; fromz = cam_r * cos(cam_phi) ; upx = sin(cam_phi+PI/2.0) * cos(cam_theta) ; upy = sin(cam_phi+PI/2.0) * sin(cam_theta) ; upz = cos(cam_phi+PI/2.0) ; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // set the viewing transformation gluLookAt(fromx, fromy, fromz, 0,0,0, upx, upy, upz); setup_light() ; draw_xy_plane() ; draw_frog() ; if( draw_axes_flag ) draw_axes() ; } // load a simple ASCII text file object format of a triangulated surface void load_surface(surface *s, char *filename) { FILE *file ; int i ; file = fopen(filename,"r") ; if( file == NULL ) { fprintf(stderr,"unable to open '%s'\n",filename) ; exit(1) ; } /* read vertices */ fscanf(file,"%d\n",&s->nvertices) ; s->vertices = (float **)malloc( s->nvertices * sizeof(float *)) ; for( i = 0 ; i < s->nvertices ; i++ ) { s->vertices[i] = (float *)malloc( 3 * sizeof(float) ) ; fscanf(file,"%g %g %g\n", &s->vertices[i][0], &s->vertices[i][1],&s->vertices[i][2]) ; } /* read triangles */ fscanf(file,"%d\n",&s->ntriangles) ; s->vnormals = (float **)malloc( s->nvertices * sizeof(float *)) ; // if its the same number then it is really vertex normals if(s->ntriangles == s->nvertices) { s->nvnormals = s->nvertices; for(i = 0; i < s->nvnormals; i++) { s->vnormals[i] = (float *)malloc( 3 * sizeof(float) ) ; fscanf(file,"%g %g %g\n",&s->vnormals[i][0], &s->vnormals[i][1],&s->vnormals[i][2]) ; } fscanf(file,"%d\n",&s->ntriangles) ; // now read # of triangles } else s->nvnormals = 0; s->triangles = (int **)malloc( s->ntriangles * sizeof(int *)) ; for( i = 0 ; i < s->ntriangles ; i++ ) { s->triangles[i] = (int *)malloc( 3 * sizeof(int) ) ; fscanf(file,"%d %d %d\n", &s->triangles[i][0], &s->triangles[i][1],&s->triangles[i][2]) ; } fclose(file) ; } /* GLUT Callback Stubs */ void display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); draw() ; glutSwapBuffers(); glutReportErrors(); } void timer(int value) { cam_theta += cam_dtheta ; if( (cam_phi+cam_dphi < 0) && (cam_phi+cam_dphi > -PI) ) cam_phi += cam_dphi ; else cam_dphi = 0 ; if( (cam_r+cam_dr < 80.0 ) && (cam_r+cam_dr > 1.0) ) cam_r += cam_dr ; else cam_dr = 0 ; if( go ) glutTimerFunc( 25, timer, value++); glutPostRedisplay() ; if( cam_dtheta ) cam_dtheta -= cam_dtheta/8.0 ; if( cam_dphi ) cam_dphi -= cam_dphi/8.0 ; if( cam_dr > 0 ) cam_dr -= 1.0/16.0 ; else if( cam_dr < 0 ) cam_dr += 1.0/16.0 ; ticks++ ; // here to help animate the frog } void reshape(int w, int h) { viewport_x = w ; viewport_y = h ; glViewport(0,0,viewport_x,viewport_y); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // set up the perspective view frustum matrix gluPerspective(45,(float)w/(float)h,1,400) ; // angular_fovy, aspect_ratio, nearclipz, farclipz } void keyboard( unsigned char key, int x, int y ) { switch( key ) { case 27: exit(0); break; // escape key to exit case ',': // rotate camera counter clockwise cam_dtheta -= cam_d/4.0 ; break ; case '.': // rotate camera clockwise cam_dtheta += cam_d/4.0 ; break ; case 'k': // tilt camera upwards cam_dphi += cam_d/4.0 ; break; case 'm': // tilt camera downwards cam_dphi -= cam_d/4.0 ; break; case 'i': // move camera in toward middle cam_dr -= 0.75 ; break ; case 'o': // move camera radially outward cam_dr += 0.75 ; break ; case 'a': // toggle axis drawing draw_axes_flag = ! draw_axes_flag ; break ; case 'p': // toggle pause go = !go ; fprintf(stderr,"%s\n", go?"run":"pause"); if( go ) glutTimerFunc( 25, timer, 0); break ; case '=': nfrogs++; break; case '-': if(--nfrogs < 0) nfrogs = 0; break; } glutPostRedisplay(); } void init_gl(int argc, char **argv) { glClearColor(0.25, 0.25, 0.25, 1); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); } void init_glut(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB|GLUT_DEPTH|GLUT_DOUBLE); glutInitWindowSize(WINDOW_X,WINDOW_Y); glutCreateWindow("Assignment 03"); glutDisplayFunc( display ); glutReshapeFunc( reshape ); glutKeyboardFunc( keyboard ); glutTimerFunc( 25, timer, 0); } // colore the nose yellow, hind legs red, front legs white, and the rest green colorize(float x, float z) { // this is specific to this frog model!!! if(z < 0) { if(x*x > 1.1) glColor3f(1,1,1); else if(z < -2.5) glColor3f(1,1,0); else glColor3f(0,1,0); } else glColor3f(1,0,0); } // See p765 & http://jerome.jouvie.free.fr/OpenGl/Tutorials/Tutorial16.php void init_postgl(void) { int i ; #ifdef FLAT load_surface(&s,"frog.tri"); // Could be any object glShadeModel(GL_FLAT); #else load_surface(&s,"frog_with_normals.tri"); // Could be any object glShadeModel(GL_SMOOTH); #endif froglist = glGenLists(1) ; // make an empty display list glNewList(froglist, GL_COMPILE);// initialize the list to create //glEnable(GL_NORMALIZE); // would eliminate normalize cross() glBegin(GL_TRIANGLES) ; // we'll add triangles to the list for( i = 0 ; i < s.ntriangles ; i++ ) { int idx0, idx1, idx2 ; float v0[3], v1[3], n[3] = { 1,1,1 }; // you'll replace the 1's idx0 = s.triangles[i][0] ; // 3 vertices that form a triangle idx1 = s.triangles[i][1] ; idx2 = s.triangles[i][2] ; #ifdef FLAT /* 2. XXXXX compute face normal for each triangle and * store it result in the vector n[] already declared above. * To compute the surface normal you take the cross product * of two different triangle edge vectors. Once you have * it working the FLAT shaded frog should be most brightly * on the side facing the central light source. * Broken into steps... * A. you need to form 2 edge vectors v0[], v1[] * B. take their cross product and put it in n[] * C. normalize n[] * The real work for these steps is marked XXXXX elewhere */ subtract(s.vertices[idx1], s.vertices[idx0], v0) ; subtract(s.vertices[idx2], s.vertices[idx0], v1) ; cross(v1, v0, n); // normal perpedicular to triangle face normalize(n); glNormal3fv(n); // one face normal per triangle glVertex3fv(s.vertices[idx0]); glVertex3fv(s.vertices[idx1]); glVertex3fv(s.vertices[idx2]); #else // smooth shading will have 1 normal per vertex glNormal3fv(s.vnormals[idx0]); colorize(s.vertices[idx0][0], s.vertices[idx0][2]); glVertex3fv(s.vertices[idx0]); glNormal3fv(s.vnormals[idx1]); colorize(s.vertices[idx0][0], s.vertices[idx1][2]); glVertex3fv(s.vertices[idx1]); glNormal3fv(s.vnormals[idx2]); colorize(s.vertices[idx0][0], s.vertices[idx2][2]); glVertex3fv(s.vertices[idx2]); #endif } glEnd() ; glEndList() ; } main(int argc, char **argv) { init_glut(argc, argv); init_gl(argc, argv); init_postgl() ; glutReportErrors(); glutMainLoop(); }