You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
		
		
		
		
		
			
		
			
				
					
					
						
							460 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							460 lines
						
					
					
						
							12 KiB
						
					
					
				| /***************************************************************************** | |
|  * Wave Simulation in OpenGL | |
|  * (C) 2002 Jakob Thomsen | |
|  * http://home.in.tum.de/~thomsen | |
|  * Modified for GLFW by Sylvain Hellegouarch - sh@programmationworld.com | |
|  * Modified for variable frame rate by Marcus Geelnard | |
|  * 2003-Jan-31: Minor cleanups and speedups / MG | |
|  * 2010-10-24: Formatting and cleanup - Camilla Berglund | |
|  *****************************************************************************/ | |
| 
 | |
| #if defined(_MSC_VER) | |
|  // Make MS math.h define M_PI | |
|  #define _USE_MATH_DEFINES | |
| #endif | |
|  | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <math.h> | |
|  | |
| #include <glad/glad.h> | |
| #include <GLFW/glfw3.h> | |
|  | |
| #include <linmath.h> | |
|  | |
| // Maximum delta T to allow for differential calculations | |
| #define MAX_DELTA_T 0.01 | |
|  | |
| // Animation speed (10.0 looks good) | |
| #define ANIMATION_SPEED 10.0 | |
|  | |
| GLfloat alpha = 210.f, beta = -70.f; | |
| GLfloat zoom = 2.f; | |
| 
 | |
| double cursorX; | |
| double cursorY; | |
| 
 | |
| struct Vertex | |
| { | |
|     GLfloat x, y, z; | |
|     GLfloat r, g, b; | |
| }; | |
| 
 | |
| #define GRIDW 50 | |
| #define GRIDH 50 | |
| #define VERTEXNUM (GRIDW*GRIDH) | |
|  | |
| #define QUADW (GRIDW - 1) | |
| #define QUADH (GRIDH - 1) | |
| #define QUADNUM (QUADW*QUADH) | |
|  | |
| GLuint quad[4 * QUADNUM]; | |
| struct Vertex vertex[VERTEXNUM]; | |
| 
 | |
| /* The grid will look like this: | |
|  * | |
|  *      3   4   5 | |
|  *      *---*---* | |
|  *      |   |   | | |
|  *      | 0 | 1 | | |
|  *      |   |   | | |
|  *      *---*---* | |
|  *      0   1   2 | |
|  */ | |
| 
 | |
| //======================================================================== | |
| // Initialize grid geometry | |
| //======================================================================== | |
|  | |
| void init_vertices(void) | |
| { | |
|     int x, y, p; | |
| 
 | |
|     // Place the vertices in a grid | |
|     for (y = 0;  y < GRIDH;  y++) | |
|     { | |
|         for (x = 0;  x < GRIDW;  x++) | |
|         { | |
|             p = y * GRIDW + x; | |
| 
 | |
|             vertex[p].x = (GLfloat) (x - GRIDW / 2) / (GLfloat) (GRIDW / 2); | |
|             vertex[p].y = (GLfloat) (y - GRIDH / 2) / (GLfloat) (GRIDH / 2); | |
|             vertex[p].z = 0; | |
| 
 | |
|             if ((x % 4 < 2) ^ (y % 4 < 2)) | |
|                 vertex[p].r = 0.0; | |
|             else | |
|                 vertex[p].r = 1.0; | |
| 
 | |
|             vertex[p].g = (GLfloat) y / (GLfloat) GRIDH; | |
|             vertex[p].b = 1.f - ((GLfloat) x / (GLfloat) GRIDW + (GLfloat) y / (GLfloat) GRIDH) / 2.f; | |
|         } | |
|     } | |
| 
 | |
|     for (y = 0;  y < QUADH;  y++) | |
|     { | |
|         for (x = 0;  x < QUADW;  x++) | |
|         { | |
|             p = 4 * (y * QUADW + x); | |
| 
 | |
|             quad[p + 0] = y       * GRIDW + x;     // Some point | |
|             quad[p + 1] = y       * GRIDW + x + 1; // Neighbor at the right side | |
|             quad[p + 2] = (y + 1) * GRIDW + x + 1; // Upper right neighbor | |
|             quad[p + 3] = (y + 1) * GRIDW + x;     // Upper neighbor | |
|         } | |
|     } | |
| } | |
| 
 | |
| double dt; | |
| double p[GRIDW][GRIDH]; | |
| double vx[GRIDW][GRIDH], vy[GRIDW][GRIDH]; | |
| double ax[GRIDW][GRIDH], ay[GRIDW][GRIDH]; | |
| 
 | |
