-
-
Save cosinekitty/e8f4f3cf34019bb873ad2d7f552d7fa6 to your computer and use it in GitHub Desktop.
#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; | |
} |
/* | |
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 |
Where can I find "raylib.h" and "waterpool.hpp"? Sorry for my ignorance if I am missing something trivial.
Where can I find "raylib.h" and "waterpool.hpp"? Sorry for my ignorance if I am missing something trivial.
Hi @satyam-bhardwaj, the file waterpool.hpp is inside this gist. You just need to scroll down below animate.cpp. The file raylib.h is available separately in the raylib project. I hope this helps.
Where can I find "raylib.h" and "waterpool.hpp"? Sorry for my ignorance if I am missing something trivial.
Hi @satyam-bhardwaj, the file waterpool.hpp is inside this gist. You just need to scroll down below animate.cpp. The file raylib.h is available separately in the raylib project. I hope this helps.
Thank you for your kind and quick reply!
How do I compile this?
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
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
Yes, this is the exact code that was used to create the video at https://www.youtube.com/watch?v=9YV2043cfO0