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