| //======================================================================== | |
| // Initialize grid | |
| //======================================================================== | |
|  | |
| void init_grid(void) | |
| { | |
|     int x, y; | |
|     double dx, dy, d; | |
| 
 | |
|     for (y = 0; y < GRIDH;  y++) | |
|     { | |
|         for (x = 0; x < GRIDW;  x++) | |
|         { | |
|             dx = (double) (x - GRIDW / 2); | |
|             dy = (double) (y - GRIDH / 2); | |
|             d = sqrt(dx * dx + dy * dy); | |
|             if (d < 0.1 * (double) (GRIDW / 2)) | |
|             { | |
|                 d = d * 10.0; | |
|                 p[x][y] = -cos(d * (M_PI / (double)(GRIDW * 4))) * 100.0; | |
|             } | |
|             else | |
|                 p[x][y] = 0.0; | |
| 
 | |
|             vx[x][y] = 0.0; | |
|             vy[x][y] = 0.0; | |
|         } | |
|     } | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Draw scene | |
| //======================================================================== | |
|  | |
| void draw_scene(GLFWwindow* window) | |
| { | |
|     // Clear the color and depth buffers | |
|     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| 
 | |
|     // We don't want to modify the projection matrix | |
|     glMatrixMode(GL_MODELVIEW); | |
|     glLoadIdentity(); | |
| 
 | |
|     // Move back | |
|     glTranslatef(0.0, 0.0, -zoom); | |
|     // Rotate the view | |
|     glRotatef(beta, 1.0, 0.0, 0.0); | |
|     glRotatef(alpha, 0.0, 0.0, 1.0); | |
| 
 | |
|     glDrawElements(GL_QUADS, 4 * QUADNUM, GL_UNSIGNED_INT, quad); | |
| 
 | |
|     glfwSwapBuffers(window); | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Initialize Miscellaneous OpenGL state | |
| //======================================================================== | |
|  | |
| void init_opengl(void) | |
| { | |
|     // Use Gouraud (smooth) shading | |
|     glShadeModel(GL_SMOOTH); | |
| 
 | |
|     // Switch on the z-buffer | |
|     glEnable(GL_DEPTH_TEST); | |
| 
 | |
|     glEnableClientState(GL_VERTEX_ARRAY); | |
|     glEnableClientState(GL_COLOR_ARRAY); | |
|     glVertexPointer(3, GL_FLOAT, sizeof(struct Vertex), vertex); | |
|     glColorPointer(3, GL_FLOAT, sizeof(struct Vertex), &vertex[0].r); // Pointer to the first color | |
|  | |
|     glPointSize(2.0); | |
| 
 | |
|     // Background color is black | |
|     glClearColor(0, 0, 0, 0); | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Modify the height of each vertex according to the pressure | |
| //======================================================================== | |
|  | |
| void adjust_grid(void) | |
| { | |
|     int pos; | |
|     int x, y; | |
| 
 | |
|     for (y = 0; y < GRIDH;  y++) | |
|     { | |
|         for (x = 0;  x < GRIDW;  x++) | |
|         { | |
|             pos = y * GRIDW + x; | |
|             vertex[pos].z = (float) (p[x][y] * (1.0 / 50.0)); | |
|         } | |
|     } | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Calculate wave propagation | |
| //======================================================================== | |
|  | |
| void calc_grid(void) | |
| { | |
|     int x, y, x2, y2; | |
|     double time_step = dt * ANIMATION_SPEED; | |
| 
 | |
|     // Compute accelerations | |
|     for (x = 0;  x < GRIDW;  x++) | |
|     { | |
|         x2 = (x + 1) % GRIDW; | |
|         for(y = 0; y < GRIDH; y++) | |
|             ax[x][y] = p[x][y] - p[x2][y]; | |
|     } | |
| 
 | |
|     for (y = 0;  y < GRIDH;  y++) | |
|     { | |
|         y2 = (y + 1) % GRIDH; | |
|         for(x = 0; x < GRIDW; x++) | |
|             ay[x][y] = p[x][y] - p[x][y2]; | |
|     } | |
| 
 | |
|     // Compute speeds | |
|     for (x = 0;  x < GRIDW;  x++) | |
|     { | |
|         for (y = 0;  y < GRIDH;  y++) | |
|         { | |
|             vx[x][y] = vx[x][y] + ax[x][y] * time_step; | |
|             vy[x][y] = vy[x][y] + ay[x][y] * time_step; | |
|         } | |
|     } | |
| 
 | |
|     // Compute pressure | |
|     for (x = 1;  x < GRIDW;  x++) | |
|     { | |
|         x2 = x - 1; | |
|         for (y = 1;  y < GRIDH;  y++) | |
|         { | |
|             y2 = y - 1; | |
|             p[x][y] = p[x][y] + (vx[x2][y] - vx[x][y] + vy[x][y2] - vy[x][y]) * time_step; | |
|         } | |
|     } | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Print errors | |
| //======================================================================== | |
|  | |
| static void error_callback(int error, const char* description) | |
| { | |
|     fprintf(stderr, "Error: %s\n", description); | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Handle key strokes | |
| //======================================================================== | |
|  | |
| void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) | |
| { | |
|     if (action != GLFW_PRESS) | |
|         return; | |
| 
 | |
|     switch (key) | |
|     { | |
|         case GLFW_KEY_ESCAPE: | |
|             glfwSetWindowShouldClose(window, GLFW_TRUE); | |
|             break; | |
|         case GLFW_KEY_SPACE: | |
|             init_grid(); | |
|             break; | |
|         case GLFW_KEY_LEFT: | |
|             alpha += 5; | |
|             break; | |
|         case GLFW_KEY_RIGHT: | |
|             alpha -= 5; | |
|             break; | |
|         case GLFW_KEY_UP: | |
|             beta -= 5; | |
|             break; | |
|         case GLFW_KEY_DOWN: | |
|             beta += 5; | |
|             break; | |
|         case GLFW_KEY_PAGE_UP: | |
|             zoom -= 0.25f; | |
|             if (zoom < 0.f) | |
|                 zoom = 0.f; | |
|             break; | |
|         case GLFW_KEY_PAGE_DOWN: | |
|             zoom += 0.25f; | |
|             break; | |
|         default: | |
|             break; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Callback function for mouse button events | |
| //======================================================================== | |
|  | |
| void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) | |
| { | |
|     if (button != GLFW_MOUSE_BUTTON_LEFT) | |
|         return; | |
| 
 | |
|     if (action == GLFW_PRESS) | |
|     { | |
|         glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); | |
|         glfwGetCursorPos(window, &cursorX, &cursorY); | |
|     } | |
|     else | |
|         glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Callback function for cursor motion events | |
| //======================================================================== | |
|  | |
| void cursor_position_callback(GLFWwindow* window, double x, double y) | |
| { | |
|     if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) | |
|     { | |
|         alpha += (GLfloat) (x - cursorX) / 10.f; | |
|         beta += (GLfloat) (y - cursorY) / 10.f; | |
| 
 | |
|         cursorX = x; | |
|         cursorY = y; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Callback function for scroll events | |
| //======================================================================== | |
|  | |
| void scroll_callback(GLFWwindow* window, double x, double y) | |
| { | |
|     zoom += (float) y / 4.f; | |
|     if (zoom < 0) | |
|         zoom = 0; | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // Callback function for framebuffer resize events | |
| //======================================================================== | |
|  | |
| void framebuffer_size_callback(GLFWwindow* window, int width, int height) | |
| { | |
|     float ratio = 1.f; | |
|     mat4x4 projection; | |
| 
 | |
|     if (height > 0) | |
|         ratio = (float) width / (float) height; | |
| 
 | |
|     // Setup viewport | |
|     glViewport(0, 0, width, height); | |
| 
 | |
|     // Change to the projection matrix and set our viewing volume | |
|     glMatrixMode(GL_PROJECTION); | |
|     mat4x4_perspective(projection, | |
|                        60.f * (float) M_PI / 180.f, | |
|                        ratio, | |
|                        1.f, 1024.f); | |
|     glLoadMatrixf((const GLfloat*) projection); | |
| } | |
| 
 | |
| 
 | |
| //======================================================================== | |
| // main | |
| //======================================================================== | |
|  | |
| int main(int argc, char* argv[]) | |
| { | |
|     GLFWwindow* window; | |
|     double t, dt_total, t_old; | |
|     int width, height; | |
| 
 | |
|     glfwSetErrorCallback(error_callback); | |
| 
 | |
|     if (!glfwInit()) | |
|         exit(EXIT_FAILURE); | |
| 
 | |
|     window = glfwCreateWindow(640, 480, "Wave Simulation", NULL, NULL); | |
|     if (!window) | |
|     { | |
|         glfwTerminate(); | |
|         exit(EXIT_FAILURE); | |
|     } | |
| 
 | |
|     glfwSetKeyCallback(window, key_callback); | |
|     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); | |
|     glfwSetMouseButtonCallback(window, mouse_button_callback); | |
|     glfwSetCursorPosCallback(window, cursor_position_callback); | |
|     glfwSetScrollCallback(window, scroll_callback); | |
| 
 | |
|     glfwMakeContextCurrent(window); | |
|     gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); | |
|     glfwSwapInterval(1); | |
| 
 | |
|     glfwGetFramebufferSize(window, &width, &height); | |
|     framebuffer_size_callback(window, width, height); | |
| 
 | |
|     // Initialize OpenGL | |
|     init_opengl(); | |
| 
 | |
|     // Initialize simulation | |
|     init_vertices(); | |
|     init_grid(); | |
|     adjust_grid(); | |
| 
 | |
|     // Initialize timer | |
|     t_old = glfwGetTime() - 0.01; | |
| 
 | |
|     while (!glfwWindowShouldClose(window)) | |
|     { | |
|         t = glfwGetTime(); | |
|         dt_total = t - t_old; | |
|         t_old = t; | |
| 
 | |
|         // Safety - iterate if dt_total is too large | |
|         while (dt_total > 0.f) | |
|         { | |
|             // Select iteration time step | |
|             dt = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total; | |
|             dt_total -= dt; | |
| 
 | |
|             // Calculate wave propagation | |
|             calc_grid(); | |
|         } | |
| 
 | |
|         // Compute height of each vertex | |
|         adjust_grid(); | |
| 
 | |
|         // Draw wave grid to OpenGL display | |
|         draw_scene(window); | |
| 
 | |
|         glfwPollEvents(); | |
|     } | |
| 
 | |
|     exit(EXIT_SUCCESS); | |
| } | |
| 
 | |
| 
 |