//======================================================================== // This is an example program for the GLFW library // // The program uses a "split window" view, rendering four views of the // same scene in one window (e.g. uesful for 3D modelling software). This // demo uses scissors to separete the four different rendering areas from // each other. // // (If the code seems a little bit strange here and there, it may be // because I am not a friend of orthogonal projections) //======================================================================== #define GLFW_INCLUDE_GLU #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //======================================================================== // Global variables //======================================================================== // Mouse position static int xpos = 0, ypos = 0; // Window size static int width, height; // Active view: 0 = none, 1 = upper left, 2 = upper right, 3 = lower left, // 4 = lower right static int active_view = 0; // Rotation around each axis static int rot_x = 0, rot_y = 0, rot_z = 0; // Do redraw? static int do_redraw = 1; // Keep running? static GLboolean running = GL_TRUE; //======================================================================== // Draw a solid torus (use a display list for the model) //======================================================================== #define TORUS_MAJOR 1.5 #define TORUS_MINOR 0.5 #define TORUS_MAJOR_RES 32 #define TORUS_MINOR_RES 32 static void drawTorus(void) { static GLuint torus_list = 0; int i, j, k; double s, t, x, y, z, nx, ny, nz, scale, twopi; if (!torus_list) { // Start recording displaylist torus_list = glGenLists(1); glNewList(torus_list, GL_COMPILE_AND_EXECUTE); // Draw torus twopi = 2.0 * M_PI; for (i = 0; i < TORUS_MINOR_RES; i++) { glBegin(GL_QUAD_STRIP); for (j = 0; j <= TORUS_MAJOR_RES; j++) { for (k = 1; k >= 0; k--) { s = (i + k) % TORUS_MINOR_RES + 0.5; t = j % TORUS_MAJOR_RES; // Calculate point on surface x = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * cos(t * twopi / TORUS_MAJOR_RES); y = TORUS_MINOR * sin(s * twopi / TORUS_MINOR_RES); z = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * sin(t * twopi / TORUS_MAJOR_RES); // Calculate surface normal nx = x - TORUS_MAJOR * cos(t * twopi / TORUS_MAJOR_RES); ny = y; nz = z - TORUS_MAJOR * sin(t * twopi / TORUS_MAJOR_RES); scale = 1.0 / sqrt(nx*nx + ny*ny + nz*nz); nx *= scale; ny *= scale; nz *= scale; glNormal3f((float) nx, (float) ny, (float) nz); glVertex3f((float) x, (float) y, (float) z); } } glEnd(); } // Stop recording displaylist glEndList(); } else { // Playback displaylist glCallList(torus_list); } } //======================================================================== // Draw the scene (a rotating torus) //======================================================================== static void drawScene(void) { const GLfloat model_diffuse[4] = {1.0f, 0.8f, 0.8f, 1.0f}; const GLfloat model_specular[4] = {0.6f, 0.6f, 0.6f, 1.0f}; const GLfloat model_shininess = 20.0f; glPushMatrix(); // Rotate the object glRotatef((GLfloat) rot_x * 0.5f, 1.0f, 0.0f, 0.0f); glRotatef((GLfloat) rot_y * 0.5f, 0.0f, 1.0f, 0.0f); glRotatef((GLfloat) rot_z * 0.5f, 0.0f, 0.0f, 1.0f); // Set model color (used for orthogonal views, lighting disabled) glColor4fv(model_diffuse); // Set model material (used for perspective view, lighting enabled) glMaterialfv(GL_FRONT, GL_DIFFUSE, model_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, model_specular); glMaterialf(GL_FRONT, GL_SHININESS, model_shininess); // Draw torus drawTorus(); glPopMatrix(); } //======================================================================== // Draw a 2D grid (used for orthogonal views) //======================================================================== static void drawGrid(float scale, int steps) { int i; float x, y; glPushMatrix(); // Set background to some dark bluish grey glClearColor(0.05f, 0.05f, 0.2f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); // Setup modelview matrix (flat XY view) glLoadIdentity(); gluLookAt(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // We don't want to update the Z-buffer glDepthMask(GL_FALSE); // Set grid color glColor3f(0.0f, 0.5f, 0.5f); glBegin(GL_LINES); // Horizontal lines x = scale * 0.5f * (float) (steps - 1); y = -scale * 0.5f * (float) (steps - 1); for (i = 0; i < steps; i++) { glVertex3f(-x, y, 0.0f); glVertex3f(x, y, 0.0f); y += scale; } // Vertical lines x = -scale * 0.5f * (float) (steps - 1); y = scale * 0.5f * (float) (steps - 1); for (i = 0; i < steps; i++) { glVertex3f(x, -y, 0.0f); glVertex3f(x, y, 0.0f); x += scale; } glEnd(); // Enable Z-buffer writing again glDepthMask(GL_TRUE); glPopMatrix(); } //======================================================================== // Draw all views //======================================================================== static void drawAllViews(void) { const GLfloat light_position[4] = {0.0f, 8.0f, 8.0f, 1.0f}; const GLfloat light_diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; const GLfloat light_specular[4] = {1.0f, 1.0f, 1.0f, 1.0f}; const GLfloat light_ambient[4] = {0.2f, 0.2f, 0.3f, 1.0f}; double aspect; // Calculate aspect of window if (height > 0) aspect = (double) width / (double) height; else aspect = 1.0; // Clear screen glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Enable scissor test glEnable(GL_SCISSOR_TEST); // Enable depth test glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); // ** ORTHOGONAL VIEWS ** // For orthogonal views, use wireframe rendering glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Enable line anti-aliasing glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Setup orthogonal projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-3.0 * aspect, 3.0 * aspect, -3.0, 3.0, 1.0, 50.0); // Upper left view (TOP VIEW) glViewport(0, height / 2, width / 2, height / 2); glScissor(0, height / 2, width / 2, height / 2); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0f, 10.0f, 1e-3f, // Eye-position (above) 0.0f, 0.0f, 0.0f, // View-point 0.0f, 1.0f, 0.0f); // Up-vector drawGrid(0.5, 12); drawScene(); // Lower left view (FRONT VIEW) glViewport(0, 0, width / 2, height / 2); glScissor(0, 0, width / 2, height / 2); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0f, 0.0f, 10.0f, // Eye-position (in front of) 0.0f, 0.0f, 0.0f, // View-point 0.0f, 1.0f, 0.0f); // Up-vector drawGrid(0.5, 12); drawScene(); // Lower right view (SIDE VIEW) glViewport(width / 2, 0, width / 2, height / 2); glScissor(width / 2, 0, width / 2, height / 2); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(10.0f, 0.0f, 0.0f, // Eye-position (to the right) 0.0f, 0.0f, 0.0f, // View-point 0.0f, 1.0f, 0.0f); // Up-vector drawGrid(0.5, 12); drawScene(); // Disable line anti-aliasing glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); // ** PERSPECTIVE VIEW ** // For perspective view, use solid rendering glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Enable face culling (faster rendering) glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CW); // Setup perspective projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(65.0f, aspect, 1.0f, 50.0f); // Upper right view (PERSPECTIVE VIEW) glViewport(width / 2, height / 2, width / 2, height / 2); glScissor(width / 2, height / 2, width / 2, height / 2); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(3.0f, 1.5f, 3.0f, // Eye-position 0.0f, 0.0f, 0.0f, // View-point 0.0f, 1.0f, 0.0f); // Up-vector // Configure and enable light source 1 glLightfv(GL_LIGHT1, GL_POSITION, light_position); glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular); glEnable(GL_LIGHT1); glEnable(GL_LIGHTING); // Draw scene drawScene(); // Disable lighting glDisable(GL_LIGHTING); // Disable face culling glDisable(GL_CULL_FACE); // Disable depth test glDisable(GL_DEPTH_TEST); // Disable scissor test glDisable(GL_SCISSOR_TEST); // Draw a border around the active view if (active_view > 0 && active_view != 2) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, 2.0, 0.0, 2.0, 0.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef((GLfloat) ((active_view - 1) & 1), (GLfloat) (1 - (active_view - 1) / 2), 0.0f); glColor3f(1.0f, 1.0f, 0.6f); glBegin(GL_LINE_STRIP); glVertex2i(0, 0); glVertex2i(1, 0); glVertex2i(1, 1); glVertex2i(0, 1); glVertex2i(0, 0); glEnd(); } } //======================================================================== // Window size callback function //======================================================================== static void windowSizeFun(GLFWwindow window, int w, int h) { width = w; height = h > 0 ? h : 1; do_redraw = 1; } //======================================================================== // Window refresh callback function //======================================================================== static void windowRefreshFun(GLFWwindow window) { do_redraw = 1; } //======================================================================== // Mouse position callback function //======================================================================== static void cursorPosFun(GLFWwindow window, int x, int y) { // Depending on which view was selected, rotate around different axes switch (active_view) { case 1: rot_x += y - ypos; rot_z += x - xpos; do_redraw = 1; break; case 3: rot_x += y - ypos; rot_y += x - xpos; do_redraw = 1; break; case 4: rot_y += x - xpos; rot_z += y - ypos; do_redraw = 1; break; default: // Do nothing for perspective view, or if no view is selected break; } // Remember cursor position xpos = x; ypos = y; } //======================================================================== // Mouse button callback function //======================================================================== static void mouseButtonFun(GLFWwindow window, int button, int action) { if ((button == GLFW_MOUSE_BUTTON_LEFT) && action == GLFW_PRESS) { // Detect which of the four views was clicked active_view = 1; if (xpos >= width / 2) active_view += 1; if (ypos >= height / 2) active_view += 2; } else if (button == GLFW_MOUSE_BUTTON_LEFT) { // Deselect any previously selected view active_view = 0; } do_redraw = 1; } //======================================================================== // Window close callback function //======================================================================== static int windowCloseFun(GLFWwindow window) { running = GL_FALSE; return GL_TRUE; } //======================================================================== // main //======================================================================== int main(void) { GLFWwindow window; // Initialise GLFW if (!glfwInit()) { fprintf(stderr, "Failed to initialize GLFW\n"); exit(EXIT_FAILURE); } glfwWindowHint(GLFW_DEPTH_BITS, 16); // Open OpenGL window window = glfwCreateWindow(500, 500, GLFW_WINDOWED, "Split view demo", NULL); if (!window) { fprintf(stderr, "Failed to open GLFW window\n"); exit(EXIT_FAILURE); } // Enable vsync glfwSwapInterval(1); // Enable sticky keys glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); // Enable mouse cursor (only needed for fullscreen mode) glfwSetInputMode(window, GLFW_CURSOR_MODE, GLFW_CURSOR_NORMAL); // Set callback functions glfwSetWindowCloseCallback(windowCloseFun); glfwSetWindowSizeCallback(windowSizeFun); glfwSetWindowRefreshCallback(windowRefreshFun); glfwSetCursorPosCallback(cursorPosFun); glfwSetMouseButtonCallback(mouseButtonFun); // Main loop do { // Only redraw if we need to if (do_redraw) { // Draw all views drawAllViews(); // Swap buffers glfwSwapBuffers(window); do_redraw = 0; } // Wait for new events glfwWaitEvents(); if (glfwGetKey(window, GLFW_KEY_ESCAPE)) running = GL_FALSE; } // Check if the ESC key was pressed or the window was closed while (running); // Close OpenGL window and terminate GLFW glfwTerminate(); exit(EXIT_SUCCESS); }