/************************************************************** * file: rotate.c * * Author: Chen Li <chenli@cs.nyu.edu> * Homework 1 for Chee's Visualization Course, 1998 * Modified by Chee for Visualization Course, 2001 * * Synopsis: * This program displays two cubes, one inside the * other. The outer one is transparent so we can see * the inner cube. The vertices of the cube have * different colors. Both cubes can rotate independently. * You can control the speed of rotation using the mouse. * Three radio buttons allow you to control lighting, * shading, and turn on/off rotation. * * * * $Id: rotate.html,v 1.1 2001/09/26 21:46:27 yap Exp yap $ **************************************************************/ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <GL/glut.h> // define some global consts #define MY_CUBE1_LIST 1 #define MY_CUBE2_LIST 2 int mainWin, win1, win2, win3; // define some labels shown with the buttons char *label1 = "FLAT"; char *label2 = "ROTATE"; char *label3 = "LIGHT"; // define some color constants #define BLACK {0.0,0.0,0.0} #define RED {1.0,0.0,0.0} #define GREEN {0.0,1.0,0.0} #define BLUE {0.0,0.0,1.0} #define YELLOW {1.0,1.0,0.0} #define MAGENTA {1.0,0.0,1.0} #define CYAN {0.0,1.0,1.0} #define WHITE {1.0,1.0,1.0} #define UNKNOWN {0.2,0.3,0.5} // define some cube constants GLfloat vertices[][3] = {{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; // define the normals on vertices GLfloat normals[][3] = {{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; // define the colors on vertices GLfloat cube1_colors[][3] = {UNKNOWN, RED, YELLOW, GREEN, BLUE, MAGENTA, WHITE, CYAN }; // define a different set of colors for another cube GLfloat cube2_colors[][3] = {{0.3,0.2,0.5}, {.8,0.1,0.1}, {.4,.4,0.2}, {0.1,.8,0.1}, {.1,.1,.8} ,{.4,0.2,.4}, {1.0,1.0,.3}, {0.0,.9 ,1.0} }; // define some global states static GLfloat axis1[] = {1.0,1.0,1.0}; static GLfloat w1[] = {1.0, 1.0, 1.0}; static GLfloat theta1[] = {0.0, 0.0, 0.0}; static GLfloat axis2[] = {0.0,1.0,0.0}; static GLfloat w2[] = {1.0, 1.0, 1.0}; static GLfloat theta2[] = {0.0, 0.0, 0.0}; int spinOn=1, smoothShading=1, lightOff=1; // define light parameters GLfloat global_ambient[] = {0.2, 0.2, 0.2, 1.0}; // Global ambient term GLfloat light0_diffuse[] = {0.0, 1.0, 0.0, 1.0}; // Green diffuse light GLfloat light0_specular[] = {1.0, 1.0, 1.0, 0.0}; // White specular light GLfloat light0_position[] = {1.0, 1.0, 1.0, 0.0}; // Infinite light location GLfloat light1_diffuse[] = {1.0, 0.0, 0.0, 1.0}; // Red diffuse light GLfloat light1_specular[] = {1.0, 1.0, 1.0, 0.0}; // White specular light GLfloat light1_position[] = {-1.0, 1.0, 1.0, 0.0}; // Infinite light location GLfloat light2_diffuse[] = {0.0, 0.0, 1.0, 1.0}; // Blue diffuse light GLfloat light2_specular[] = {1.0, 1.0, 1.0, 0.0}; // White specular light GLfloat light2_position[] = {0.0, -1.0, 1.0, 0.0}; // Infinite light location // declare some functions in advance void polygon1(int a, int b, int c , int d); void polygon2(int a, int b, int c , int d); void drawButton(); void drawPressedButton(); void buildCube(); void calculateRotationForCube1(); void calculateRotationForCube2(); GLfloat* crossProduct(float x1, float y1, float z1, float x2, float y2, float z2); float dotProduct(float x1, float y1, float z1, float x2, float y2, float z2); float getAngle(float x1, float y1, float z1, float x2, float y2, float z2); /*************************************************************** * OpenGL/GLUT callback functions ***************************************************************/ static void Idle( void ) { /* update animation vars */ if (spinOn) { calculateRotationForCube1(); calculateRotationForCube2(); } // redisplay the subwindows glutSetWindow(win1); glutPostRedisplay(); glutSetWindow(win2); glutPostRedisplay(); glutSetWindow(win3); glutPostRedisplay(); // set the current window back to the main window and mark redisplay glutSetWindow(mainWin); glutPostRedisplay(); } static void Display( void ) { /* display callback, clear frame buffer and z buffer, rotate cube and draw, swap buffers */ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // use smooth or flat shadings if (smoothShading) glShadeModel(GL_SMOOTH); else glShadeModel(GL_FLAT); // enable/disable lightings if (lightOff) glDisable(GL_LIGHTING); else glEnable(GL_LIGHTING); // draw the inner cube glPushMatrix(); glRotatef(theta1[0], 1.0, 0.0, 0.0); glRotatef(theta1[1], 0.0, 1.0, 0.0); glRotatef(theta1[2], 0.0, 0.0, 1.0); glPolygonMode(GL_FRONT, GL_FILL); glCallList(MY_CUBE1_LIST); glPopMatrix(); // draw the outer cube glPushMatrix(); glScalef(3.0, 3.0, 3.0); glRotatef(theta2[0], 1.0, 0.0, 0.0); glRotatef(theta2[1], 0.0, 1.0, 0.0); glRotatef(theta2[2], 0.0, 0.0, 1.0); glPolygonMode(GL_FRONT, GL_LINE); glCallList(MY_CUBE2_LIST); glPopMatrix(); // force refresh glFlush(); glutSwapBuffers(); } static void Reshape( int width, int height ) { // printf("width=%d, height=%d \n", width, height); glViewport( 0, 0, width, height ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); // glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 ); glFrustum( -2.0, 2.0, -2.0, 2.0, 5.0, 25.0 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glTranslatef( 0.0, 0.0, -15.0 ); } static void Key( unsigned char key, int x, int y ) { switch (key) { case 27: exit(0); break; case 'r': case 'R': spinOn = 1 - spinOn; //toggle between rotate and spin mode break; case 's': case 'S': smoothShading = 1- smoothShading; //toggle between the shading modes break; case 'l': case 'L': lightOff = 1 - lightOff; // turn on/off light break; } glutPostRedisplay(); } static void Mouse(int button, int state, int x, int y) { /* mouse callback, specify the axis and degree of rotation */ static int x1, y1, x2, y2, deltaX, deltaY; GLfloat *tmp = NULL; float angSpeed; int i; if (((button==GLUT_MIDDLE_BUTTON) || (button==GLUT_LEFT_BUTTON)) && state == GLUT_DOWN) { x1 = x - 200; y1 = - y + 200; // printf("x1=%d, y1=%d\n", x1, y1); } if(button==GLUT_MIDDLE_BUTTON && state == GLUT_UP) { x2 = x - 200; y2 = -y + 200; deltaX = x2 - x1; deltaY = y2 - y1; if ((abs(deltaX) <=1 ) && (abs(deltaY) <=1 ) ) return; glPushMatrix(); tmp = crossProduct(x1, y1, 150.0, x2, y2, 150.0); axis1[0] = tmp[0]; axis1[1] = tmp[1]; axis1[2] = tmp[2]; angSpeed = (spinOn)? getAngle(x1, y1, 150, x2, y2, 150) / 20.0 : getAngle(x1, y1, 150, x2, y2, 150) / 4.0; for (i=0; i<3; i++) { w1[i] = angSpeed*axis1[i]; } /* printf("x1=%d, y1=%d, x2=%d, y2=%d \n", x1, y1, x2, y2); printf("The axis for rotation one is %f, %f, %f. \n", axis1[0], axis1[1], axis1[2]); printf("The inner rotation speed is %f %f %f \n\n", w1[0], w1[1], w1[2]); */ if (!spinOn) { // we do one-time rotation calculateRotationForCube1(); glutPostRedisplay(); // restore to angular speed for (i=0; i<3; i++) w1[i] /= 5.0; } } if(button==GLUT_LEFT_BUTTON && state == GLUT_UP) { x2 = x - 200; y2 = -y + 200; deltaX = x2 - x1; deltaY = y2 - y1; if ((abs(deltaX) <=1 ) && (abs(deltaY) <=1 ) ) return; tmp = crossProduct(x1, y1, 150.0, x2, y2, 150.0); axis2[0] = tmp[0]; axis2[1] = tmp[1]; axis2[2] = tmp[2]; angSpeed = (spinOn)? getAngle(x1, y1, 150, x2, y2, 150) / 20.0 : getAngle(x1, y1, 150, x2, y2, 150) / 4.0; for (i=0; i<3; i++) { w2[i] = angSpeed*axis2[i]; } /* printf("x1=%d, y1=%d, x2=%d, y2=%d \n", x1, y1, x2, y2); printf("The axis for rotation two is %f, %f, %f. \n", axis2[0], axis2[1], axis2[2]); printf("The outer rotation speed is %f %f %f\n\n", w2[0], w2[1], w2[2]); */ if (!spinOn) { // we do one-time rotation calculateRotationForCube2(); glutPostRedisplay(); // restore to angular speed for (i=0; i<3; i++) w2[i] /= 5.0; } } } static void Init( void ) { /* setup lighting, etc */ // glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); glEnable(GL_LIGHT2); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); glMaterialf(GL_FRONT, GL_SHININESS, 100.0); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular); glLightfv(GL_LIGHT0, GL_POSITION, light0_position); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular); glLightfv(GL_LIGHT1, GL_POSITION, light1_position); glLightfv(GL_LIGHT2, GL_DIFFUSE, light2_diffuse); glLightfv(GL_LIGHT2, GL_SPECULAR, light2_specular); glLightfv(GL_LIGHT2, GL_POSITION, light2_position); // build some display lists buildCube(); } void right_menu(int id) { glutIdleFunc(NULL); if(id == 1) exit(0); else Display(); glutIdleFunc(Idle); } void displayButton(char* label, int state) { int len, i; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glColor3f(1.0, 1.0, 0.0); glPushMatrix(); glTranslatef(-40, 0, 0); glScalef(8.0, 8.0, 1.0); if (state) drawButton(); else drawPressedButton(); glScalef(.125, .125, 1.0); glTranslatef(20, -5.0, 0.0); glScalef(0.1, 0.1, 0.1); len = (int) strlen(label); for (i = 0; i < len; i++) { glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, label[i]); } glPopMatrix(); glFlush(); glutSwapBuffers(); } void displayButton1(void) { displayButton(label1, smoothShading); } static void flatButtonMouse(int button, int state, int x, int y) { /* mouse callback, to toggle the flat shading option */ if (state == GLUT_DOWN) { smoothShading = 1 - smoothShading; } } void displayButton2(void) { displayButton(label2, spinOn); } static void spinButtonMouse(int button, int state, int x, int y) { /* mouse callback, to toggle the spin/rotate mode */ if (state == GLUT_DOWN) { spinOn = 1 - spinOn; } } void displayButton3(void) { displayButton(label3, lightOff); } static void lightButtonMouse(int button, int state, int x, int y) { /* mouse callback, to toggle light on/off mode */ if (state == GLUT_DOWN) { lightOff = 1 - lightOff; } } static void buttonReshape( int width, int height ) { // printf("width=%d, height=%d \n", width, height); glViewport( 0, 0, width, height ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluOrtho2D(0, width, 0, height); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glTranslatef( width/2, height/2, 0.0 ); } int main( int argc, char *argv[] ) { glutInit( &argc, argv ); glutInitWindowSize( 400, 400 ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); //define the top level window mainWin = glutCreateWindow(argv[0]); Init(); // define a menu glutCreateMenu(right_menu); glutAddMenuEntry("Quit",1); glutAddMenuEntry("Reset",2); glutAttachMenu(GLUT_RIGHT_BUTTON); // end menu definition glutReshapeFunc( Reshape ); glutKeyboardFunc( Key ); glutMouseFunc( Mouse ); // glutSpecialFunc( SpecialKey ); glutDisplayFunc( Display ); glutIdleFunc( Idle ); glEnable(GL_DEPTH_TEST); // Enable hidden--surface--removal // define some sub-windows to contain the radio buttons // flat/smooth shading button win1 = glutCreateSubWindow(mainWin, 0, 0, 100, 20); glutDisplayFunc(displayButton1); glClearColor(0.0, 0.0, 0.5, 1.0); glutReshapeFunc( buttonReshape ); glutMouseFunc( flatButtonMouse ); // spin/rotate button win2 = glutCreateSubWindow(mainWin, 150, 0, 100, 20); glutDisplayFunc(displayButton2); glClearColor(0.0, 0.0, 0.5, 0.0); glutReshapeFunc( buttonReshape ); glutMouseFunc( spinButtonMouse ); // light on/off button win3 = glutCreateSubWindow(mainWin, 300, 0, 100, 20); glutDisplayFunc(displayButton3); glClearColor(0.0, 0.0, 0.5, 0.0); glutReshapeFunc( buttonReshape ); glutMouseFunc( lightButtonMouse ); // end of subwins definitions // set the current window back to the top level one glutSetWindow(mainWin); // start the events handling loop glutMainLoop(); } /********************************************************* * define some supporting functions *********************************************************/ void drawButton() { // draw a square of size two glBegin(GL_LINE_LOOP); glVertex3f(1.0, 1.0, 0.0); glColor3f(1.0, 1.0, 0.0); glVertex3f(1.0, -1.0, 0.0); glColor3f(1.0, 1.0, 0.0); glVertex3f(-1.0, -1.0, 0.0); glColor3f(1.0, 1.0, 0.0); glVertex3f(-1.0, 1.0, 0.0); glColor3f(1.0, 1.0, 0.0); glEnd(); } void drawPressedButton() { drawButton(); // draw a cross in the square glBegin(GL_LINES); glVertex3f(-1.0, -1.0, 0.0); glColor3f(1.0, 1.0, 0.0); glVertex3f(1.0, 1.0, 0.0); glColor3f(1.0, 1.0, 0.0); glVertex3f(-1.0, 1.0, 0.0); glColor3f(1.0, 1.0, 0.0); glVertex3f(1.0, -1.0, 0.0); glColor3f(1.0, 1.0, 0.0); glEnd(); } void mapVertexAttr(int tag, int i) { if (tag == MY_CUBE1_LIST) { glColor3fv(cube1_colors[i]); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, cube1_colors[i]); } else { glColor3fv(cube2_colors[i]); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, cube2_colors[i]); } glNormal3fv(normals[i]); } void polygon(int tag, int a, int b, int c , int d) { /* draw a polygon via list of vertices */ glBegin(GL_POLYGON); mapVertexAttr(tag, a); glVertex3fv(vertices[a]); mapVertexAttr(tag, b); glVertex3fv(vertices[b]); mapVertexAttr(tag, c); glVertex3fv(vertices[c]); mapVertexAttr(tag, d); glVertex3fv(vertices[d]); glEnd(); } void buildCube(void) { //define two display lists with difference color scheme /* map vertices to faces */ glNewList(MY_CUBE1_LIST, GL_COMPILE); polygon(MY_CUBE1_LIST, 0,3,2,1); polygon(MY_CUBE1_LIST, 2,3,7,6); polygon(MY_CUBE1_LIST, 7,3,0,4); polygon(MY_CUBE1_LIST, 1,2,6,5); polygon(MY_CUBE1_LIST, 4,5,6,7); polygon(MY_CUBE1_LIST, 5,4,0,1); glEndList(); glNewList(MY_CUBE2_LIST, GL_COMPILE); polygon(MY_CUBE2_LIST, 0,3,2,1); polygon(MY_CUBE2_LIST, 2,3,7,6); polygon(MY_CUBE2_LIST, 7,3,0,4); polygon(MY_CUBE2_LIST, 1,2,6,5); polygon(MY_CUBE2_LIST, 4,5,6,7); polygon(MY_CUBE2_LIST, 5,4,0,1); glEndList(); }; void calculateRotationForCube1() { theta1[0] += w1[0]; theta1[0] = (theta1[0] >= 360)? theta1[0]-360 : theta1[0]; theta1[1] += w1[1]; theta1[1] = (theta1[1] >= 360)? theta1[1]-360 : theta1[1]; theta1[2] += w1[2]; theta1[2] = (theta1[2] >= 360)? theta1[2]-360 : theta1[2]; } void calculateRotationForCube2() { theta2[0] += w2[0]; theta2[0] = (theta2[0] >= 360)? theta2[0]-360 : theta2[0]; theta2[1] += w2[1]; theta2[1] = (theta2[1] >= 360)? theta2[1]-360 : theta2[1]; theta2[2] += w2[2]; theta2[2] = (theta2[2] >= 360)? theta2[2]-360 : theta2[2]; } // compute the cross product of two vectors GLfloat* crossProduct(float x1, float y1, float z1, float x2, float y2, float z2) { GLfloat *r; double d; if ((r=(GLfloat*)malloc(sizeof(GLfloat) * 3)) == NULL) { perror("Memory allocation failed -- Chen Li"); exit(1); } // CrossProduct of ([x1, y1, z1], [x2, y2, z2]) = // [y1 z2 - z1 y2, z1 x2 - x1 z2, x1 y2 - y1 x2] r[0] = y1*z2 - z1*y2; r[1] = z1*x2 - x1*z2; r[2] = x1*y2 - y1*x2; // normalize it d = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]); r[0] /= d; r[1] /= d; r[2] /= d; return(r); } // compute the dot product of two vectors float dotProduct(float x1, float y1, float z1, float x2, float y2, float z2) { return (x1*x2 + y1*y2 + z1*z2); } // compute the length of a vector float lengthOfVector(float x, float y, float z) { return sqrt(x*x+y*y+z*z); } // compute the angle between two vectors float getAngle(float x1, float y1, float z1, float x2, float y2, float z2) { float cos; cos = dotProduct(x1, y1, z1, x2, y2, z2) / (lengthOfVector(x1, y1, z1) * lengthOfVector(x2, y2, z2)); // printf("cos(theta) is %f, acos is %f \n", cos, acos(cos)); return(acos(cos)*360/(2*3.1415926)); // convert to degrees }