Last active
April 16, 2024 19:42
-
-
Save cosinekitty/e8f4f3cf34019bb873ad2d7f552d7fa6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#include <cmath> | |
#include <string> | |
#include "raylib.h" | |
#include "waterpool.hpp" | |
const int WIDTH = 160; | |
const int HEIGHT = 160; | |
using PoolType = Sapphire::WaterPool<WIDTH, HEIGHT>; | |
const int PIXELS_PER_CELL = 4; | |
struct RenderContext | |
{ | |
const int screenWidth = WIDTH * PIXELS_PER_CELL; | |
const int screenHeight = HEIGHT * PIXELS_PER_CELL; | |
float zoom = 8000.0f; // pixels per meter | |
float xCenter = 0.05f; | |
float yCenter = 0.00f; | |
int xScreen(float x) const | |
{ | |
return (screenWidth/2) + static_cast<int>(round(zoom * (x - xCenter))); | |
} | |
int yScreen(float y) const | |
{ | |
return (screenHeight/2) - static_cast<int>(round(zoom * (y - yCenter))); | |
} | |
int scale(float r) const | |
{ | |
return static_cast<int>(round(zoom * r)); | |
} | |
static Color cellColor(const Sapphire::WaterCell& cell) | |
{ | |
using namespace Sapphire; | |
if (cell.wet == 0.0f) | |
return WHITE; | |
float g = 128.0f * (cell.pos + 1.0f); | |
if (g > 255.0f) | |
g = 255.0f; | |
else if (g < 0.0f) | |
g = 0.0f; | |
return CLITERAL(Color){0, static_cast<unsigned char>(g), 32, 255}; | |
} | |
void draw(const PoolType& pool) | |
{ | |
using namespace Sapphire; | |
for (int i = 0; i < WIDTH; ++i) | |
{ | |
for (int j = 0; j < HEIGHT; ++j) | |
{ | |
const WaterCell& cell = pool.getCell(i, j); | |
Color color = cellColor(cell); | |
DrawRectangle(i*PIXELS_PER_CELL, j*PIXELS_PER_CELL, PIXELS_PER_CELL, PIXELS_PER_CELL, color); | |
} | |
} | |
} | |
}; | |
int main(int argc, const char *argv[]) | |
{ | |
using namespace Sapphire; | |
float dt = 1.0f / 48000.0f; | |
float halflife = 0.07f; | |
float c = 2.0f; // speed of waves in meters/second | |
float s = 0.001f; // grid spacing in meters | |
float k = (c*c) / (s*s); // propagation constant [second^(-2)] | |
PoolType pool; | |
RenderContext render; | |
for (int i = 0; i < 3; ++i) | |
{ | |
for (int j = 0; j < 3; ++j) | |
{ | |
int r = 1 + i*i + j*j; | |
pool.getCell(i + WIDTH/5, j + HEIGHT/2).vel = +20000.0f / r; | |
} | |
} | |
// Create reflective barriers. | |
for (int i = 30; i+10 < WIDTH; ++i) | |
{ | |
pool.getCell(i, HEIGHT/2-7).wet = 0.0f; | |
pool.getCell(i-13, HEIGHT/2+17).wet = 0.0f; | |
} | |
InitWindow(render.screenWidth, render.screenHeight, "Water Simulation by Don Cross"); | |
SetTargetFPS(240); | |
while (!WindowShouldClose()) | |
{ | |
BeginDrawing(); | |
ClearBackground(BLACK); | |
render.draw(pool); | |
EndDrawing(); | |
pool.update(dt, halflife, k); | |
} | |
CloseWindow(); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
waterpool.hpp - Don Cross - 2023-06-26 | |
Simulation of waves moving on the surface of water. | |
Based on ideas from the video | |
"How to write a Height-Field Water Simulator with 100 lines of code" | |
by Matthias Müller / Ten Minute Physics: | |
https://www.youtube.com/watch?v=hswBi5wcqAA | |
*/ | |
#ifndef __COSINEKITTY_WATERPOOL_HPP | |
#define __COSINEKITTY_WATERPOOL_HPP | |
#include <vector> | |
#include <cmath> | |
namespace Sapphire | |
{ | |
struct WaterCell | |
{ | |
float wet {1.0f}; | |
float pos {0.0f}; | |
float vel {0.0f}; | |
float acc {0.0f}; | |
}; | |
template <int WIDTH, int HEIGHT> | |
class WaterPool | |
{ | |
private: | |
static_assert(WIDTH > 0, "Width must be a positive integer."); | |
static_assert(HEIGHT > 0, "Height must be a positive integer."); | |
static const int SIZE = WIDTH * HEIGHT; | |
std::vector<WaterCell> cell { SIZE }; | |
static constexpr int index(int i, int j) | |
{ | |
// Use border-wraparound logic. | |
// Tolerate -1, but not arbitrarily negative integers. | |
return ((i + WIDTH)%WIDTH) + WIDTH*((j + HEIGHT)%HEIGHT); | |
} | |
float acceleration(const WaterCell& h, int i, int j) const | |
{ | |
const WaterCell& o = cell[index(i, j)]; | |
return o.wet*(o.pos - h.pos); | |
} | |
public: | |
const WaterCell& getCell(int i, int j) const | |
{ | |
return cell.at(index(i, j)); | |
} | |
WaterCell& getCell(int i, int j) | |
{ | |
return cell.at(index(i, j)); | |
} | |
void update(float dt, float halflife, float k) | |
{ | |
const float damp = pow(0.5, dt/halflife); | |
// Calculate acceleration of each water cell. | |
for (int i = 0; i < WIDTH; ++i) | |
{ | |
for (int j = 0; j < HEIGHT; ++j) | |
{ | |
WaterCell& h = cell[index(i, j)]; | |
if (h.wet > 0.0f) | |
{ | |
h.acc = k * ( | |
acceleration(h, i, j+1) + | |
acceleration(h, i, j-1) + | |
acceleration(h, i-1, j) + | |
acceleration(h, i+1, j) | |
); | |
} | |
} | |
} | |
// Use accelerations to update position and velocity of each water cell. | |
for (int i = 0; i < WIDTH; ++i) | |
{ | |
for (int j = 0; j < HEIGHT; ++j) | |
{ | |
WaterCell& h = cell[index(i, j)]; | |
if (h.wet > 0.0f) | |
{ | |
h.vel = (damp * h.vel) + (dt * h.acc); | |
h.pos += (dt * h.vel); | |
} | |
} | |
} | |
} | |
}; | |
} | |
#endif // __COSINEKITTY_WATERPOOL_HPP |
How do I compile this?
Here is the bash script I used to build and run the program on Linux, using gcc 12.2.0:
#!/bin/bash g++ -Wall -Werror -O3 -o animate animate.cpp -l raylib -l pthread -l dl || exit 1 ./animate $1 || exit 1 exit 0
Oh , thanks a lot
Works 👍
Thanks for making the video , learnt good things ❤️
Excited for more
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is the bash script I used to build and run the program on Linux, using gcc 12.2.0: