/* This program displays a symmetrical dome that uses a single bezier curve along each side, and allows the "inner" control points to be controlled by the mouse, so we can better understand their effects. Scott D. Anderson Fall 2002 */ #include #include #include #include // This program doesn't actually use TW, but tw.h includes the GL header files #include void display(void); void set_viewport(GLsizei ww, GLsizei wh); /* Initial width and height of the drawing window. Modified in resize callback. */ GLsizei win_height = 500; GLsizei win_width = 500; GLint pw,ph; // projection (image) width and height int show_cp = 1; // boolean -- whether to show control points GLdouble lookat[]= {0, 0, 0}; /* gaze */ GLdouble viewer[]= {0, 0, 2.5}; /* initial viewer location */ GLdouble vup[] = {0,1,0}; /* initial camera orientation */ char viewpoint = 'f'; /* t for top, s for side */ /* Dome perimeter control points. These are 2D; we use code to arrange them around along z=+/-1 and x=+/-1. The curve takes off and lands at roughly 45 degree angle, but we want it to get fairly high, so we go to a height of 0.5. */ GLfloat edge[] = { +1.0, 0.0, +0.5, 0.5, -0.5, 0.5, -1.0, 0.0}; GLfloat innerCP[] = { +0.5, +0.5, +0.5 }; /* The dome control points. Numbered with 0 as the +X,+Z quadrant and moving backwards and left. */ GLfloat dome[16*3]; void initDomeCP() { int i=0; // Note that this doesn't fill in any of the interior points. It's // also a bit redundant, in that each corner is filled twice. for(i=0;i<4;++i) { dome[i*3+0]=+1; /* X=+1 strip */ dome[i*3+1]=edge[i*2+1]; /* copy Y value */ dome[i*3+2]=edge[i*2+0]; /* copy Z value */ dome[i*12+0]=edge[i*2+0]; /* copy X value */ dome[i*12+1]=edge[i*2+1]; /* copy Y value */ dome[i*12+2]=+1; /* Z=+1 strip */ dome[i*12+9+0]=edge[i*2+0]; /* copy X value */ dome[i*12+9+1]=edge[i*2+1]; /* copy Y value */ dome[i*12+9+2]=-1; /* Z=-1 strip */ dome[i*3+36+0]=-1; /* X=-1 strip */ dome[i*3+36+1]=edge[i*2+1]; /* copy Y value */ dome[i*3+36+2]=edge[i*2+0]; /* copy Z value */ } // This fills in the interior points. We have to adjust the sign of // the X&Y for each quadrant. dome[5*3+0]=innerCP[0]; dome[5*3+1]=innerCP[1]; dome[5*3+2]=innerCP[2]; dome[6*3+0]=innerCP[0]; dome[6*3+1]=innerCP[1]; dome[6*3+2]=-innerCP[2]; /* flip Z */ dome[9*3+0]=-innerCP[0]; /* flip X */ dome[9*3+1]=innerCP[1]; dome[9*3+2]=innerCP[2]; dome[10*3+0]=-innerCP[0]; /* flip X */ dome[10*3+1]=innerCP[1]; dome[10*3+2]=-innerCP[2]; /* flip X */ } int wirep = 1; /* True if we want a wireframe dome */ void draw_dome() { int steps = 8; glEnable(GL_AUTO_NORMAL); /* automatically compute normals */ glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, dome); glEnable(GL_MAP2_VERTEX_3); glMapGrid2f(steps,0.0,1.0,steps,0.0,1.0); glEvalMesh2(wirep?GL_LINE:GL_FILL,0,steps,0,steps); } /* ********************************************************************** Callbacks for mouse buttons and keyboard */ void mouse(int btn, int state, int x, int y) { GLdouble model[16]; GLdouble proj[16]; GLint view[4]; GLdouble winX; GLdouble winY; GLdouble winZ; GLdouble objX; GLdouble objY; GLdouble objZ; GLint result; static GLdouble savedZ; // Control the position of the front-left control point if(state == GLUT_DOWN) { glGetDoublev(GL_MODELVIEW_MATRIX,model); glGetDoublev(GL_PROJECTION_MATRIX,proj); glGetIntegerv(GL_VIEWPORT,view); result = gluProject(dome[5*3+0],dome[5*3+1],dome[5*3+2], model,proj,view, &winX,&winY,&winZ); if(result != GL_TRUE) { printf("gluProject failed!\n"); } /* printf("(x,wh-y) = (%d,%d)\n(winX,winY,winZ)=(%f,%f,%f)\n", result, x, win_height-y, winX,winY,winZ); */ savedZ=winZ; } else { glGetDoublev(GL_MODELVIEW_MATRIX,model); glGetDoublev(GL_PROJECTION_MATRIX,proj); glGetIntegerv(GL_VIEWPORT,view); winX=x+0.5; winY=win_height-y+0.5; winZ=savedZ; //printf("(winX,winY,winZ)=(%f,%f,%f)\n",winX,winY,winZ); result = gluUnProject(winX,winY,winZ, model,proj,view, &objX,&objY,&objZ); if(result != GL_TRUE) { printf("gluUnproject failed!\n"); } /* printf("(x,y,z) = (%f,%f,%f)\n(objX,objY,objZ)=(%f,%f,%f)\n", result, dome[5*3+0],dome[5*3+1],dome[5*3+2], objX,objY,objZ); */ switch(viewpoint) { case 'f': dome[5*3+0]=objX; dome[5*3+1]=objY; break; case 't': dome[5*3+0]=objX; dome[5*3+2]=objZ; break; case 's': dome[5*3+1]=objY; dome[5*3+2]=objZ; break; } } glutPostRedisplay(); } void set_viewer(GLfloat x, GLfloat y, GLfloat z) { viewer[0] = x; viewer[1] = y; viewer[2] = z; } void rotate_viewer(GLfloat degrees) { GLfloat r = degrees*M_PI/180.0; GLfloat c = cos(r); GLfloat s = sin(r); GLfloat x = c*viewer[0]-s*viewer[2]; GLfloat z = s*viewer[0]+c*viewer[2]; viewer[0] = x; viewer[2] = z; } void move_viewer(GLfloat amount) { int i; GLfloat d; for(i=0;i<3;++i){ d = (lookat[i]-viewer[i])*amount; viewer[i] += d; } } int spinp = 0; /* true if the viewpoint should spin */ void spin() { rotate_viewer(1); glutPostRedisplay(); } typedef void idlefunc(void); void keys(unsigned char key, int x, int y) { switch(key) { case 'R': rotate_viewer(10); break; case 'r': rotate_viewer(-10); break; case 'Z': move_viewer(-0.1);break; case 'z': move_viewer(+0.1);break; case 'f': viewer[0]=viewer[1]=0; viewer[2]=3; vup[0]=vup[2]=0; vup[1]=1; viewpoint = key; break; case 't': viewer[0]=viewer[2]=0; viewer[1]=3; vup[0]=vup[1]=0; vup[2]=-1; viewpoint = key; break; case 's': viewer[1]=viewer[2]=0; viewer[0]=3; vup[0]=vup[2]=0; vup[1]=1; viewpoint = key; break; case 'v': // a nice viewpoint to see the curve viewer[0]=viewer[1]=viewer[2]=1.5; vup[0]=vup[2]=0; vup[1]=1; break; case 'q': exit(0); break; case 'p': { int i,j; for(i=0; i<4; ++i) for(j=0; j<4; ++j) { printf("(%d,%d) = (%f,%f,%f)\n",i,j, dome[i*12+j*3+0],dome[i*12+j*3+1],dome[i*12+j*3+2]); } } break; case '=': printf("%f %f %f (%f)\n",viewer[0],viewer[1],viewer[2], viewer[0]*viewer[0]+viewer[1]*viewer[1]+viewer[2]*viewer[2] ); break; case 'w': wirep = !wirep; break; case 'S': spinp = !spinp; glutIdleFunc(spinp ? spin : (idlefunc*) 0 ); break; case '?': printf("0-3 show rectangles,\nR/r to rotate 10 degrees\nZ/z to move closer/farther by 10%\nt for a top view\nf for a front view\ns for a side view\nv for a nice direct view\nS to toggle spinning\n= to report viewer location and distance\np to print the points\nw to toggle wireframe\nq to quit\n"); break; } glutPostRedisplay(); } /* ********************************************************************** Setting the viewport. To avoid distorting the image, we need to use letterboxing, and compare the aspect ratio of the window (ww/wh) with the aspect ratio of the image or projection (pw/ph). If the window is skinner than the image, we will have wasted space on top, and if the window is shorter than the image, we will have wasted space on the right. */ // Args are window width and window height void set_viewport(GLsizei ww, GLsizei wh) { GLint vh,vw; // viewport height and width // The following condition is equivalent to pw/ph > ww/wh, except it // avoids truncating division. if( pw*wh > ww*ph ) { // ignore hw, since window is too tall; waste space at the top vh = (ww*ph)/pw; vw = ww; } else { // ignore ww, since window is too wide; waste space at the right vh = wh; vw = (wh*pw)/ph; } // cout << "vh = " << vh << " and vw = " << vw << endl; glViewport(0,0,vw,vh); } /* ********************************************************************** Callbacks for reshape. Mainly need to reset the viewport, based on the new dimensions. */ void reshape(int ww, int wh) { win_width=ww; // Globals, so others can reset the viewport win_height=wh; set_viewport(ww,wh); } void set_view() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(90,1,1,40); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(viewer[0],viewer[1],viewer[2], lookat[0],lookat[1],lookat[2], vup[0],vup[1],vup[2]); pw=ph=5; set_viewport(win_width,win_height); } /* ================================================================ */ void ambient(GLfloat amount) { GLfloat amb[3]; amb[0]=amb[1]=amb[2]=amount; glLightModelfv(GL_LIGHT_MODEL_AMBIENT,amb); } void distantLight(GLint light_id) { GLfloat light_ambient[] = {0.5, 0.5, 0.4, 1}; GLfloat light_diffuse[] = {0.7, 0.7, 0.6, 1}; GLfloat light_specular[] = {0.7, 0.7, 0.6, 1}; GLfloat light_position[] = {1, 1, 1, 0}; glLightfv(light_id, GL_AMBIENT, light_ambient); glLightfv(light_id, GL_DIFFUSE, light_diffuse); glLightfv(light_id, GL_SPECULAR, light_specular); glLightfv(light_id, GL_POSITION, light_position); } /* ================================================================ */ void grayMaterial(GLfloat amb, GLfloat diff, GLfloat spec, GLfloat shininess) { int i; GLfloat mat_ambient[] = {0, 0, 0, 1}; GLfloat mat_diffuse[] = {0, 0, 0, 1}; GLfloat mat_specular[] = {0, 0, 0, 1}; for(i=0;i<3;++i) { mat_ambient[i] = amb; mat_diffuse[i] = diff; mat_specular[i] = spec; } glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialf (GL_FRONT, GL_SHININESS, shininess); } /* ================================================================ */ void display(void) { int i,j; glPushAttrib(GL_ALL_ATTRIB_BITS); // save graphics state glClearColor (0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Update viewer position in modelview matrix */ set_view(); glEnable(GL_LIGHTING); ambient(0.5); distantLight(GL_LIGHT0); glEnable(GL_LIGHT0); grayMaterial(0.4,0.6,0.5,10); draw_dome(); if(show_cp) { glDisable(GL_LIGHTING); glColor3f(0,1,0); glPointSize(3); glBegin(GL_POINTS); for(i=0; i<4; ++i) for(j=0; j<4; ++j) { glVertex3fv(&dome[i*12+j*3]); } glEnd(); glColor3f(0,0,1); glPointSize(5); glBegin(GL_POINTS); glVertex3fv(dome); glEnd(); glColor3f(1,1,0); glPointSize(6); glBegin(GL_POINTS); glVertex3fv(&dome[5*3]); glEnd(); } glFlush(); glPopAttrib(); glutSwapBuffers(); } int main(int argc, char** argv) { glutInit(&argc,argv); initDomeCP(); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(win_width,win_height); glutCreateWindow(argv[0]); glutDisplayFunc(display); set_view(); glutKeyboardFunc(keys); glutMouseFunc(mouse); glutReshapeFunc(reshape); glLineWidth(1); glEnable(GL_DEPTH_TEST); reshape(win_width,win_height); glutMainLoop(); }