// Peter Bui // CSE 40166 Computer Graphics (Fall 2010) // Example 16: 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 #define SBSIZE 256 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; static GLuint SelectBuffer[SBSIZE]; 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) { glMatrixMode(GL_PROJECTION); if (reset) glLoadIdentity(); if (CameraOrthographic) glOrtho(-MAX_ROWS, MAX_ROWS, -MAX_ROWS, MAX_ROWS, -MAX_COLS, MAX_COLS); else gluPerspective(45.0, (GLdouble)(WindowWidth)/(GLdouble)(WindowHeight), 0.1, 1000.0); } // 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); for (int r = 0; r < MAX_ROWS; r++) { glPushName(r); for (int c = 0; c < MAX_COLS; c++) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glPushName(c); glPushMatrix(); { glTranslatef(r, 0, c); glCallList(DL_BOX); } glPopMatrix(); glPopName(); } glPopName(); } 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); glPushMatrix(); { glPushName((int)SelectedBox.x); glPushName((int)SelectedBox.z); glTranslatef(SelectedBox.x, SelectedBox.y, SelectedBox.z); glCallList(DL_BOX); glPopName(); glPopName(); } glPopMatrix(); glPopAttrib(); } // Draw points ----------------------------------------------------------------- void draw_points() { for (int i = 0; i < (int)Controls.size(); i++) { Point p = Controls[i]; glPushName((int)p.x); glPushName((int)p.z); glPushName(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); glLineStipple(1, 0x0101); glBegin(GL_LINES); { glVertex3f(p.x, 0.0, p.z); glVertex3f(p.x, p.y, p.z); }; glEnd(); glDisable(GL_LINE_STIPPLE); } glPopAttrib(); glPopName(); glPopName(); glPopName(); } 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); { for (size_t i = 0; (Controls.size() - i) > 3; i += 3) { Point p0 = Controls[i + 0]; Point p1 = Controls[i + 1]; Point p2 = Controls[i + 2]; Point p3 = Controls[i + 3]; for (double a = 1.0; a > 0.0; a -= 0.05) { double b = 1.0 - a; double x = p0.x*a*a*a + p1.x*3*a*a*b + p2.x*3*a*b*b + p3.x*b*b*b; double y = p0.y*a*a*a + p1.y*3*a*a*b + p2.y*3*a*b*b + p3.y*b*b*b; double z = p0.z*a*a*a + p1.z*3*a*a*b + p2.z*3*a*b*b + p3.z*b*b*b; glVertex3f(x, y, z); } } }; glEnd(); } glPopAttrib(); } // Draw scene ------------------------------------------------------------------ void draw_scene() { if (CameraOrthographic) { double d = CAMERA_DISTANCE_MAX - CameraDistance; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(90, 1.0, 0.0, 0.0); glScalef(d/10.0, d/10.0, d/10.0); glTranslatef(-CameraFocus.x, 0.0, -CameraFocus.z); } else { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(EyeX, EyeY, EyeZ, DirX, DirY, DirZ, 0.0, 1.0, 0.0); } 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() { GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); glSelectBuffer(SBSIZE, SelectBuffer); glRenderMode(GL_SELECT); glInitNames(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPickMatrix(MouseX, viewport[3] - MouseY, 5, 5, viewport); setup_projection(false); glMatrixMode(GL_MODELVIEW); } void process_hits(GLint hits, GLuint *buffer) { GLuint names; GLuint *bp = buffer; for (int i = 0; i < hits; i++) { names = *bp; bp += 3; if (names < 2) goto ph_continue; SelectedBox.x = *(bp); SelectedBox.z = *(bp + 1); if (names == 3) { ControlIndex = *(bp + 2); break; } else { ControlIndex = -1; } ph_continue: bp += names; } } void stop_picking() { GLint hits; glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); hits = glRenderMode(GL_RENDER); if (hits > 0) process_hits(hits, SelectBuffer); } // 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) { GLdouble modelview[16]; GLdouble projection[16]; GLint viewport[4]; double wx, wy, wz, ox, oy, oz; glGetDoublev(GL_MODELVIEW_MATRIX, modelview); glGetDoublev(GL_PROJECTION_MATRIX, projection); glGetIntegerv(GL_VIEWPORT, viewport); wx = MouseX; wy = viewport[3] - MouseY - 1; glReadPixels((int)wx, (int)wy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &wz); gluUnProject(wx, wy, wz, modelview, projection, viewport, &ox, &oy, &oz); Controls[ControlIndex].x = SelectedBox.x = round(ox); Controls[ControlIndex].z = SelectedBox.z = round(oz); } 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: --------------------------------------------