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