xref: /third_party/glfw/examples/wave.c (revision b877906b)
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
33GLfloat alpha = 210.f, beta = -70.f;
34GLfloat zoom = 2.f;
35
36double cursorX;
37double cursorY;
38
39struct 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
53GLuint quad[4 * QUADNUM];
54struct 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
71void 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
110double dt;
111double p[GRIDW][GRIDH];
112double vx[GRIDW][GRIDH], vy[GRIDW][GRIDH];
113double ax[GRIDW][GRIDH], ay[GRIDW][GRIDH];
114
115//========================================================================
116// Initialize grid
117//========================================================================
118
119void 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
150void 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
175void 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
199void 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
219void 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
266static 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
276void 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
319void 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
338void 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
355void 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
367void 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
392int 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