// Peter Bui // CSE 40166 Computer Graphics (Fall 2010) // Lab 5: bezier #include #include #include #include #ifdef __APPLE__ #include #else #include #endif using namespace std; // Point data structure -------------------------------------------------------- typedef struct { double x; double y; double z; } Point; // Constants ------------------------------------------------------------------- #define WHEEL_UP 3 #define WHEEL_DOWN 4 #define MAX_ROWS 100 #define MAX_COLS 100 #define CAMERA_DISTANCE_MIN 1.0 #define CAMERA_DISTANCE_MAX 100.0 // TODO SELECTION: define Select Buffer Size Constant enum { DL_BOX = 1, DL_GRID, }; // Global variables ------------------------------------------------------------ static size_t WindowWidth = 640; static size_t WindowHeight = 480; static GLint MouseX = 0; static GLint MouseY = 0; static GLint MouseButton; static GLint MouseState; static double CameraLatitude = 45.0; static double CameraLongitude = 25.0; static double CameraDistance = 50.0; static bool CameraOrthographic = false; 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; // TODO SELECTION: define global Select Buffer static int FrameRate = 60; static Point CameraFocus = { MAX_ROWS / 2, 0.0, MAX_COLS / 2 }; static Point SelectedBox = { MAX_ROWS / 2, 0.0, MAX_COLS / 2 }; static bool ShowGrid = true; static vector Controls; static GLint ControlIndex = -1; // Update camera --------------------------------------------------------------- void update_camera_location() { double L = CameraDistance * cos(M_PI*CameraLongitude/180.0); double X = MAX_ROWS / 2; double Z = MAX_COLS / 2; EyeX = X + L * -sin(M_PI*CameraLatitude/180.0); EyeY = CameraDistance * sin(M_PI*CameraLongitude/180.0); EyeZ = Z + L * cos(M_PI*CameraLatitude/180.0); DirX = X; DirY = 0.0; DirZ = Z; 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); glShadeModel(GL_SMOOTH); update_camera_location(); } // Projection ------------------------------------------------------------------ void setup_projection(bool reset = true) { // TODO PROJECTION: // Make sure you are modifying the GL_PROJECTION matrix. // If reset is true, then load identity matrix. // If CameraOrthographic is true, then setup projection using glOrtho // otherwise, setup projection using gluPerspective. } // Reshape callback ------------------------------------------------------------ void reshape(GLsizei nw, GLsizei nh) { WindowWidth = nw; WindowHeight = nh; glViewport(0, 0, WindowWidth, WindowHeight); setup_projection(); } // Draw box -------------------------------------------------------------------- void draw_box() { glBegin(GL_QUADS); { glVertex3f(-0.5, 0.0, -0.5); glVertex3f( 0.5, 0.0, -0.5); glVertex3f( 0.5, 0.0, 0.5); glVertex3f(-0.5, 0.0, 0.5); } glEnd(); } // Draw grid ------------------------------------------------------------------- void draw_grid() { glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_LIGHTING); // TODO SELECTION: // 1. For each row, push the row as a Name // 2. For each column, push the column as a Name // 3. Remember to pop the names for (int r = 0; r < MAX_ROWS; r++) { for (int c = 0; c < MAX_COLS; c++) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glPushMatrix(); { glTranslatef(r, 0, c); glCallList(DL_BOX); } glPopMatrix(); } } glPopAttrib(); } // Make display lists ---------------------------------------------------------- void make_display_lists() { glNewList(DL_BOX, GL_COMPILE); { draw_box(); }; glEndList(); glNewList(DL_GRID, GL_COMPILE); { draw_grid(); }; glEndList(); } // Draw selected box ----------------------------------------------------------- void draw_selected_box() { glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_LIGHTING); glColor3f(0.0, 1.0, 0.0); // TODO SELECTION: // Push the x and z coordinates of the box as names. Remember to pop them. glPushMatrix(); { glTranslatef(SelectedBox.x, SelectedBox.y, SelectedBox.z); glCallList(DL_BOX); } glPopMatrix(); glPopAttrib(); } // Draw points ----------------------------------------------------------------- void draw_points() { // TODO SELECTION: // For each control point, push the x, z, and index (i) of the point as // names. Remember to pop them. for (int i = 0; i < (int)Controls.size(); i++) { Point p = Controls[i]; glPushMatrix(); { if (ControlIndex == i) glColor3f(0.0, 1.0, 0.0); else glColor3f(1.0, 0.0, 0.0); glTranslatef(p.x, p.y, p.z); glutSolidSphere(0.5, 24, 24); } glPopMatrix(); glPushAttrib(GL_LIGHTING_BIT); { glDisable(GL_LIGHTING); glColor3f(1.0, 1.0, 1.0); glEnable(GL_LINE_STIPPLE); // Note: you can create dotted or dashed lines by using glLineStipple(1, 0x0101); // GL_LINE_STIPPLE and by specifying a pattern. glBegin(GL_LINES); { glVertex3f(p.x, 0.0, p.z); glVertex3f(p.x, p.y, p.z); }; glEnd(); glDisable(GL_LINE_STIPPLE); } glPopAttrib(); } glPushAttrib(GL_LIGHTING_BIT); { glDisable(GL_LIGHTING); glColor3f(1.0, 1.0, 1.0); glEnable(GL_LINE_STIPPLE); glLineStipple(1, 0xAAAA); glBegin(GL_LINE_STRIP); { for (size_t i = 0; i < Controls.size(); i++) { Point p = Controls[i]; glVertex3f(p.x, p.y, p.z); } } glEnd(); glDisable(GL_LINE_STIPPLE); } glPopAttrib(); } // Draw beziers ----------------------------------------------------------------- void draw_beziers() { glPushAttrib(GL_LIGHTING_BIT | GL_LINE_BIT); { glDisable(GL_LIGHTING); glColor3f(1.0, 1.0, 0.0); glLineWidth(2.0); glBegin(GL_LINE_STRIP); { // TODO BEZIER: For every consecutive series of 4 points, draw a // bezier curve using the cubic formula: // // B(t) = p0*t^3 + 3*p1*t^2(1-t) + 3*p2*t*(1-t)^2 + p3*(1-t)^3 // // where B(t) is the new vertex, 0 <= t <= 1.0, p0, p1, p2, p3 are // the four points in consecutive order. // // Note: p3 of the first four points, becomes p0 of the next four // points. // // See the following for more information: // http://www.gamedev.net/reference/articles/article1808.asp }; glEnd(); } glPopAttrib(); } // Draw scene ------------------------------------------------------------------ void draw_scene() { if (CameraOrthographic) { // TODO PROJECTION: // Setup Orthographic camera by: // 1. Rotating the XZ plane to become the XY plane. // 2. Zooming our lens by scaling using d/10.0 (where d = // CAMERA_DISTANCE_MAX - CameraDistance). // 3. Centering our view by translating based on the location of our // CameraFocus. } else { // TODO PROJECTION: // Setup perspective projection using gluLookAt. } if (ShowGrid) glColor3f(0.3, 0.3, 0.3); else glColor3f(0.0, 0.0, 0.0); glCallList(DL_GRID); draw_selected_box(); draw_points(); draw_beziers(); glFlush(); } // Display callback ------------------------------------------------------------ void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); setup_projection(); draw_scene(); glutSwapBuffers(); } // Keyboard callback ----------------------------------------------------------- void keyboard(unsigned char key, int x, int y) { switch (key) { case 'q': case 'Q': exit(EXIT_SUCCESS); break; case 'o': CameraOrthographic = !CameraOrthographic; CameraFocus.x = SelectedBox.x; CameraFocus.z = SelectedBox.z; if (!CameraOrthographic) ControlIndex = -1; break; case '-': if (ControlIndex >= 0) Controls[ControlIndex].y--; break; case '=': if (ControlIndex >= 0) Controls[ControlIndex].y++; break; case 'g': ShowGrid = !ShowGrid; break; } update_camera_location(); } // Special callback ----------------------------------------------------------- void special(int key, int x, int y) { switch (key) { case GLUT_KEY_LEFT: if (ControlIndex >= 0) { Controls[ControlIndex].x--; SelectedBox.x--; } break; case GLUT_KEY_RIGHT: if (ControlIndex >= 0) { Controls[ControlIndex].x++; SelectedBox.x++; } break; case GLUT_KEY_UP: if (ControlIndex >= 0) { Controls[ControlIndex].z--; SelectedBox.z--; } break; case GLUT_KEY_DOWN: if (ControlIndex >= 0) { Controls[ControlIndex].z++; SelectedBox.z++; } break; } update_camera_location(); glutPostRedisplay(); } // Picking --------------------------------------------------------------------- void start_picking() { // TODO SELECTION: // Start picking by: // 1. Grab viewport array. // 2. Initialize select buffer, switch to selection rendering mode, and // initialize name stack. // 3. Setup refined projection using gluPickMatrix. Be sure to also setup // normal projection (set reset parameter to false). Also, remember to // store the original projection. } void process_hits(GLint hits, GLuint *buffer) { GLuint names; GLuint *bp = buffer; for (int i = 0; i < hits; i++) { names = *bp; bp += 3; // TODO SELECTION: // Set SelectedBox x and z. If we have 3 names, then the last argument // is the new ControlIndex and we can stop processing. Otherwise, set // ControlIndex to -1 and continue processing hits. bp += names; } } void stop_picking() { // TODO SELECTION: // Reset original projection matrix, switch to normal rendering mode and // process hits. } // Mouse callback -------------------------------------------------------------- void mouse(int button, int state, int x, int y) { MouseX = x; MouseY = y; MouseButton = button; MouseState = state; switch (button) { case GLUT_LEFT_BUTTON: case GLUT_MIDDLE_BUTTON: case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN) { start_picking(); draw_scene(); stop_picking(); } else { if (button == GLUT_LEFT_BUTTON) { if (ControlIndex < 0 && SelectedBox.x >= 0 && SelectedBox.y >= 0) { Point p = { SelectedBox.x, SelectedBox.y + 1.0, SelectedBox.z }; if (Controls.size() > 0) p.y = Controls.back().y; Controls.push_back(p); ControlIndex = Controls.size() - 1; } } else if (button == GLUT_MIDDLE_BUTTON) { if (ControlIndex >= 0) { Controls.erase(Controls.begin() + ControlIndex); ControlIndex = -1; SelectedBox.x = -1; SelectedBox.z = -1; } } else { ControlIndex = -1; if (CameraOrthographic) { CameraFocus.x = SelectedBox.x; CameraFocus.z = SelectedBox.z; } } } break; case WHEEL_UP: if (ControlIndex >= 0 && !CameraOrthographic) Controls[ControlIndex].y += 0.1; else CameraDistance = (CameraDistance > CAMERA_DISTANCE_MIN ? CameraDistance - 1.0 : CAMERA_DISTANCE_MIN); break; case WHEEL_DOWN: if (ControlIndex >= 0 && !CameraOrthographic) Controls[ControlIndex].y -= 0.1; else CameraDistance = (CameraDistance < CAMERA_DISTANCE_MAX ? CameraDistance + 1.0 : CAMERA_DISTANCE_MAX); break; } update_camera_location(); } // Motion callback ------------------------------------------------------------- void motion(int x, int y) { double dx = (double)(x - MouseX)/(double)(WindowWidth); double dy = (double)(y - MouseY)/(double)(WindowHeight); if (MouseButton == GLUT_RIGHT_BUTTON) { CameraLatitude += 180.0*dx; CameraLongitude += 180.0*dy; if (CameraLongitude < -90.0) CameraLongitude = -90.0 + 0.0001; if (CameraLongitude > 90.0) CameraLongitude = 90.0 - 0.0001; update_camera_location(); } if (MouseButton == GLUT_LEFT_BUTTON && ControlIndex >= 0 && CameraOrthographic) { // TODO SELECTION: // Use gluUnProjection to map window coordinates to object coordinates. // With the coordinates, you can update the current Control point's // position (x, z). Be sure to round the object coordinates. } MouseX = x; MouseY = y; glutPostRedisplay(); } // Timer callback -------------------------------------------------------------- void timer(int value) { glutPostRedisplay(); glutTimerFunc(1000/FrameRate, timer, 0); } // Main execution -------------------------------------------------------------- int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(WindowWidth, WindowHeight); glutCreateWindow("bezier"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutSpecialFunc(special); glutMouseFunc(mouse); glutMotionFunc(motion); glutTimerFunc(1000/FrameRate, timer, 0); init_scene(); make_display_lists(); glutMainLoop(); return (EXIT_SUCCESS); } // vim: set sts=4 sw=4 ts=8 ft=cpp: --------------------------------------------