1 //======================================================================== 2 // This is an example program for the GLFW library 3 // 4 // The program uses a "split window" view, rendering four views of the 5 // same scene in one window (e.g. uesful for 3D modelling software). This 6 // demo uses scissors to separete the four different rendering areas from 7 // each other. 8 // 9 // (If the code seems a little bit strange here and there, it may be 10 // because I am not a friend of orthogonal projections) 11 //======================================================================== 12 13 #include <glad/glad.h> 14 #include <GLFW/glfw3.h> 15 16 #if defined(_MSC_VER) 17 // Make MS math.h define M_PI 18 #define _USE_MATH_DEFINES 19 #endif 20 21 #include <math.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 25 #include <linmath.h> 26 27 28 //======================================================================== 29 // Global variables 30 //======================================================================== 31 32 // Mouse position 33 static double xpos = 0, ypos = 0; 34 35 // Window size 36 static int width, height; 37 38 // Active view: 0 = none, 1 = upper left, 2 = upper right, 3 = lower left, 39 // 4 = lower right 40 static int active_view = 0; 41 42 // Rotation around each axis 43 static int rot_x = 0, rot_y = 0, rot_z = 0; 44 45 // Do redraw? 46 static int do_redraw = 1; 47 48 49 //======================================================================== 50 // Draw a solid torus (use a display list for the model) 51 //======================================================================== 52 53 #define TORUS_MAJOR 1.5 54 #define TORUS_MINOR 0.5 55 #define TORUS_MAJOR_RES 32 56 #define TORUS_MINOR_RES 32 57 drawTorus(void)58 static void drawTorus(void) 59 { 60 static GLuint torus_list = 0; 61 int i, j, k; 62 double s, t, x, y, z, nx, ny, nz, scale, twopi; 63 64 if (!torus_list) 65 { 66 // Start recording displaylist 67 torus_list = glGenLists(1); 68 glNewList(torus_list, GL_COMPILE_AND_EXECUTE); 69 70 // Draw torus 71 twopi = 2.0 * M_PI; 72 for (i = 0; i < TORUS_MINOR_RES; i++) 73 { 74 glBegin(GL_QUAD_STRIP); 75 for (j = 0; j <= TORUS_MAJOR_RES; j++) 76 { 77 for (k = 1; k >= 0; k--) 78 { 79 s = (i + k) % TORUS_MINOR_RES + 0.5; 80 t = j % TORUS_MAJOR_RES; 81 82 // Calculate point on surface 83 x = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * cos(t * twopi / TORUS_MAJOR_RES); 84 y = TORUS_MINOR * sin(s * twopi / TORUS_MINOR_RES); 85 z = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * sin(t * twopi / TORUS_MAJOR_RES); 86 87 // Calculate surface normal 88 nx = x - TORUS_MAJOR * cos(t * twopi / TORUS_MAJOR_RES); 89 ny = y; 90 nz = z - TORUS_MAJOR * sin(t * twopi / TORUS_MAJOR_RES); 91 scale = 1.0 / sqrt(nx*nx + ny*ny + nz*nz); 92 nx *= scale; 93 ny *= scale; 94 nz *= scale; 95 96 glNormal3f((float) nx, (float) ny, (float) nz); 97 glVertex3f((float) x, (float) y, (float) z); 98 } 99 } 100 101 glEnd(); 102 } 103 104 // Stop recording displaylist 105 glEndList(); 106 } 107 else 108 { 109 // Playback displaylist 110 glCallList(torus_list); 111 } 112 } 113 114 115 //======================================================================== 116 // Draw the scene (a rotating torus) 117 //======================================================================== 118 drawScene(void)119 static void drawScene(void) 120 { 121 const GLfloat model_diffuse[4] = {1.0f, 0.8f, 0.8f, 1.0f}; 122 const GLfloat model_specular[4] = {0.6f, 0.6f, 0.6f, 1.0f}; 123 const GLfloat model_shininess = 20.0f; 124 125 glPushMatrix(); 126 127 // Rotate the object 128 glRotatef((GLfloat) rot_x * 0.5f, 1.0f, 0.0f, 0.0f); 129 glRotatef((GLfloat) rot_y * 0.5f, 0.0f, 1.0f, 0.0f); 130 glRotatef((GLfloat) rot_z * 0.5f, 0.0f, 0.0f, 1.0f); 131 132 // Set model color (used for orthogonal views, lighting disabled) 133 glColor4fv(model_diffuse); 134 135 // Set model material (used for perspective view, lighting enabled) 136 glMaterialfv(GL_FRONT, GL_DIFFUSE, model_diffuse); 137 glMaterialfv(GL_FRONT, GL_SPECULAR, model_specular); 138 glMaterialf(GL_FRONT, GL_SHININESS, model_shininess); 139 140 // Draw torus 141 drawTorus(); 142 143 glPopMatrix(); 144 } 145 146 147 //======================================================================== 148 // Draw a 2D grid (used for orthogonal views) 149 //======================================================================== 150 drawGrid(float scale,int steps)151 static void drawGrid(float scale, int steps) 152 { 153 int i; 154 float x, y; 155 mat4x4 view; 156 157 glPushMatrix(); 158 159 // Set background to some dark bluish grey 160 glClearColor(0.05f, 0.05f, 0.2f, 0.0f); 161 glClear(GL_COLOR_BUFFER_BIT); 162 163 // Setup modelview matrix (flat XY view) 164 { 165 vec3 eye = { 0.f, 0.f, 1.f }; 166 vec3 center = { 0.f, 0.f, 0.f }; 167 vec3 up = { 0.f, 1.f, 0.f }; 168 mat4x4_look_at(view, eye, center, up); 169 } 170 glLoadMatrixf((const GLfloat*) view); 171 172 // We don't want to update the Z-buffer 173 glDepthMask(GL_FALSE); 174 175 // Set grid color 176 glColor3f(0.0f, 0.5f, 0.5f); 177 178 glBegin(GL_LINES); 179 180 // Horizontal lines 181 x = scale * 0.5f * (float) (steps - 1); 182 y = -scale * 0.5f * (float) (steps - 1); 183 for (i = 0; i < steps; i++) 184 { 185 glVertex3f(-x, y, 0.0f); 186 glVertex3f(x, y, 0.0f); 187 y += scale; 188 } 189 190 // Vertical lines 191 x = -scale * 0.5f * (float) (steps - 1); 192 y = scale * 0.5f * (float) (steps - 1); 193 for (i = 0; i < steps; i++) 194 { 195 glVertex3f(x, -y, 0.0f); 196 glVertex3f(x, y, 0.0f); 197 x += scale; 198 } 199 200 glEnd(); 201 202 // Enable Z-buffer writing again 203 glDepthMask(GL_TRUE); 204 205 glPopMatrix(); 206 } 207 208 209 //======================================================================== 210 // Draw all views 211 //======================================================================== 212 drawAllViews(void)213 static void drawAllViews(void) 214 { 215 const GLfloat light_position[4] = {0.0f, 8.0f, 8.0f, 1.0f}; 216 const GLfloat light_diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; 217 const GLfloat light_specular[4] = {1.0f, 1.0f, 1.0f, 1.0f}; 218 const GLfloat light_ambient[4] = {0.2f, 0.2f, 0.3f, 1.0f}; 219 float aspect; 220 mat4x4 view, projection; 221 222 // Calculate aspect of window 223 if (height > 0) 224 aspect = (float) width / (float) height; 225 else 226 aspect = 1.f; 227 228 // Clear screen 229 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 230 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 231 232 // Enable scissor test 233 glEnable(GL_SCISSOR_TEST); 234 235 // Enable depth test 236 glEnable(GL_DEPTH_TEST); 237 glDepthFunc(GL_LEQUAL); 238 239 // ** ORTHOGONAL VIEWS ** 240 241 // For orthogonal views, use wireframe rendering 242 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 243 244 // Enable line anti-aliasing 245 glEnable(GL_LINE_SMOOTH); 246 glEnable(GL_BLEND); 247 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 248 249 // Setup orthogonal projection matrix 250 glMatrixMode(GL_PROJECTION); 251 glLoadIdentity(); 252 glOrtho(-3.0 * aspect, 3.0 * aspect, -3.0, 3.0, 1.0, 50.0); 253 254 // Upper left view (TOP VIEW) 255 glViewport(0, height / 2, width / 2, height / 2); 256 glScissor(0, height / 2, width / 2, height / 2); 257 glMatrixMode(GL_MODELVIEW); 258 { 259 vec3 eye = { 0.f, 10.f, 1e-3f }; 260 vec3 center = { 0.f, 0.f, 0.f }; 261 vec3 up = { 0.f, 1.f, 0.f }; 262 mat4x4_look_at( view, eye, center, up ); 263 } 264 glLoadMatrixf((const GLfloat*) view); 265 drawGrid(0.5, 12); 266 drawScene(); 267 268 // Lower left view (FRONT VIEW) 269 glViewport(0, 0, width / 2, height / 2); 270 glScissor(0, 0, width / 2, height / 2); 271 glMatrixMode(GL_MODELVIEW); 272 { 273 vec3 eye = { 0.f, 0.f, 10.f }; 274 vec3 center = { 0.f, 0.f, 0.f }; 275 vec3 up = { 0.f, 1.f, 0.f }; 276 mat4x4_look_at( view, eye, center, up ); 277 } 278 glLoadMatrixf((const GLfloat*) view); 279 drawGrid(0.5, 12); 280 drawScene(); 281 282 // Lower right view (SIDE VIEW) 283 glViewport(width / 2, 0, width / 2, height / 2); 284 glScissor(width / 2, 0, width / 2, height / 2); 285 glMatrixMode(GL_MODELVIEW); 286 { 287 vec3 eye = { 10.f, 0.f, 0.f }; 288 vec3 center = { 0.f, 0.f, 0.f }; 289 vec3 up = { 0.f, 1.f, 0.f }; 290 mat4x4_look_at( view, eye, center, up ); 291 } 292 glLoadMatrixf((const GLfloat*) view); 293 drawGrid(0.5, 12); 294 drawScene(); 295 296 // Disable line anti-aliasing 297 glDisable(GL_LINE_SMOOTH); 298 glDisable(GL_BLEND); 299 300 // ** PERSPECTIVE VIEW ** 301 302 // For perspective view, use solid rendering 303 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 304 305 // Enable face culling (faster rendering) 306 glEnable(GL_CULL_FACE); 307 glCullFace(GL_BACK); 308 glFrontFace(GL_CW); 309 310 // Setup perspective projection matrix 311 glMatrixMode(GL_PROJECTION); 312 mat4x4_perspective(projection, 313 65.f * (float) M_PI / 180.f, 314 aspect, 315 1.f, 50.f); 316 glLoadMatrixf((const GLfloat*) projection); 317 318 // Upper right view (PERSPECTIVE VIEW) 319 glViewport(width / 2, height / 2, width / 2, height / 2); 320 glScissor(width / 2, height / 2, width / 2, height / 2); 321 glMatrixMode(GL_MODELVIEW); 322 { 323 vec3 eye = { 3.f, 1.5f, 3.f }; 324 vec3 center = { 0.f, 0.f, 0.f }; 325 vec3 up = { 0.f, 1.f, 0.f }; 326 mat4x4_look_at( view, eye, center, up ); 327 } 328 glLoadMatrixf((const GLfloat*) view); 329 330 // Configure and enable light source 1 331 glLightfv(GL_LIGHT1, GL_POSITION, light_position); 332 glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient); 333 glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse); 334 glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular); 335 glEnable(GL_LIGHT1); 336 glEnable(GL_LIGHTING); 337 338 // Draw scene 339 drawScene(); 340 341 // Disable lighting 342 glDisable(GL_LIGHTING); 343 344 // Disable face culling 345 glDisable(GL_CULL_FACE); 346 347 // Disable depth test 348 glDisable(GL_DEPTH_TEST); 349 350 // Disable scissor test 351 glDisable(GL_SCISSOR_TEST); 352 353 // Draw a border around the active view 354 if (active_view > 0 && active_view != 2) 355 { 356 glViewport(0, 0, width, height); 357 358 glMatrixMode(GL_PROJECTION); 359 glLoadIdentity(); 360 glOrtho(0.0, 2.0, 0.0, 2.0, 0.0, 1.0); 361 362 glMatrixMode(GL_MODELVIEW); 363 glLoadIdentity(); 364 glTranslatef((GLfloat) ((active_view - 1) & 1), (GLfloat) (1 - (active_view - 1) / 2), 0.0f); 365 366 glColor3f(1.0f, 1.0f, 0.6f); 367 368 glBegin(GL_LINE_STRIP); 369 glVertex2i(0, 0); 370 glVertex2i(1, 0); 371 glVertex2i(1, 1); 372 glVertex2i(0, 1); 373 glVertex2i(0, 0); 374 glEnd(); 375 } 376 } 377 378 379 //======================================================================== 380 // Framebuffer size callback function 381 //======================================================================== 382 framebufferSizeFun(GLFWwindow * window,int w,int h)383 static void framebufferSizeFun(GLFWwindow* window, int w, int h) 384 { 385 width = w; 386 height = h > 0 ? h : 1; 387 do_redraw = 1; 388 } 389 390 391 //======================================================================== 392 // Window refresh callback function 393 //======================================================================== 394 windowRefreshFun(GLFWwindow * window)395 static void windowRefreshFun(GLFWwindow* window) 396 { 397 drawAllViews(); 398 glfwSwapBuffers(window); 399 do_redraw = 0; 400 } 401 402 403 //======================================================================== 404 // Mouse position callback function 405 //======================================================================== 406 cursorPosFun(GLFWwindow * window,double x,double y)407 static void cursorPosFun(GLFWwindow* window, double x, double y) 408 { 409 int wnd_width, wnd_height, fb_width, fb_height; 410 double scale; 411 412 glfwGetWindowSize(window, &wnd_width, &wnd_height); 413 glfwGetFramebufferSize(window, &fb_width, &fb_height); 414 415 scale = (double) fb_width / (double) wnd_width; 416 417 x *= scale; 418 y *= scale; 419 420 // Depending on which view was selected, rotate around different axes 421 switch (active_view) 422 { 423 case 1: 424 rot_x += (int) (y - ypos); 425 rot_z += (int) (x - xpos); 426 do_redraw = 1; 427 break; 428 case 3: 429 rot_x += (int) (y - ypos); 430 rot_y += (int) (x - xpos); 431 do_redraw = 1; 432 break; 433 case 4: 434 rot_y += (int) (x - xpos); 435 rot_z += (int) (y - ypos); 436 do_redraw = 1; 437 break; 438 default: 439 // Do nothing for perspective view, or if no view is selected 440 break; 441 } 442 443 // Remember cursor position 444 xpos = x; 445 ypos = y; 446 } 447 448 449 //======================================================================== 450 // Mouse button callback function 451 //======================================================================== 452 mouseButtonFun(GLFWwindow * window,int button,int action,int mods)453 static void mouseButtonFun(GLFWwindow* window, int button, int action, int mods) 454 { 455 if ((button == GLFW_MOUSE_BUTTON_LEFT) && action == GLFW_PRESS) 456 { 457 // Detect which of the four views was clicked 458 active_view = 1; 459 if (xpos >= width / 2) 460 active_view += 1; 461 if (ypos >= height / 2) 462 active_view += 2; 463 } 464 else if (button == GLFW_MOUSE_BUTTON_LEFT) 465 { 466 // Deselect any previously selected view 467 active_view = 0; 468 } 469 470 do_redraw = 1; 471 } 472 key_callback(GLFWwindow * window,int key,int scancode,int action,int mods)473 static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) 474 { 475 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 476 glfwSetWindowShouldClose(window, GLFW_TRUE); 477 } 478 479 480 //======================================================================== 481 // main 482 //======================================================================== 483 main(void)484 int main(void) 485 { 486 GLFWwindow* window; 487 488 // Initialise GLFW 489 if (!glfwInit()) 490 { 491 fprintf(stderr, "Failed to initialize GLFW\n"); 492 exit(EXIT_FAILURE); 493 } 494 495 glfwWindowHint(GLFW_SAMPLES, 4); 496 497 // Open OpenGL window 498 window = glfwCreateWindow(500, 500, "Split view demo", NULL, NULL); 499 if (!window) 500 { 501 fprintf(stderr, "Failed to open GLFW window\n"); 502 503 glfwTerminate(); 504 exit(EXIT_FAILURE); 505 } 506 507 // Set callback functions 508 glfwSetFramebufferSizeCallback(window, framebufferSizeFun); 509 glfwSetWindowRefreshCallback(window, windowRefreshFun); 510 glfwSetCursorPosCallback(window, cursorPosFun); 511 glfwSetMouseButtonCallback(window, mouseButtonFun); 512 glfwSetKeyCallback(window, key_callback); 513 514 // Enable vsync 515 glfwMakeContextCurrent(window); 516 gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); 517 glfwSwapInterval(1); 518 519 if (GLAD_GL_ARB_multisample || GLAD_GL_VERSION_1_3) 520 glEnable(GL_MULTISAMPLE_ARB); 521 522 glfwGetFramebufferSize(window, &width, &height); 523 framebufferSizeFun(window, width, height); 524 525 // Main loop 526 for (;;) 527 { 528 // Only redraw if we need to 529 if (do_redraw) 530 windowRefreshFun(window); 531 532 // Wait for new events 533 glfwWaitEvents(); 534 535 // Check if the window should be closed 536 if (glfwWindowShouldClose(window)) 537 break; 538 } 539 540 // Close OpenGL window and terminate GLFW 541 glfwTerminate(); 542 543 exit(EXIT_SUCCESS); 544 } 545 546