• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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