• 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 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