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