wave.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. /*****************************************************************************
  2. * Wave Simulation in OpenGL
  3. * (C) 2002 Jakob Thomsen
  4. * http://home.in.tum.de/~thomsen
  5. * Modified for GLFW by Sylvain Hellegouarch - sh@programmationworld.com
  6. * Modified for variable frame rate by Marcus Geelnard
  7. * 2003-Jan-31: Minor cleanups and speedups / MG
  8. * 2010-10-24: Formatting and cleanup - Camilla Löwy
  9. *****************************************************************************/
  10. #if defined(_MSC_VER)
  11. // Make MS math.h define M_PI
  12. #define _USE_MATH_DEFINES
  13. #endif
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <math.h>
  17. #include <glad/gl.h>
  18. #define GLFW_INCLUDE_NONE
  19. #include <GLFW/glfw3.h>
  20. #include <linmath.h>
  21. // Maximum delta T to allow for differential calculations
  22. #define MAX_DELTA_T 0.01
  23. // Animation speed (10.0 looks good)
  24. #define ANIMATION_SPEED 10.0
  25. GLfloat alpha = 210.f, beta = -70.f;
  26. GLfloat zoom = 2.f;
  27. double cursorX;
  28. double cursorY;
  29. struct Vertex
  30. {
  31. GLfloat x, y, z;
  32. GLfloat r, g, b;
  33. };
  34. #define GRIDW 50
  35. #define GRIDH 50
  36. #define VERTEXNUM (GRIDW*GRIDH)
  37. #define QUADW (GRIDW - 1)
  38. #define QUADH (GRIDH - 1)
  39. #define QUADNUM (QUADW*QUADH)
  40. GLuint quad[4 * QUADNUM];
  41. struct Vertex vertex[VERTEXNUM];
  42. /* The grid will look like this:
  43. *
  44. * 3 4 5
  45. * *---*---*
  46. * | | |
  47. * | 0 | 1 |
  48. * | | |
  49. * *---*---*
  50. * 0 1 2
  51. */
  52. //========================================================================
  53. // Initialize grid geometry
  54. //========================================================================
  55. void init_vertices(void)
  56. {
  57. int x, y, p;
  58. // Place the vertices in a grid
  59. for (y = 0; y < GRIDH; y++)
  60. {
  61. for (x = 0; x < GRIDW; x++)
  62. {
  63. p = y * GRIDW + x;
  64. vertex[p].x = (GLfloat) (x - GRIDW / 2) / (GLfloat) (GRIDW / 2);
  65. vertex[p].y = (GLfloat) (y - GRIDH / 2) / (GLfloat) (GRIDH / 2);
  66. vertex[p].z = 0;
  67. if ((x % 4 < 2) ^ (y % 4 < 2))
  68. vertex[p].r = 0.0;
  69. else
  70. vertex[p].r = 1.0;
  71. vertex[p].g = (GLfloat) y / (GLfloat) GRIDH;
  72. vertex[p].b = 1.f - ((GLfloat) x / (GLfloat) GRIDW + (GLfloat) y / (GLfloat) GRIDH) / 2.f;
  73. }
  74. }
  75. for (y = 0; y < QUADH; y++)
  76. {
  77. for (x = 0; x < QUADW; x++)
  78. {
  79. p = 4 * (y * QUADW + x);
  80. quad[p + 0] = y * GRIDW + x; // Some point
  81. quad[p + 1] = y * GRIDW + x + 1; // Neighbor at the right side
  82. quad[p + 2] = (y + 1) * GRIDW + x + 1; // Upper right neighbor
  83. quad[p + 3] = (y + 1) * GRIDW + x; // Upper neighbor
  84. }
  85. }
  86. }
  87. double dt;
  88. double p[GRIDW][GRIDH];
  89. double vx[GRIDW][GRIDH], vy[GRIDW][GRIDH];
  90. double ax[GRIDW][GRIDH], ay[GRIDW][GRIDH];
  91. //========================================================================
  92. // Initialize grid
  93. //========================================================================
  94. void init_grid(void)
  95. {
  96. int x, y;
  97. double dx, dy, d;
  98. for (y = 0; y < GRIDH; y++)
  99. {
  100. for (x = 0; x < GRIDW; x++)
  101. {
  102. dx = (double) (x - GRIDW / 2);
  103. dy = (double) (y - GRIDH / 2);
  104. d = sqrt(dx * dx + dy * dy);
  105. if (d < 0.1 * (double) (GRIDW / 2))
  106. {
  107. d = d * 10.0;
  108. p[x][y] = -cos(d * (M_PI / (double)(GRIDW * 4))) * 100.0;
  109. }
  110. else
  111. p[x][y] = 0.0;
  112. vx[x][y] = 0.0;
  113. vy[x][y] = 0.0;
  114. }
  115. }
  116. }
  117. //========================================================================
  118. // Draw scene
  119. //========================================================================
  120. void draw_scene(GLFWwindow* window)
  121. {
  122. // Clear the color and depth buffers
  123. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  124. // We don't want to modify the projection matrix
  125. glMatrixMode(GL_MODELVIEW);
  126. glLoadIdentity();
  127. // Move back
  128. glTranslatef(0.0, 0.0, -zoom);
  129. // Rotate the view
  130. glRotatef(beta, 1.0, 0.0, 0.0);
  131. glRotatef(alpha, 0.0, 0.0, 1.0);
  132. glDrawElements(GL_QUADS, 4 * QUADNUM, GL_UNSIGNED_INT, quad);
  133. glfwSwapBuffers(window);
  134. }
  135. //========================================================================
  136. // Initialize Miscellaneous OpenGL state
  137. //========================================================================
  138. void init_opengl(void)
  139. {
  140. // Use Gouraud (smooth) shading
  141. glShadeModel(GL_SMOOTH);
  142. // Switch on the z-buffer
  143. glEnable(GL_DEPTH_TEST);
  144. glEnableClientState(GL_VERTEX_ARRAY);
  145. glEnableClientState(GL_COLOR_ARRAY);
  146. glVertexPointer(3, GL_FLOAT, sizeof(struct Vertex), vertex);
  147. glColorPointer(3, GL_FLOAT, sizeof(struct Vertex), &vertex[0].r); // Pointer to the first color
  148. glPointSize(2.0);
  149. // Background color is black
  150. glClearColor(0, 0, 0, 0);
  151. }
  152. //========================================================================
  153. // Modify the height of each vertex according to the pressure
  154. //========================================================================
  155. void adjust_grid(void)
  156. {
  157. int pos;
  158. int x, y;
  159. for (y = 0; y < GRIDH; y++)
  160. {
  161. for (x = 0; x < GRIDW; x++)
  162. {
  163. pos = y * GRIDW + x;
  164. vertex[pos].z = (float) (p[x][y] * (1.0 / 50.0));
  165. }
  166. }
  167. }
  168. //========================================================================
  169. // Calculate wave propagation
  170. //========================================================================
  171. void calc_grid(void)
  172. {
  173. int x, y, x2, y2;
  174. double time_step = dt * ANIMATION_SPEED;
  175. // Compute accelerations
  176. for (x = 0; x < GRIDW; x++)
  177. {
  178. x2 = (x + 1) % GRIDW;
  179. for(y = 0; y < GRIDH; y++)
  180. ax[x][y] = p[x][y] - p[x2][y];
  181. }
  182. for (y = 0; y < GRIDH; y++)
  183. {
  184. y2 = (y + 1) % GRIDH;
  185. for(x = 0; x < GRIDW; x++)
  186. ay[x][y] = p[x][y] - p[x][y2];
  187. }
  188. // Compute speeds
  189. for (x = 0; x < GRIDW; x++)
  190. {
  191. for (y = 0; y < GRIDH; y++)
  192. {
  193. vx[x][y] = vx[x][y] + ax[x][y] * time_step;
  194. vy[x][y] = vy[x][y] + ay[x][y] * time_step;
  195. }
  196. }
  197. // Compute pressure
  198. for (x = 1; x < GRIDW; x++)
  199. {
  200. x2 = x - 1;
  201. for (y = 1; y < GRIDH; y++)
  202. {
  203. y2 = y - 1;
  204. p[x][y] = p[x][y] + (vx[x2][y] - vx[x][y] + vy[x][y2] - vy[x][y]) * time_step;
  205. }
  206. }
  207. }
  208. //========================================================================
  209. // Print errors
  210. //========================================================================
  211. static void error_callback(int error, const char* description)
  212. {
  213. fprintf(stderr, "Error: %s\n", description);
  214. }
  215. //========================================================================
  216. // Handle key strokes
  217. //========================================================================
  218. void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
  219. {
  220. if (action != GLFW_PRESS)
  221. return;
  222. switch (key)
  223. {
  224. case GLFW_KEY_ESCAPE:
  225. glfwSetWindowShouldClose(window, GLFW_TRUE);
  226. break;
  227. case GLFW_KEY_SPACE:
  228. init_grid();
  229. break;
  230. case GLFW_KEY_LEFT:
  231. alpha += 5;
  232. break;
  233. case GLFW_KEY_RIGHT:
  234. alpha -= 5;
  235. break;
  236. case GLFW_KEY_UP:
  237. beta -= 5;
  238. break;
  239. case GLFW_KEY_DOWN:
  240. beta += 5;
  241. break;
  242. case GLFW_KEY_PAGE_UP:
  243. zoom -= 0.25f;
  244. if (zoom < 0.f)
  245. zoom = 0.f;
  246. break;
  247. case GLFW_KEY_PAGE_DOWN:
  248. zoom += 0.25f;
  249. break;
  250. default:
  251. break;
  252. }
  253. }
  254. //========================================================================
  255. // Callback function for mouse button events
  256. //========================================================================
  257. void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
  258. {
  259. if (button != GLFW_MOUSE_BUTTON_LEFT)
  260. return;
  261. if (action == GLFW_PRESS)
  262. {
  263. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  264. glfwGetCursorPos(window, &cursorX, &cursorY);
  265. }
  266. else
  267. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  268. }
  269. //========================================================================
  270. // Callback function for cursor motion events
  271. //========================================================================
  272. void cursor_position_callback(GLFWwindow* window, double x, double y)
  273. {
  274. if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
  275. {
  276. alpha += (GLfloat) (x - cursorX) / 10.f;
  277. beta += (GLfloat) (y - cursorY) / 10.f;
  278. cursorX = x;
  279. cursorY = y;
  280. }
  281. }
  282. //========================================================================
  283. // Callback function for scroll events
  284. //========================================================================
  285. void scroll_callback(GLFWwindow* window, double x, double y)
  286. {
  287. zoom += (float) y / 4.f;
  288. if (zoom < 0)
  289. zoom = 0;
  290. }
  291. //========================================================================
  292. // Callback function for framebuffer resize events
  293. //========================================================================
  294. void framebuffer_size_callback(GLFWwindow* window, int width, int height)
  295. {
  296. float ratio = 1.f;
  297. mat4x4 projection;
  298. if (height > 0)
  299. ratio = (float) width / (float) height;
  300. // Setup viewport
  301. glViewport(0, 0, width, height);
  302. // Change to the projection matrix and set our viewing volume
  303. glMatrixMode(GL_PROJECTION);
  304. mat4x4_perspective(projection,
  305. 60.f * (float) M_PI / 180.f,
  306. ratio,
  307. 1.f, 1024.f);
  308. glLoadMatrixf((const GLfloat*) projection);
  309. }
  310. //========================================================================
  311. // main
  312. //========================================================================
  313. int main(int argc, char* argv[])
  314. {
  315. GLFWwindow* window;
  316. double t, dt_total, t_old;
  317. int width, height;
  318. glfwSetErrorCallback(error_callback);
  319. if (!glfwInit())
  320. exit(EXIT_FAILURE);
  321. window = glfwCreateWindow(640, 480, "Wave Simulation", NULL, NULL);
  322. if (!window)
  323. {
  324. glfwTerminate();
  325. exit(EXIT_FAILURE);
  326. }
  327. glfwSetKeyCallback(window, key_callback);
  328. glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
  329. glfwSetMouseButtonCallback(window, mouse_button_callback);
  330. glfwSetCursorPosCallback(window, cursor_position_callback);
  331. glfwSetScrollCallback(window, scroll_callback);
  332. glfwMakeContextCurrent(window);
  333. gladLoadGL(glfwGetProcAddress);
  334. glfwSwapInterval(1);
  335. glfwGetFramebufferSize(window, &width, &height);
  336. framebuffer_size_callback(window, width, height);
  337. // Initialize OpenGL
  338. init_opengl();
  339. // Initialize simulation
  340. init_vertices();
  341. init_grid();
  342. adjust_grid();
  343. // Initialize timer
  344. t_old = glfwGetTime() - 0.01;
  345. while (!glfwWindowShouldClose(window))
  346. {
  347. t = glfwGetTime();
  348. dt_total = t - t_old;
  349. t_old = t;
  350. // Safety - iterate if dt_total is too large
  351. while (dt_total > 0.f)
  352. {
  353. // Select iteration time step
  354. dt = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
  355. dt_total -= dt;
  356. // Calculate wave propagation
  357. calc_grid();
  358. }
  359. // Compute height of each vertex
  360. adjust_grid();
  361. // Draw wave grid to OpenGL display
  362. draw_scene(window);
  363. glfwPollEvents();
  364. }
  365. glfwTerminate();
  366. exit(EXIT_SUCCESS);
  367. }