463 lines
12 KiB
C
463 lines
12 KiB
C
|
/*****************************************************************************
|
||
|
* 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 Löwy
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#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/gl.h>
|
||
|
#define GLFW_INCLUDE_NONE
|
||
|
#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);
|
||
|
gladLoadGL(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();
|
||
|
}
|
||
|
|
||
|
glfwTerminate();
|
||
|
exit(EXIT_SUCCESS);
|
||
|
}
|
||
|
|