splitview.c 15 KB

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