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