This OpenGL allows the user to move a 3D wagon on flat surface. Use the left and right arrow keys to rotate the vehicle and the up and down keys to control the speed. Space will stop the vehicle. There are 4 camera modes: first person, bird's eye, third person, orbit. To switch between these camera modes, use keys 1 - 4 respectively. The mouse scroll wheel may be used for zooming if allowed in the particular camera mode. Pressing 0 will reset the vehicle to the origin and in a resting position. A mini-map is included. Toggle the wireframe model of the wagon, press w.
wagon.cc:
// Peter Bui // CSE 40166 Computer Graphics (Fall 2010) // Example 13: wagon #include "color.h" #include <cmath> #include <cstdio> #include <cstdlib> #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut.h> #endif using namespace std; // Constants ------------------------------------------------------------------- #define WHEEL_UP 3 #define WHEEL_DOWN 4 #define CAMERA_DISTANCE_MIN 1.0 #define CAMERA_DISTANCE_MAX 100.0 enum { DL_WAGON = 1, DL_GRID, DL_JENNY_CUB, DL_BOX }; enum { CAMERA_FIRST_PERSON, CAMERA_BIRDS_EYE, CAMERA_THIRD_PERSON, CAMERA_ORBIT }; // Global variables ------------------------------------------------------------ static size_t WindowWidth = 640; static size_t WindowHeight = 480; static GLint MouseX = 0; static GLint MouseY = 0; static double CameraLatitude = 45.0; static double CameraLongitude = 25.0; static double CameraDistance = 50.0; static double EyeX = -100.0; static double EyeY = 50.0; static double EyeZ = 100.0; static double DirX = 0.0; static double DirY = 0.0; static double DirZ = 0.0; static double WagonOrientation = 0.0; static double WagonSpeed = 0.0; static double WagonX = 0.0; static double WagonZ = 0.0; static double WagonLength = 10.0; static double WagonWidth = 4.0; static double WagonSideWidth = 0.5; static double JennyCubOrientation = 0.0; static double JennyCubOrientationDelta = 2.0; static GLUquadric *Quadric = NULL; static int CameraMode = CAMERA_ORBIT; static bool WireFrameMode = false; // Update camera --------------------------------------------------------------- void update_camera_location() { double L = CameraDistance * cos(M_PI*CameraLongitude/180.0); switch (CameraMode) { case CAMERA_ORBIT: EyeX = WagonX + L * -sin(M_PI*CameraLatitude/180.0); EyeY = CameraDistance * sin(M_PI*CameraLongitude/180.0); EyeZ = WagonZ + L * cos(M_PI*CameraLatitude/180.0); DirX = WagonX; DirY = 0.0; DirZ = WagonZ; break; case CAMERA_BIRDS_EYE: EyeX = WagonX; EyeY = CameraDistance; EyeZ = WagonZ; DirX = WagonX; DirY = 0.0; DirZ = WagonZ - 0.01; break; case CAMERA_FIRST_PERSON: EyeX = WagonX; EyeY = 6.0; EyeZ = WagonZ; DirX = WagonX + CameraDistance*cos(WagonOrientation*M_PI/180.0); DirY = 6.0; DirZ = WagonZ - CameraDistance*sin(WagonOrientation*M_PI/180.0); break; case CAMERA_THIRD_PERSON: EyeX = WagonX - CameraDistance*cos(WagonOrientation*M_PI/180.0); EyeY = 10.0; EyeZ = WagonZ + CameraDistance*sin(WagonOrientation*M_PI/180.0); DirX = WagonX + CameraDistance/2.0*cos(WagonOrientation*M_PI/180.0); DirY = 10.0; DirZ = WagonZ - CameraDistance/2.0*sin(WagonOrientation*M_PI/180.0); } glutPostRedisplay(); } // Initialize scene ------------------------------------------------------------ void init_scene() { glEnable(GL_DEPTH_TEST); GLfloat diffuse0[] = { 0.8, 0.8, 0.8, 1.0 }; GLfloat ambient0[] = { 0.2, 0.2, 0.2, 1.0 }; GLfloat specular0[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat position0[] = { 100.0, 100.0, 100.0, 1.0 }; glEnable(GL_LIGHTING); 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); glEnable(GL_COLOR_MATERIAL) ; glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_NORMALIZE); glEnable(GL_SMOOTH); if (Quadric == NULL) Quadric = gluNewQuadric(); update_camera_location(); } // Draw wagon ------------------------------------------------------------------ void draw_wagon_base() { glPushMatrix(); { glScalef(WagonLength, 1.0, WagonWidth); glutSolidCube(1.0); } glPopMatrix(); } void draw_wagon_side(double tx, double ty, double tz, double sx, double sy, double sz) { glPushMatrix(); { glTranslatef(tx, ty, tz); glScalef(sx, sy, sz); glutSolidCube(1.0); } glPopMatrix(); } void draw_wagon_logo(double tx, double ty, double tz, bool reflect) { glPushMatrix(); { glPushAttrib(GL_LINE_BIT); glColor3f(1.0, 1.0, 1.0); glLineWidth(4.0); glTranslatef(tx, ty, tz); glScalef(reflect ? -0.01 : 0.01, 0.01, 0.01); for (const char *c = "Radio Flyer"; *c; c++) glutStrokeCharacter(GLUT_STROKE_ROMAN, *c); glPopAttrib(); } glPopMatrix(); } void draw_wagon_body() { glColor3f(1.0, 0.0, 0.0); draw_wagon_base(); draw_wagon_side(0.0, 1.0, -(WagonWidth/2.0 - WagonSideWidth/2.0), WagonLength, 1.0, WagonSideWidth); draw_wagon_side(0.0, 1.0, (WagonWidth/2.0 - WagonSideWidth/2.0), WagonLength, 1.0, WagonSideWidth); draw_wagon_side( (WagonLength/2.0 - WagonSideWidth/2.0), 1.0, 0.0, WagonSideWidth, 1.0, WagonWidth); draw_wagon_side(-(WagonLength/2.0 - WagonSideWidth/2.0), 1.0, 0.0, WagonSideWidth, 1.0, WagonWidth); draw_wagon_logo(-WagonLength/2.0*0.75, 0.0, WagonWidth/2.0, false); draw_wagon_logo( WagonLength/2.0*0.75, 0.0, -WagonWidth/2.0, true); } void draw_wagon_wheel(double tx, double ty, double tz) { glPushMatrix(); { glTranslatef(tx, ty, tz); glColor3f(1.0, 1.0, 1.0); gluDisk(Quadric, 0.0, 1.0, 24, 24); glColor3f(0.0, 0.0, 1.0); glutSolidTorus(0.5, 0.9, 24, 24); } glPopMatrix(); } void draw_wagon_spoke(double tx, double ty, double tz, double length) { glPushMatrix(); { glTranslatef(tx, ty, tz); glColor3f(0.0, 1.0, 0.0); gluCylinder(Quadric, 0.5, 0.5, length, 24, 24); glTranslatef(0.0, 0.30, -tz); glRotatef(-90.0, 1.0, 0.0, 0.0); glutSolidCone(0.25, 1.0, 24, 24); } glPopMatrix(); } void draw_wagon_wheels() { draw_wagon_wheel(-WagonLength/2.0*0.75, -1.75, -WagonWidth/2.0 - 0.5); draw_wagon_wheel(-WagonLength/2.0*0.75, -1.75, WagonWidth/2.0 + 0.5); draw_wagon_wheel( WagonLength/2.0*0.75, -1.75, WagonWidth/2.0 + 0.5); draw_wagon_wheel( WagonLength/2.0*0.75, -1.75, -WagonWidth/2.0 - 0.5); draw_wagon_spoke(-WagonLength/2.0*0.75, -1.75, -WagonWidth/2.0 - 0.5, WagonWidth + 1.0); draw_wagon_spoke( WagonLength/2.0*0.75, -1.75, -WagonWidth/2.0 - 0.5, WagonWidth + 1.0); } void draw_wagon() { draw_wagon_body(); draw_wagon_wheels(); } // Draw jenny cub -------------------------------------------------------------- void draw_jenny_cub_body() { glPushMatrix(); { glColor3f(0.75, 0.5, 0.0); glRotatef(-90.0, 1.0, 0.0, 0.0); glutSolidCone(1.5, 2.0, 24, 24); } glPopMatrix(); } void draw_jenny_cub_ears() { glPushMatrix(); { glRotatef(90.0, 0.0, 1.0, 0.0); glPushMatrix(); { glTranslatef( cos(M_PI/5), sin(M_PI/5), 0.0); glutSolidTorus(0.15, 0.25, 24, 24); gluDisk(Quadric, 0.0, 0.25, 24, 24); } glPopMatrix(); glPushMatrix(); { glTranslatef(-cos(M_PI/5), sin(M_PI/5), 0.0); glutSolidTorus(0.15, 0.25, 24, 24); gluDisk(Quadric, 0.0, 0.25, 24, 24); } glPopMatrix(); } glPopMatrix(); } void draw_jenny_cub_eyes() { glPushAttrib(GL_ALL_ATTRIB_BITS); glColor3f(0.0, 0.0, 0.0); glPushMatrix(); { glTranslatef(0.9*cos(M_PI*0.1), 0.3, 0.9*sin(M_PI*0.1)); glutSolidSphere(0.1, 24, 24); } glPopMatrix(); glPushMatrix(); { glTranslatef(0.9*cos(M_PI*0.1), 0.3, -0.9*sin(M_PI*0.1)); glutSolidSphere(0.1, 24, 24); } glPopMatrix(); glPopAttrib(); } void draw_jenny_cub_nose() { glPushAttrib(GL_ALL_ATTRIB_BITS); glColor3f(0.0, 0.0, 0.0); glPushMatrix(); { glTranslatef(0.5*0.9*cos(M_PI*0.1), 0.5*0.9*sin(M_PI*0.1), 0.0); glutSolidSphere(0.15, 24, 24); } glPopMatrix(); glPopAttrib(); } void draw_jenny_cub_head() { glPushMatrix(); { glColor3f(0.75, 0.5, 0.0); glTranslatef(0.0, 2.5, 0.0); glutSolidSphere(1.0, 24, 24); draw_jenny_cub_ears(); draw_jenny_cub_eyes(); glTranslatef(0.75, -0.25, 0.0); glutSolidSphere(0.5, 24, 24); draw_jenny_cub_nose(); } glPopMatrix(); } void draw_jenny_cub() { draw_jenny_cub_body(); draw_jenny_cub_head(); } // Draw grid ------------------------------------------------------------------- void draw_grid() { glPushMatrix(); glPushAttrib(GL_LIGHTING_BIT); { glDisable(GL_LIGHTING); glScalef(5.0, 5.0, 5.0); for(int k = 0; k < 2; k++) { for(int i = -50; i < 51; i++) { glBegin(GL_LINE_STRIP); { for(int j = -50; j < 51; j++) { glColor3fv(color_rotate()); glVertex3f(k == 0 ? i : j, 0, k == 0 ? j : i); } } glEnd(); } } } glPopAttrib(); glPopMatrix(); } // Make display lists ---------------------------------------------------------- void make_display_lists() { glNewList(DL_WAGON, GL_COMPILE); { draw_wagon(); }; glEndList(); glNewList(DL_JENNY_CUB, GL_COMPILE); { draw_jenny_cub(); }; glEndList(); glNewList(DL_GRID, GL_COMPILE); { draw_grid(); }; glEndList(); glNewList(DL_BOX, GL_COMPILE); { glBegin(GL_POLYGON); { glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f( 0.5, 0.5); glVertex2f( 0.5, -0.5); } glEnd(); }; glEndList(); } // Draw scene ------------------------------------------------------------------ void draw_scene() { glViewport(0, 0, WindowWidth, WindowHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (GLdouble)WindowWidth/(GLdouble)WindowHeight, 1.0, 750.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(EyeX, EyeY, EyeZ, DirX, DirY, DirZ, 0.0, 1.0, 0.0); glPolygonMode(GL_FRONT_AND_BACK, WireFrameMode ? GL_LINE : GL_FILL); glEnable(GL_LIGHTING); glShadeModel(GL_SMOOTH); glPushMatrix(); { glTranslatef(WagonX, 3.0, WagonZ); glRotatef(WagonOrientation, 0.0, 1.0, 0.0); glCallList(DL_WAGON); glTranslatef(-WagonLength/4.0, 0.0, 0.0); glRotatef(JennyCubOrientation, 0.0, 1.0, 0.0); glCallList(DL_JENNY_CUB); } glPopMatrix(); glCallList(DL_GRID); } // Draw minimap ---------------------------------------------------------------- void draw_minimap() { glViewport(0, 0, WindowWidth/3, WindowHeight/3); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-250.0, 250.0, -250.0, 250.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_LIGHTING); glShadeModel(GL_FLAT); glPushMatrix(); { glTranslatef(0.0, 0.0, -0.1); glRotatef(90.0, 1.0, 0.0, 0.0); glCallList(DL_GRID); } glPopMatrix(); glPushMatrix(); { glTranslatef(WagonX, -WagonZ, 0.0); glColor3f(1.0, 0.0, 0.0); glScalef(25.0, 25.0, 1.0); glCallList(DL_BOX); } glPopMatrix(); } // Draw text ------------------------------------------------------------------- void draw_text() { char buffer[1024]; glViewport(0, 0, WindowWidth, WindowHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, WindowWidth, 0.0, WindowHeight); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_LIGHTING); glShadeModel(GL_SMOOTH); glTranslatef(WindowWidth*0.4, 10.0, 0.0); glScalef(0.1, 0.1, 1.0); glColor3f(1.0, 1.0, 0.0); snprintf(buffer, 1024, "x: %0.2lf, z: %0.2lf, ort: %0.2lf, speed: %0.2lf", WagonX, WagonZ, WagonOrientation, WagonSpeed); for (char *c = buffer; *c; c++) glutStrokeCharacter(GLUT_STROKE_ROMAN, *c); } // Display callback ------------------------------------------------------------ void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw_scene(); draw_minimap(); draw_text(); glutSwapBuffers(); } // Reshape callback ------------------------------------------------------------ void reshape(GLsizei nw, GLsizei nh) { WindowWidth = nw; WindowHeight = nh; init_scene(); } // Keyboard callback ----------------------------------------------------------- void keyboard(unsigned char key, int x, int y) { switch (key) { case 'q': case 'Q': exit(EXIT_SUCCESS); break; case 'w': WireFrameMode = !WireFrameMode; break; case ' ': WagonSpeed = 0.0; break; case '0': CameraMode = CAMERA_ORBIT; WagonSpeed = 0; WagonOrientation = 0.0; WagonX = 0.0; WagonZ = 0.0; JennyCubOrientation = 0.0; break; case '1': CameraMode = CAMERA_FIRST_PERSON; break; case '2': CameraMode = CAMERA_BIRDS_EYE; break; case '3': CameraMode = CAMERA_THIRD_PERSON; break; case '4': CameraMode = CAMERA_ORBIT; break; } update_camera_location(); } // Special callback ----------------------------------------------------------- void special(int key, int x, int y) { switch (key) { case GLUT_KEY_LEFT: WagonOrientation += 10.0; break; case GLUT_KEY_RIGHT: WagonOrientation -= 10.0; break; case GLUT_KEY_UP: WagonSpeed += 0.1; break; case GLUT_KEY_DOWN: WagonSpeed -= 0.1; break; } update_camera_location(); } // Mouse callback -------------------------------------------------------------- void mouse(int button, int state, int x, int y) { MouseX = x; MouseY = y; switch (button) { case WHEEL_UP: CameraDistance = (CameraDistance > CAMERA_DISTANCE_MIN ? CameraDistance - 1.0 : CAMERA_DISTANCE_MIN); break; case WHEEL_DOWN: CameraDistance = (CameraDistance < CAMERA_DISTANCE_MAX ? CameraDistance + 1.0 : CAMERA_DISTANCE_MAX); break; } update_camera_location(); } // Motion callback ------------------------------------------------------------- void motion(int x, int y) { CameraLatitude += 180.0*(double)(x - MouseX)/WindowWidth; CameraLongitude += 180.0*(double)(y - MouseY)/WindowHeight; if (CameraLongitude < -90.0) CameraLongitude = -90.0; if (CameraLongitude > 90.0) CameraLongitude = 90.0; MouseX = x; MouseY = y; update_camera_location(); } // Timer callback -------------------------------------------------------------- void timer(int value) { WagonX += WagonSpeed * cos(WagonOrientation*M_PI/180.0); WagonZ -= WagonSpeed * sin(WagonOrientation*M_PI/180.0); if (fabs(WagonSpeed) > 0.01) { if (JennyCubOrientation > 90.0) JennyCubOrientationDelta = -fabs(WagonSpeed)*10.0; if (JennyCubOrientation < -90.0) JennyCubOrientationDelta = fabs(WagonSpeed)*10.0; JennyCubOrientation += JennyCubOrientationDelta; } update_camera_location(); glutTimerFunc(100, timer, 0); } // Main execution -------------------------------------------------------------- int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(WindowWidth, WindowHeight); glutCreateWindow("wagon"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutSpecialFunc(special); glutMouseFunc(mouse); glutTimerFunc(100, timer, 0); glutMotionFunc(motion); init_scene(); make_display_lists(); glutMainLoop(); return (EXIT_SUCCESS); } // vim: sts=4 sw=4 ts=8 ft=cpp