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
35 static double xpos = 0, ypos = 0;
36
37 // Window size
38 static int width, height;
39
40 // Active view: 0 = none, 1 = upper left, 2 = upper right, 3 = lower left,
41 // 4 = lower right
42 static int active_view = 0;
43
44 // Rotation around each axis
45 static int rot_x = 0, rot_y = 0, rot_z = 0;
46
47 // Do redraw?
48 static 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
drawTorus(void)60 static 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
drawScene(void)121 static 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
drawGrid(float scale, int steps)153 static 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
drawAllViews(void)215 static 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
framebufferSizeFun(GLFWwindow* window, int w, int h)385 static 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
windowRefreshFun(GLFWwindow* window)397 static 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
cursorPosFun(GLFWwindow* window, double x, double y)409 static 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
mouseButtonFun(GLFWwindow* window, int button, int action, int mods)455 static 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
key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)475 static 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
main(void)486 int 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