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