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