Last active
March 9, 2025 13:52
-
-
Save cosinekitty/e8f4f3cf34019bb873ad2d7f552d7fa6 to your computer and use it in GitHub Desktop.
Revisions
-
cosinekitty revised this gist
Jul 8, 2023 . 1 changed file with 109 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,109 @@ #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; } -
cosinekitty created this gist
Jul 8, 2023 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,99 @@ /* 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