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