/**************************************************************
* 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
}