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