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