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