• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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