#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define FAR_CLIP 500 #define PI 3.14159265 #define FOV 50 #define POSITIVE 1 #define NEGATIVE 0 #define ON 1 #define OFF 0 #define AIR_RESISTANCE 0.8 //The higher the number, the more resistance. (recommend between 0-1) #define DECAY 1.2 //higher = more energy lost (must be > 1) #define CYLINDER_SEGMENTS globals::number_segments //Very large effect on performance. Realism of animation increases with more segments. #define CYLINDER_LENGTH globals::length //length of cylinders. (default 7) #define NUMBER_OF_CYLINDERS globals::number_cylinders //Number around circle. (default: 20) #define SEGMENT_CHANGE 1 #define CYLINDER_CHANGE 2 #define LENGTH_CHANGE 3 #define BOUNCE_CHANGE 4 #define CYLINDER_DEGREES_PER_SIDE 20 namespace globals { static int view_width, view_height; static int mousebutton, mousestate; static float rotation[3] = {0, 0, 0}; static float zoom = 14; static float velocity = 1; static float degree = 0; static float spin_number = 10; static float vel_flag = POSITIVE; static float number_segments = 10; static float length = 10.0; static float number_cylinders = 20; static int mode = SEGMENT_CHANGE; static int lighting = ON; static int motion_blur = ON; static float motion_blur_value = .6; static float camera_pos[2] = {0, 0}; static float pause_mode = OFF; static int overlap_mode = 1; static float bounce; } void calcNormal(float x1, float x2, float x3, float y1, float y2, float y3, float z1, float z2, float z3, float &resx, float &resy, float &resz){ float vectora[4], vectorb[4], vectorc[4]; vectora[1] = x1 - x2; vectora[2] = y1 - y2; vectora[3] = z1 - z2; vectorb[1] = x2 - x3; vectorb[2] = y2 - y3; vectorb[3] = z2 - z3; vectorc[1] = (vectora[2] * vectorb[3]) - (vectora[3] * vectorb[2]); vectorc[2] = (vectora[3] * vectorb[1]) - (vectora[1] * vectorb[3]); vectorc[3] = (vectora[1] * vectorb[2]) - (vectora[2] * vectorb[1]); //then normalize the vector float magnitude = (float)(sqrt((vectorc[1] * vectorc[1]) + (vectorc[2] * vectorc[2]) + (vectorc[3] * vectorc[3]))); if (magnitude == 0) magnitude = 1; vectorc[1] = vectorc[1] / magnitude; vectorc[2] = vectorc[2] / magnitude; vectorc[3] = vectorc[3] / magnitude; resx = vectorc[1]; resy = vectorc[2]; resz = vectorc[3]; } void set_velocity(void) { float radians = 0; radians = AIR_RESISTANCE * (globals::degree * PI)/180; if ((globals::spin_number * sin(radians)) > 5){ globals::velocity = 5; } else if ((globals::spin_number * sin(radians)) < -5){ globals::velocity = -5; } else { globals::velocity = globals::spin_number * sin(radians); } if (globals::velocity > 0){ if (globals::vel_flag == NEGATIVE){ globals::spin_number = globals::spin_number/DECAY; } globals::vel_flag = POSITIVE; } if (globals::velocity < 0){ if (globals::vel_flag == POSITIVE){ globals::spin_number = globals::spin_number/DECAY; } globals::vel_flag = NEGATIVE; } globals::degree++; } void render_cylinder(float radius, float length, float sides) { float x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4, normx, normy, normz; float radiansA = 0, radiansB = 0; glBegin(GL_QUADS); for (int i = 0; i <= 180; i = i + sides){ radiansA = ( (i) * PI)/180; radiansB = ((i + sides) * PI)/180; x1 = radius * cos(radiansA); y1 = radius * sin(radiansA); z1 = 0; x2 = radius * cos(radiansB); y2 = radius * sin(radiansB); z2 = 0; x3 = radius * cos(radiansB); y3 = radius * sin(radiansB); z3 = length; x4 = radius * cos(radiansA); y4 = radius * sin(radiansA); z4 = length; if (globals::lighting == ON){ calcNormal(x1, x2, x3, y1, y2, y3, z1, z2, z3, normx, normy, normz); glNormal3f(normx, normy, normz); } glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2); glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4); } for (int i = 0; i <= 180; i = i + sides){ radiansA = ( (i) * PI)/180; radiansB = ((i + sides) * PI)/180; x1 = -radius * cos(radiansA); y1 = -radius * sin(radiansA); z1 = 0; x2 = -radius * cos(radiansB); y2 = -radius * sin(radiansB); z2 = 0; x3 = -radius * cos(radiansB); y3 = -radius * sin(radiansB); z3 = length; x4 = -radius * cos(radiansA); y4 = -radius * sin(radiansA); z4 = length; if (globals::lighting == ON){ calcNormal(x1, x2, x3, y1, y2, y3, z1, z2, z3, normx, normy, normz); glNormal3f(normx, normy, normz); } glVertex3f(x1, y1, z1); glVertex3f(x2, y2, z2); glVertex3f(x3, y3, z3); glVertex3f(x4, y4, z4); } glEnd(); } void camera(void) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(FOV, ((float)globals::view_width)/globals::view_height, 1, FAR_CLIP); gluLookAt(globals::zoom, globals::zoom, globals::zoom, 0, 0, 0, 0, 1, 0); } void display(void) { glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //Camera glRotatef(globals::camera_pos[0], -1, 0, 1); glRotatef(globals::camera_pos[1], 1, 0, 1); glTranslatef(0, 1, 0); //Object Spin glRotatef(globals::rotation[0], 1, 0, 0); glRotatef(globals::rotation[1], 0, 1, 0); glRotatef(globals::rotation[2], 0, 0, 1); //Model float degrees =90 - abs(globals::velocity * 15.0) + globals::bounce; globals::motion_blur_value = abs(globals::velocity)/7; //---String---// glRotatef(-90, 1, 0, 0); glColor3f(.5, .1, .1); for (int j = 0; j < CYLINDER_SEGMENTS/1.3; j++){ render_cylinder(0.3, CYLINDER_LENGTH/CYLINDER_SEGMENTS, 45); glTranslatef(0, 0, CYLINDER_LENGTH/CYLINDER_SEGMENTS); } for (int j = 0; j < CYLINDER_SEGMENTS/1.3; j++){ glTranslatef(0, 0, -CYLINDER_LENGTH/CYLINDER_SEGMENTS); } glRotatef(90, 1, 0, 0); //---Yellow Layer---// glColor3f(.5, .5, .1); for (int i = 0; i < NUMBER_OF_CYLINDERS; i++){ glRotatef(360/NUMBER_OF_CYLINDERS, 0, 1, 0); for (int j = 0; j < CYLINDER_SEGMENTS; j++){ if (CYLINDER_SEGMENTS != 1){ render_cylinder(0.3, CYLINDER_LENGTH/(CYLINDER_SEGMENTS-globals::overlap_mode), CYLINDER_DEGREES_PER_SIDE); glTranslatef(0, 0, CYLINDER_LENGTH/CYLINDER_SEGMENTS); glRotatef((degrees+10)/CYLINDER_SEGMENTS, 1, 0, 0); } else if (CYLINDER_SEGMENTS == 1){ render_cylinder(0.3, CYLINDER_LENGTH, CYLINDER_DEGREES_PER_SIDE); glTranslatef(0, 0, CYLINDER_LENGTH/CYLINDER_SEGMENTS); glRotatef((degrees+10)/CYLINDER_SEGMENTS, 1, 0, 0); } } for (int j = 0; j < CYLINDER_SEGMENTS; j++){ glRotatef(-(degrees+10)/CYLINDER_SEGMENTS, 1, 0, 0); glTranslatef(0, 0, -CYLINDER_LENGTH/CYLINDER_SEGMENTS); } } //--Red Layer---// glTranslatef(0, -.75, 0); glColor3f(.5, .1, .1); glRotatef((360/NUMBER_OF_CYLINDERS)/2, 0, 1, 0); for (int i = 0; i < NUMBER_OF_CYLINDERS; i++){ glRotatef(360/NUMBER_OF_CYLINDERS, 0, 1, 0); for (int j = 0; j < CYLINDER_SEGMENTS; j++){ if (CYLINDER_SEGMENTS != 1){ render_cylinder(0.3, CYLINDER_LENGTH/(CYLINDER_SEGMENTS-globals::overlap_mode), CYLINDER_DEGREES_PER_SIDE); glTranslatef(0, 0, CYLINDER_LENGTH/CYLINDER_SEGMENTS); glRotatef((degrees+10)/CYLINDER_SEGMENTS, 1, 0, 0); } else if (CYLINDER_SEGMENTS == 1){ render_cylinder(0.3, CYLINDER_LENGTH, CYLINDER_DEGREES_PER_SIDE); glTranslatef(0, 0, CYLINDER_LENGTH/CYLINDER_SEGMENTS); glRotatef((degrees+10)/CYLINDER_SEGMENTS, 1, 0, 0); } } for (int j = 0; j < CYLINDER_SEGMENTS; j++){ glRotatef(-(degrees+10)/CYLINDER_SEGMENTS, 1, 0, 0); glTranslatef(0, 0, -CYLINDER_LENGTH/CYLINDER_SEGMENTS); } } glRotatef(-(360/NUMBER_OF_CYLINDERS)/2, 0, 1, 0); //---Blue Layer---// glTranslatef(0, -.25, 0); glColor3f(.1, .1, .5); for (int i = 0; i < NUMBER_OF_CYLINDERS/2; i++){ glRotatef(360/(NUMBER_OF_CYLINDERS/2), 0, 1, 0); glRotatef(90, 1, 0, 0); glTranslatef(0, 1, 0); for (int j = 0; j < CYLINDER_SEGMENTS; j++){ if (CYLINDER_SEGMENTS != 1){ render_cylinder(0.3, (CYLINDER_LENGTH/1.5)/(CYLINDER_SEGMENTS-globals::overlap_mode), CYLINDER_DEGREES_PER_SIDE); glTranslatef(0, 0, (CYLINDER_LENGTH/1.5)/CYLINDER_SEGMENTS); glRotatef((120-(degrees*1.3))/CYLINDER_SEGMENTS, -1, 0, 0); } else if (CYLINDER_SEGMENTS == 1){ render_cylinder(0.3, CYLINDER_LENGTH/1.5, CYLINDER_DEGREES_PER_SIDE); glTranslatef(0, 0, (CYLINDER_LENGTH/1.5)/CYLINDER_SEGMENTS); glRotatef((120-(degrees*1.3))/CYLINDER_SEGMENTS, -1, 0, 0); } } for (int j = 0; j < CYLINDER_SEGMENTS; j++){ glRotatef(-(120-(degrees*1.3))/CYLINDER_SEGMENTS, -1, 0, 0); glTranslatef(0, 0, -(CYLINDER_LENGTH/1.5)/CYLINDER_SEGMENTS); } glTranslatef(0, -1, 0); glRotatef(-90, 1, 0, 0); } if (globals::pause_mode == OFF){ set_velocity(); globals::rotation[1] = globals::rotation[1] + globals::velocity; } if (globals::motion_blur == ON){ glAccum(GL_MULT, globals::motion_blur_value); glAccum(GL_ACCUM, 1 - globals::motion_blur_value); glAccum(GL_RETURN, 1.0); } glutSwapBuffers(); } void reshape (int w, int h) { glClearAccum(0.0, 0.0, 0.0, 1.0); glClear(GL_ACCUM_BUFFER_BIT); glViewport (0, 0, (GLsizei) w, (GLsizei) h); globals::view_width = w; globals::view_height = h; glutPostRedisplay(); } void keyboard(unsigned char key, int x, int y) { switch (key){ case '+': if (globals::mode == SEGMENT_CHANGE) globals::number_segments++; else if (globals::mode == LENGTH_CHANGE) globals::length++; else if (globals::mode == CYLINDER_CHANGE) globals::number_cylinders++; else if (globals::mode == BOUNCE_CHANGE) globals::bounce = globals::bounce - 1; break; case '-': if (globals::mode == SEGMENT_CHANGE && globals::number_segments > 1) globals::number_segments--; else if (globals::mode == LENGTH_CHANGE && globals::length > 1) globals::length--; else if (globals::mode == CYLINDER_CHANGE && globals::number_cylinders > 1) globals::number_cylinders--; else if (globals::mode == BOUNCE_CHANGE) globals::bounce = globals::bounce + 1; break; case '1': globals::mode = SEGMENT_CHANGE; break; case '2': globals::mode = LENGTH_CHANGE; break; case '3': globals::mode = CYLINDER_CHANGE; break; case '4': globals::mode = BOUNCE_CHANGE; break; case 'w': if (globals::zoom > 2) globals::zoom = globals::zoom - 0.5; break; case 's': globals::zoom = globals::zoom + 0.5; break; case 'r': globals::velocity = 1; globals::degree = 0; globals::spin_number = 10; globals::vel_flag = POSITIVE; globals::pause_mode = OFF; break; case 'p': if (globals::pause_mode == OFF){ globals::pause_mode = ON; } else if (globals::pause_mode == ON){ globals::pause_mode = OFF; } break; case ' ': if (globals::lighting == OFF){ glEnable(GL_LIGHTING); globals::lighting = ON; } else if (globals::lighting == ON){ glDisable(GL_LIGHTING); globals::lighting = OFF; } break; case 'b': if (globals::motion_blur == ON){ globals::motion_blur = OFF; } else if (globals::motion_blur == OFF){ globals::motion_blur = ON; } break; case 'o': if (globals::overlap_mode == 0){ globals::overlap_mode = 1; } else if (globals::overlap_mode == 1){ globals::overlap_mode = 0; } break; case 'i': globals::camera_pos[0] = globals::camera_pos[0] + 5; break; case 'k': globals::camera_pos[0] = globals::camera_pos[0] - 5; break; case 'j': globals::camera_pos[1] = globals::camera_pos[1] + 5; break; case 'l': globals::camera_pos[1] = globals::camera_pos[1] - 5; break; default: break; } glutPostRedisplay(); } void mouse (int button, int state, int x, int y) { } void mouse_movement (int x, int y) { } void init(void) { glClearColor (.5, .5, .5, 0); glEnable(GL_DEPTH_TEST); glEnable(GL_COLOR_MATERIAL); glShadeModel(GL_FLAT); glDepthFunc(GL_LEQUAL); glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT, GL_EMISSION); glDepthMask(GL_TRUE); float diffuse0[] = {0.2, 0.2, 0, 1.0}; float ambient0[] = {0.2, 0.2, 0.2, 1.0}; float specular0[] = {0.4, 0.4, 0.1, 1.0}; float position0[] = {50, 50, 0, 0}; float diffuse[] = {0.9, 0.9, 0.9, 1}; float ambient[] = {0.2, 0.2, 0.2, 1}; float specular[] = {0.9, 0.9, 0.9, 1}; float emission[] = {0, 0, 0, 1}; float shine = 12; glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0); glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0); glLightfv(GL_LIGHT0, GL_SPECULAR, specular0); glLightfv(GL_LIGHT0, GL_POSITION, position0); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission); glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, shine); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize (500,500); glutInitWindowPosition (100, 100); glutCreateWindow (argv[0]); init(); glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMouseFunc(mouse); glutMotionFunc(mouse_movement); glutMainLoop(); return 0; }