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