A record of every milestone from the first commit to today, followed by what comes next.
Commits: initial commit, initialised cmake and sfml
Set up CMake, linked SFML 3, opened a window. Nothing moves yet.
Commits: you can draw sand with mouse now, sand now falls down in both straight and diagonal way
- First working grid: flat
std::vector<Cell>, 1D index helpery * GRID_WIDTH + x - Sand falls straight down; added diagonal sliding (down-left / down-right)
- Mouse painting with a fixed brush
Commits: fixed the left bias issue and added a brush size, fixed some bugs, added eraser
- Alternating left/right horizontal scan to eliminate directional bias in sand piles
- Configurable brush radius
- Eraser tool (right-click paints Empty)
Commit: added 'Stone' material
- Added immovable
Stonematerial - Material selection via keyboard (1 Sand, 2 Stone)
Commits: added water but the flow is not proper, water logic fixed
- Water falls and tries to spread laterally
- Early version had flow artifacts; fixed with the
WATER_FLOW-limited search that finds the furthest reachable cell while preferring positions with an empty cell below
Commit: Refractored the code into different files and added water/sand swapping
- Split the monolithic
main.cppintosimulation.h,simulation.cpp, andtypes.h - Sand sinks through water via
swapWithWater()— first cross-material interaction - Material 3 = Water added to keyboard controls
Commits: added the second grid logic for better rendering, added texture to the materials and improved the logic, improved the performance of the code
- Moved to a per-cell shade system for visual depth
- Texture-based rendering experiments
- General simulation and rendering performance improvements
April 2026
Major architectural overhaul implementing the design from implementation-plan.md:
Simulation
enum class Materialreplaced withusing MaterialId = uint16_tMaterialDefstruct: id, name, movement model, trait flags, density, spreadFactor, shadeMin/Max, color, specialHookMaterialRegistryclass: O(1) lookup by id,buildDefaults()factoryCellexpanded: material, shade, temperature, life, aux (8 bytes, cache-friendly)updateSand/updateWaterreplaced by reusable families:updatePowder,updateLiquid,updateGasmoveCell/swapWithWaterreplaced bytryMove,trySwap,tryDisplaceByDensity,spawnInto- Density-based displacement is now fully data-driven — no hardcoded material pairs
- Two-pass update loop: bottom-to-top (Powder/Liquid/Static), top-to-bottom (Gas)
std::vector<uint8_t>for the updated flag (avoidsvector<bool>bitfield overhead)
Renderer
- Six-vertices-per-cell
VertexArray(360,000 writes/frame) replaced with a CPU RGBA pixel buffer and a singlesf::Texture::update()call (60,000 writes/frame) - Shade modulation in renderer:
rendered = base_color * (shade / 128.0) ColorRGBAis SFML-agnostic — onlymain.cppknows about SFML
App
- Scroll-wheel brush resize added
- HUD material name sourced from registry (
matDef->name) rather than a hardcoded string switch
April 2026
- Added
Oilas a built-in registry material MovementModel::Liquid,density = 0.8,spreadFactor = 3- Water now sinks through oil automatically via liquid density displacement
- Keyboard material selection updated:
4= Oil
Smoke / Steam
- Gas material that rises
- Gas family already in place (
MovementModel::Gas, top-down Pass 2) - Steam spawned by Water+Fire interaction via an
interactionRule
Fire
MovementModel::Organic, driven byspecialHook- Spreads to
Flammableneighbors viainteractionRules, decrementslifeeach frame, transforms to Smoke whenlifereaches 0 - Uses
Cell::lifeandCell::temperaturefields already in the struct
Cryo / Ice
- Freezes Water neighbors below a temperature threshold
- Uses
Cell::temperaturefor local heat state ConductsHeattrait enables heat propagation between neighbors
Double-buffer update
All grid writes currently happen in-place, which means a particle moved early in the scan can affect particles scanned later in the same frame. A double-buffer (read from grid A, write to grid B, swap at end of frame) eliminates this. The tryMove/trySwap/tryDisplaceByDensity/spawnInto helpers are the only functions that would need to change.
Lava
- Dense, slow-spreading liquid (
Liquid, lowspreadFactor, highdensity) - Destroys
Flammablecells on contact (viaspecialHook) - Cools to
Stonewhen adjacent to Water (temperature interaction)
Plants / Organic growth
Organicmovement model, fully hook-driven- Grows into neighboring Empty or Water cells up to a configured rate
Trait::SupportsGrowthmarks cells that plants can colonise
Acid
- Liquid that calls
spawnInto(neighbor, MAT_EMPTY)on contact withSolidLikeorFlammableneighbors - Destroys itself in the process (converts to Empty or Steam)
Pressure / fluid equalization Currently water finds its level by gravity and lateral flow but does not model pressure. True pressure equalization (water rising through a U-tube) requires a more sophisticated fluid model.
Temperature field
A per-cell temperature that propagates via ConductsHeat neighbors each frame. Fire heats its surroundings; ice cools them. Materials with ignition or freeze thresholds stored in MaterialDef react automatically.
JSON/YAML material authoring
Right now materials are defined in C++ (buildDefaults()). A future phase could load material definitions from a data file at startup, enabling modding without recompiling.
SDL3 / OpenGL backend
The simulation layer has zero SFML dependency. Swapping the backend means rewriting only main.cpp. SDL3 is the preferred future target if SFML becomes a limitation. OpenGL would only make sense as a renderer implementation detail if GPU-side particle simulation becomes worthwhile.
Performance: resting optimization Cells that have not moved for N frames could be put to sleep and skipped in the update loop. This is a common optimization in mature falling sand engines and would significantly reduce CPU load when the grid is mostly settled.