xref: /third_party/glfw/examples/splitview.c (revision b877906b)
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
35static double xpos = 0, ypos = 0;
36
37// Window size
38static int width, height;
39
40// Active view: 0 = none, 1 = upper left, 2 = upper right, 3 = lower left,
41// 4 = lower right
42static int active_view = 0;
43
44// Rotation around each axis
45static int rot_x = 0, rot_y = 0, rot_z = 0;
46
47// Do redraw?
48static 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
60static 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
121static 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
153static 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
215static 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
385static 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
397static 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
409static 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
455static 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
475static 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
486int 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