System Architecture¶
The Physics and Ecosystem engines are the core simulation components of the Aetherion engine. They operate as distinct but interacting systems within the game loop, sharing state through the VoxelGrid and the Entity Component System (ECS) provided by EnTT.
There are four key data systems that underpin the simulation:
VoxelGrid: The central spatial data structure that integrates all grid-based storage and exposes a unified interface for spatial queries.
TerrainStorage: The low-level, OpenVDB-backed storage for static terrain data, providing sparse and memory-efficient persistence.
TerrainGridRepository: An ECS overlay for terrain voxels that manages the boundary between static terrain data (OpenVDB) and transient runtime behavior (ECS).
entt::registry (ECS): The entity-component registry that manages all entities and their components, covering both terrain entities (active/dynamic terrain) and non-terrain entities (creatures, items, etc.).
The PhysicsEngine is responsible for simulating physical interactions — movement, collision detection, gravity, and forces. While other subsystems may contribute force values, it is the PhysicsEngine that resolves them into position and state changes for entities with velocity and moving components.
The EcosystemEngine handles higher-level environmental processes such as the water cycle. It should ideally perform read-only queries against the data systems and queue changes to be applied after its iteration completes, since ecosystem processing (and other systems) may run in parallel, with each spatial partition on its own thread.
The following sections explore each of these areas in detail:
Data Structures — A breakdown of the storage and state management layers and how they interact.
Physics Movement & Event Flow — How movement events are created, validated, and resolved until an entity actually moves.
Water Cycle & Ecosystem Simulation — How water dynamics, evaporation, and other environmental processes are modeled.
High-Level Overview¶
![digraph ownership {
rankdir=TB;
compound=true;
node [shape=box, style="rounded,filled", fontsize=10];
edge [fontsize=9];
// Root
World [label="World", fillcolor="#FFD700", style="rounded,filled,bold"];
// World direct members
Registry [label="entt::registry\n(Entity Component System)", fillcolor=lightyellow];
Dispatcher [label="entt::dispatcher\n(Event Dispatcher)", fillcolor=lightyellow];
VoxelGrid [label="VoxelGrid*\n(Central Spatial Hub)", fillcolor=lightcoral];
PyRegistry [label="PyRegistry", fillcolor="#E8E8E8"];
GameClock [label="GameClock", fillcolor="#E8E8E8"];
// World -> direct members
World -> Registry [label="owns"];
World -> Dispatcher [label="owns"];
World -> VoxelGrid [label="owns (raw ptr)"];
World -> PyRegistry [label="owns"];
World -> GameClock [label="owns"];
// World private engine members (listed in text below diagram)
Engines [label="Engines\n(private, raw ptrs)", fillcolor="#F0F0F0", style="rounded,filled"];
World -> Engines [label="owns"];
// VoxelGrid internal ownership
TerrainStorage [label="TerrainStorage\n(unique_ptr)", fillcolor=lightcyan];
TerrainGridRepo [label="TerrainGridRepository\n(unique_ptr)", fillcolor=lavender];
EntityGrid [label="entityGrid\n(Int32Grid::Ptr)", fillcolor=lightgray];
EventGrid [label="eventGrid\n(Int32Grid::Ptr)", fillcolor=lightgray];
LightingGrid [label="lightingGrid\n(FloatGrid::Ptr)", fillcolor=lightgray];
VoxelGrid -> TerrainStorage [label="owns (unique_ptr)"];
VoxelGrid -> TerrainGridRepo [label="owns (unique_ptr)"];
VoxelGrid -> EntityGrid [label="owns (shared_ptr)"];
VoxelGrid -> EventGrid [label="owns (shared_ptr)"];
VoxelGrid -> LightingGrid [label="owns (shared_ptr)"];
// VoxelGrid references
VoxelGrid -> Registry [label="references (&)", style=dashed, color=blue];
// TerrainStorage OpenVDB Grids (listed in text below diagram)
OpenVDBGrids [label="OpenVDB Grids\n(Int32Grid / FloatGrid)", fillcolor="#E0F7FA", style="rounded,filled"];
TerrainStorage -> OpenVDBGrids [label="owns"];
// TerrainGridRepository internal data
subgraph cluster_tgr_maps {
label="Bidirectional Tracking Maps";
style=filled;
fillcolor="#F3E5F5";
ByCoord [label="byCoord_\n(unordered_map<VoxelCoord, entity>)", fillcolor=white];
ByEntity [label="byEntity_\n(unordered_map<entity, VoxelCoord>)", fillcolor=white];
}
TerrainGridRepo -> ByCoord [label="owns"];
TerrainGridRepo -> ByEntity [label="owns"];
TerrainGridRepo -> Registry [label="references (&)", style=dashed, color=blue];
TerrainGridRepo -> TerrainStorage [label="references (&)", style=dashed, color=blue];
}](../../_images/graphviz-9e723e792a6ebf55e7d83f81a8c2d92bb651450e.png)
World → Engines (private, raw ptrs):
PhysicsEngine*— Movement, collision, gravity, forcesEcosystemEngine*— Water dynamics, plant cyclesLifeEngine*— Life cycle managementMetabolismSystem*— Energy, hunger, consumptionHealthSystem*— HP, damage, healingCombatSystem*— Attack resolution, combat logicEffectsSystem*— Status effects, buffs, debuffs
TerrainStorage → OpenVDB Grids (Int32Grid::Ptr unless noted):
terrainGrid— Entity ID source of truth (−2=empty, −1=VDB-only, ≥0=ECS entity)mainTypeGrid— Entity main type (TERRAIN, PLANT, BEAST, etc.)subType0Grid,subType1Grid— Subtype classificationsterrainMatterGrid,waterMatterGrid,vaporMatterGrid,biomassMatterGrid— Matter quantitiesmassGrid,maxSpeedGrid,minSpeedGrid— Physical propertiesheatGrid— Temperature (FloatGrid)flagsGrid— Packed bit flags (direction, canStack, matterState, gradient)maxLoadCapacityGrid— Structural load capacity
![digraph architecture {
rankdir=TB;
node [shape=box, style=rounded];
edge [fontsize=9];
// Engines
PhysicsEngine [label="PhysicsEngine\n(Movement, Collision,\nGravity, Forces)", fillcolor=lightblue, style="rounded,filled"];
EcosystemEngine [label="EcosystemEngine\n(Water Dynamics,\nPlant Cycles)", fillcolor=lightgreen, style="rounded,filled"];
// ECS Registry
Registry [label="entt::registry\n(Entity Component System)", fillcolor=lightyellow, style="rounded,filled"];
// Main VoxelGrid
VoxelGrid [label="VoxelGrid\n(Central Spatial Hub)", fillcolor=lightcoral, style="rounded,filled"];
// TerrainGridRepository subgraph
subgraph cluster_tgr {
label="TerrainGridRepository";
style=filled;
fillcolor=lavender;
TGR_MAPS [label="Bidirectional Maps\n(byCoord_, byEntity_)", shape=box, fillcolor=white, style="rounded,filled"];
}
// TerrainStorage subgraph
subgraph cluster_ts {
label="TerrainStorage";
style=filled;
fillcolor=lightcyan;
TS_OPENVDB [label="OpenVDB Grids\n(mainTypeGrid, etc.)", shape=box, fillcolor=white, style="rounded,filled"];
}
// Other grids
EG [label="EntityGrid\n(OpenVDB Int32Grid)", fillcolor=lightgray, style="rounded,filled"];
EvG [label="EventGrid\n(OpenVDB Int32Grid)", fillcolor=lightgray, style="rounded,filled"];
LG [label="LightingGrid\n(OpenVDB FloatGrid)", fillcolor=lightgray, style="rounded,filled"];
PhysicsManager [label="PhysicsManager\n(Global Constants)", fillcolor=lightyellow, style="rounded,filled"];
// Relationships - Engine to main systems
PhysicsEngine -> Registry [label="reads/writes"];
PhysicsEngine -> VoxelGrid [label="reads/writes"];
EcosystemEngine -> Registry [label="reads/writes"];
EcosystemEngine -> VoxelGrid [label="reads/writes"];
// VoxelGrid internal structure
VoxelGrid -> TGR_MAPS [label="manages"];
VoxelGrid -> EG [label="manages"];
VoxelGrid -> EvG [label="manages"];
VoxelGrid -> LG [label="manages"];
// TerrainGridRepository data access
TGR_MAPS -> Registry [label="syncs", style=dashed, color=blue];
TGR_MAPS -> TS_OPENVDB [label="manages", color=green];
// References
PhysicsEngine -> PhysicsManager [style=dotted];
EcosystemEngine -> PhysicsManager [style=dotted];
}](../../_images/graphviz-5e518b38e0e975138639170c51b8b424e957c782.png)
Hierarchical Abstraction Levels¶
![digraph abstraction_hierarchy {
rankdir=TB;
node [shape=box, style=rounded];
edge [fontsize=9];
// Layer 1: Application/Engine Layer
subgraph cluster_app {
label="Application/Engine Layer\n(Simulation Logic)";
style=filled;
fillcolor="#E8F4F8";
PE [label="PhysicsEngine", fillcolor=lightblue, style="rounded,filled"];
EE [label="EcosystemEngine", fillcolor=lightgreen, style="rounded,filled"];
}
// Layer 2: State Management & Coordination
subgraph cluster_state {
label="State Management & Coordination Layer\n(Data Orchestration)";
style=filled;
fillcolor="#FFF8E8";
VoxelGrid [label="VoxelGrid\n(Central Hub)", fillcolor=lightcoral, style="rounded,filled"];
TGR [label="TerrainGridRepository\n(Terrain Mapping)", fillcolor=lavender, style="rounded,filled"];
PM [label="PhysicsManager\n(Config)", fillcolor=lightyellow, style="rounded,filled"];
}
// Layer 3: Storage & Data Access
subgraph cluster_storage {
label="Storage & Data Access Layer\n(Persistent Data)";
style=filled;
fillcolor="#F0F8E8";
Registry [label="entt::registry\n(ECS)", fillcolor=lightyellow, style="rounded,filled"];
TS [label="TerrainStorage\n(OpenVDB Grids)", fillcolor=lightcyan, style="rounded,filled"];
EG [label="EntityGrid\n(Int32 OpenVDB)", fillcolor=lightgray, style="rounded,filled"];
EvG [label="EventGrid\n(Int32 OpenVDB)", fillcolor=lightgray, style="rounded,filled"];
LG [label="LightingGrid\n(Float OpenVDB)", fillcolor=lightgray, style="rounded,filled"];
}
// Application -> State Management
PE -> Registry [label="reads/writes", color=blue];
PE -> VoxelGrid [label="reads/writes", color=blue];
PE -> PM [label="queries", style=dotted, color=purple];
EE -> Registry [label="reads/writes", color=blue];
EE -> VoxelGrid [label="reads/writes", color=blue];
EE -> PM [label="queries", style=dotted, color=purple];
// State Management -> Storage
VoxelGrid -> TGR [label="manages", color=green];
VoxelGrid -> EG [label="manages", color=green];
VoxelGrid -> EvG [label="manages", color=green];
VoxelGrid -> LG [label="manages", color=green];
TGR -> Registry [label="syncs", style=dashed, color=blue];
TGR -> TS [label="manages", color=green];
// Cross-layer synchronization (shown at edges)
Registry -> TS [style=dotted, color=red, label="indirect sync"];
}](../../_images/graphviz-d70da513fd2c543db1b30d5877a29a0135237cc0.png)
Components¶
This section documents each component shown in the architecture diagrams above, organized by abstraction layer from storage to application.
Layer 3: Storage & Data Access¶
entt::registry (ECS)¶
Purpose: Core Entity Component System that manages all game entities and their components.
Location: External library (EnTT), integrated throughout the codebase.
Key Components Managed:
Position- 3D coordinates (x, y, z) and directionVelocity- Movement velocity (vx, vy, vz) for physics simulationEntityTypeComponent- Entity classification (mainType, subType0, subType1)MovingComponent- Animation state for entities in transitPhysicsStats- Mass, speed limits, forces, heatStructuralIntegrityComponent- Stack behavior, load capacity, matter stateMatterContainer- Terrain/water/vapor/biomass quantitiesHealthComponents,MetabolismComponents- Creature state
Usage Patterns:
// Iterate entities with specific components
auto view = registry.view<Position, Velocity, MovingComponent>();
for (auto entity : view) {
auto& pos = view.get<Position>(entity);
auto& vel = view.get<Velocity>(entity);
// Process movement...
}
Architectural Role: Central data store for all transient entity state. Works in tandem with VoxelGrid (spatial indexing) and TerrainStorage (persistent terrain data). Static terrain lives in OpenVDB; dynamic behavior lives in ECS.
TerrainStorage¶
Purpose: Low-level OpenVDB-backed storage for all static terrain data, providing sparse and memory-efficient storage.
Location: include/terrain_storage.hpp, src/terrain_storage.cpp
Key OpenVDB Grids:
entityGrid- Terrain entity ID (source of truth: -2=empty, -1=VDB-only, ≥0=ECS entity)mainTypeGrid- Entity main type (TERRAIN, PLANT, BEAST, etc.)subType0Grid,subType1Grid- Subtype classificationsterrainMatterGrid,waterMatterGrid,vaporMatterGrid,biomassMatterGrid- Matter quantitiesmassGrid,maxSpeedGrid,minSpeedGrid,heatGrid- Physical propertiesflagsGrid- Packed bit flags (direction, canStack, matterState, gradient)maxLoadGrid- Load capacity for structural integrity
Key Methods:
initialize()/initializeWithTransform()- Grid setupgetTotalMemoryUsage()- Memory profilinggetEntityId()/setEntityId()- Activity state managementIndividual getters/setters for all terrain attributes
isActive()- Check if voxel has dynamic behaviorpruneInactive()- Memory cleanup for inactive regionsiterateWaterVoxels(),iterateVaporVoxels(),iterateBiomassVoxels()- Efficient matter iteration
Thread Safety: Uses ThreadLocalAccessorCache for thread-local accessor caching, enabling O(1) voxel access without locking.
Architectural Role: Foundation layer for terrain data. OpenVDB’s sparse storage only stores non-default values, making it extremely memory-efficient for large worlds. All static terrain attributes are stored here; transient runtime behavior is handled by TerrainGridRepository + ECS.
EntityGrid (Int32Grid)¶
Purpose: Spatial index for non-terrain entities (creatures, items, projectiles).
Location: Member of VoxelGrid (include/voxelgrid.hpp)
Implementation: openvdb::Int32Grid::Ptr with default value -1 (no entity).
Thread Safety: Protected by entityGridMutex (std::shared_mutex) for concurrent read access.
Key Operations:
setEntityAt(x, y, z, entityId)- Place entitygetEntityAt(x, y, z)- Query entity (thread-safe)getEntityAtFast(x, y, z)- Fast read without lockremoveEntityAt(x, y, z)- Remove entitymoveEntity(oldPos, newPos, entityId)- Relocate entity
Architectural Role: Enables fast “what entity is at position (x,y,z)?” queries essential for collision detection, interaction, and rendering. Separate from terrain because entities can move, be destroyed, or created dynamically.
EventGrid (Int32Grid)¶
Purpose: Stores event IDs for tile effects and temporary spatial events (damage zones, buffs, environmental hazards).
Location: Member of VoxelGrid (include/voxelgrid.hpp)
Implementation: openvdb::Int32Grid::Ptr with default value -1 (no event).
Related Components: Event IDs reference TileEffectComponent entities containing damage type, value, and duration.
Usage: Enables location-based gameplay mechanics where multiple effects can stack at a position.
Architectural Role: Spatial index for tile-based events. Sparse storage means only active event locations consume memory, making it efficient for games with localized effects.
LightingGrid (FloatGrid)¶
Purpose: Stores lighting levels for each voxel position, enabling dynamic lighting and day/night cycles.
Location: Member of VoxelGrid (include/voxelgrid.hpp)
Implementation: openvdb::FloatGrid::Ptr with default value 0.0f (no light).
Key Operations:
setLightAt(x, y, z, lightLevel)- Set light valuegetLightAt(x, y, z)- Query light levelRegion queries for lighting propagation
Architectural Role: Visual system support for lighting calculations. Used by renderer to determine voxel brightness. Supports features like light propagation, shadows, and day/night cycles.
Layer 2: State Management & Coordination¶
VoxelGrid¶
Purpose: Central spatial data structure that integrates all grid-based storage and provides unified interface for spatial queries.
Location: include/voxelgrid.hpp, src/voxelgrid.cpp
Key Members:
width_,height_,depth_- Grid dimensionsregistry_- Reference toentt::registryterrainStorage_- OpenVDB-backed terrain storageterrainGridRepository_- ECS overlay for terrainentityGrid- Non-terrain entity placementeventGrid- Event ID gridlightingGrid- Lighting level gridentityGridMutex- Thread safety for entity grid
Key Methods:
- Initialization:
initialize()- Initializes all OpenVDB grids
- Unified Access:
getVoxel()/setVoxel()- Combined voxel data access
- Terrain Management:
addTerrain()/getTerrain()/removeTerrain()hasTerrainAt()- Terrain existence check
- Entity Management:
setEntityAt()/getEntityAt()/removeEntityAt()moveEntity()- Relocate entity to new position
- Spatial Queries:
getTerrainVoxelsInBox()- Region queries for terraingetEntitiesInBox()- Region queries for entities
- Persistence:
save()/load()- World serialization
Support Classes:
VoxelGridView- Read-only view for efficient region queriesVoxelGridViewFlatB- FlatBuffers-optimized view for serialization
Architectural Role: Central spatial database that delegates terrain storage to TerrainStorage (OpenVDB), manages entity grid directly, and provides unified interface for spatial queries. Acts as the bridge between ECS (entt::registry) and spatial storage (OpenVDB).
TerrainGridRepository¶
Purpose: ECS overlay for terrain voxels, managing the boundary between static terrain data (OpenVDB) and transient behavior (ECS). Implements “activate on demand” pattern where terrain with dynamic behavior gets ECS entities.
Location: include/terrain_grid_repository.hpp, src/terrain_grid_repository.cpp
Key Members:
registry_- ECS registry referencestorage_- OpenVDB storage backendbyCoord_- Coordinate → Entity mapping (robin_map)byEntity_- Entity → Coordinate mapping (bidirectional)terrainGridLocked_- Lock tracking flagmutex_- Thread safety
Static Data Methods (VDB-backed):
getEntityType()/setEntityType()getMatterQuantities()/setMatterQuantities()getPhysicsStats()/setPhysicsStats()getStructuralIntegrity()/setStructuralIntegrity()Individual getters/setters for mainType, subType0, mass, speed, etc.
Transient Data Methods (ECS-backed):
getVelocity()/setVelocity()- Auto-activates terrainhasMovingComponent()- Check for moving behavior
Lifecycle Management:
deactivateTerrain()- Migrates terrain entity from ECS to OpenVDBterrainExists()- Existence checkremoveTerrain()- Removes terrainmoveTerrain()- Moves terrain voxel to new locationcreateEntityForInactiveTerrain()- Creates ECS entityactivateTerrain()/isTerrainActive()- Activity state
Iterator Methods:
iterateWaterVoxels()- Efficient water iterationiterateVaporVoxels()- Efficient vapor iterationiterateBiomassVoxels()- Efficient biomass iterationiterateActiveTerrains()- Iterate terrain with ECS entities
Helper Methods:
getTerrainData()- Combines static and transient datatick()- Updates transient systems, auto-deactivates when idlelockTerrainGrid()/unlockTerrainGrid()- External synchronization
Architectural Role: Critical arbitration layer between static terrain (OpenVDB) and dynamic behavior (ECS). Implements “cold storage” pattern: inactive terrain is VDB-only (memory efficient), active terrain gets ECS entity (full simulation). The bidirectional maps (byCoord_, byEntity_) enable fast lookups in both directions.
PhysicsManager¶
Purpose: Singleton configuration class that stores global physics constants and game balance parameters.
Location: include/physics_manager.hpp, src/physics_manager.cpp
Key Configuration Parameters:
gravity_- Gravitational acceleration (default 5.0)friction_- Friction coefficient (default 1.0)allowSimultaneousMovement_- Multi-axis movement (default true)evaporationRate_- Water evaporation rate (default 8.0)evaporationHeatThreshold_- Heat required (default 120.0)evaporationMinQuantity_- Minimum water threshold (default 120,000)energyCostPerMovement_- Energy cost (default 0.000002)
Key Methods:
getInstance()- Singleton accessorgetGravity()/setGravity()getFriction()/setFriction()getAllowSimultaneousMovement()/setAllowSimultaneousMovement()getEnergyCostPerMovement()/setEnergyCostPerMovement()getEvaporationRate()/setEvaporationRate()save()/load()- Persistence (not yet implemented)
Design Pattern: Classic Singleton with typedef PhysicsManagerPtr for convenient access.
Architectural Role: Global configuration hub for all physics-related constants. Accessed by PhysicsEngine, EcosystemEngine, and other systems that need physics parameters. Enables runtime tuning of game balance without recompilation.
Layer 1: Application/Engine Layer¶
PhysicsEngine¶
Purpose: Handles physics simulation for all entities including gravity, movement, collision detection, and velocity-based motion.
Location: include/physics_engine.hpp, src/physics_engine.cpp
Key Members:
registry_- ECS registry referenceeventDispatcher_- Event dispatcher for physics eventsvoxelGrid_- Pointer to voxel grid for spatial queriesmutex_- Thread safetyprocessingAsync_- Flag for async processing statedebugEntity_- Entity for debugging
Key Methods:
- Synchronous Updates:
update(deltaTime)- Main physics update for entities with Velocity and MovingComponent
- Asynchronous Processing:
processAsync(deltaTime)- Checks for falling entities, runs in background
- Event Handlers:
handleSolidEntityMovement()- Event handler for solid entity movementhandleGasEntityMovement()- Event handler for gas entity movement
- Internal Systems:
velocityToMovement()- Converts physics velocities to grid movementcanJumpCheck()- Determines if entity can jump
Internal Helper Functions (in .cpp):
checkCollision()- Collision detectionapplyGravity()- Applies gravitational forcesprocessMovement()- Processes entity movement with collisionhandleMovementComplete()- Handles completion of movement animation
Architectural Role: Primary physics system that processes movement, collision, and applies forces. Interacts heavily with VoxelGrid for spatial queries and entt::registry for component updates. Queries PhysicsManager for constants like gravity and friction.
EcosystemEngine¶
Purpose: Manages ecosystem simulation including water flow, evaporation, condensation, and environmental effects. Features sophisticated parallel water simulation.
Location: include/ecosystem_engine.hpp, src/ecosystem_engine.cpp
Key Members:
waterSimulationManager_- Parallel water simulation managerevaporationQueue_- Queue for evaporation eventscondensationQueue_- Queue for condensation eventswaterFallingQueue_- Queue for water falling eventsmutex_- Thread safetyprocessingAsync_- Processing state flagdebugEntity_- Debug entity
Key Methods:
- Main Loop:
update(deltaTime)- Main ecosystem update loopprocessAsync(deltaTime)- Asynchronous ecosystem processing
- Water Simulation:
processParallelWaterSimulation()- Parallel water simulation using thread pool
- Event Processing:
processEvaporation()- Handles queued evaporationprocessCondensation()- Handles queued condensationprocessWaterFalling()- Handles queued water falling
- Iteration:
iterateTerrainTiles()- Iterates over tiles for ecosystem processing
Nested Classes:
WaterSimulationManager- Coordinates parallel water simulation with worker threads, grid box partitioning (32x32x32 chunks), and round-robin schedulerGridBoxProcessor- Processes water simulation for specific grid region using thread-local OpenVDB accessors for optimal cache performanceRoundRobinScheduler- Priority queue-based task scheduler with aging for fair task distribution
Architectural Role: Central system for environmental simulation. Uses OpenVDB-backed terrain storage for efficient water matter queries and sophisticated parallel processing architecture with grid box partitioning for cache-friendly iteration. Queries PhysicsManager for evaporation rates and thresholds.
Water Cycle Simulation¶
The EcosystemEngine implements a complete water cycle with multiple phases that interact to create realistic environmental dynamics. The cycle operates on terrain voxels stored in TerrainStorage and uses matter quantities (terrainMatter, waterMatter, vaporMatter) to track state changes.
Overview State Diagram¶
A high-level view of the water cycle states and phase transitions:
![digraph water_cycle_overview {
rankdir=TB;
node [shape=box, style="rounded,filled", fontsize=12];
edge [fontsize=10, penwidth=1.5];
// Main states
Liquid [label="Liquid Water\n\nwaterMatter > 0\nFlows, Pools, Falls",
fillcolor="#4682B4", fontcolor=white, style="rounded,filled"];
Vapor [label="Water Vapor\n\nvaporMatter > 0\nDiffuses, Rises",
fillcolor="#87CEEB", fontcolor=black, style="rounded,filled"];
// Phase transitions
Liquid -> Vapor [label=" Evaporation \n\nheat ≥ 120.0\nEndothermic",
color="#FF6347", penwidth=2, fontcolor="#FF6347"];
Vapor -> Liquid [label=" Condensation \n\nvapor > saturation\nExothermic",
color="#32CD32", penwidth=2, fontcolor="#32CD32"];
// Internal state transitions
Liquid -> Liquid [label="Flow\n(pressure)", color="#1E90FF", style=dashed];
Liquid -> Liquid [label="Rain\n(gravity)", color="#FF8C00", dir=back];
Vapor -> Vapor [label="Diffusion\n(gradient)", color="#4169E1", style=dotted];
Vapor -> Vapor [label="Rise\n(buoyancy)", color="#4169E1", dir=back, style=dotted];
// Conservation note
note [label="Conservation Laws:\n• Matter: Σ(water + vapor) = const\n• Energy: evap (−heat) ⇔ cond (+heat)",
shape=note, fillcolor="#FFFACD", fontsize=10];
}](../../_images/graphviz-b6a306661fd6469370c5aea7473850afb17e03e7.png)
Key Properties:
Phase Transitions: Evaporation (endothermic) ⇔ Condensation (exothermic)
Internal Dynamics: Flow, diffusion, rise, precipitation
Conservation: Matter and energy preserved across all transitions
Parallelization: 32³ voxel grid boxes, thread-local OpenVDB accessors
Phase 1: Liquid Water Movement¶
Trigger: Water voxels with sufficient matter quantity (> 0) seek equilibrium with neighbors.
Process:
Neighbor Analysis: For each water voxel at (x, y, z), check 6 cardinal neighbors (±x, ±y, ±z)
Pressure Calculation: Calculate water pressure based on matter quantity and vertical position
Flow Direction: Water flows from high pressure to low pressure (downward priority, then lateral)
Matter Transfer: Transfer water matter proportional to pressure difference
Implementation Details:
// Pseudo-code for water flow
for (auto coord : waterVoxels) {
float currentWater = storage.getWaterMatter(coord);
// Check below first (gravity priority)
Coord below = coord.offset(0, -1, 0);
if (canFlowTo(below)) {
float transfer = calculateFlowAmount(currentWater, storage.getWaterMatter(below));
storage.setWaterMatter(coord, currentWater - transfer);
storage.setWaterMatter(below, storage.getWaterMatter(below) + transfer);
}
// Check lateral neighbors for leveling
for (Coord neighbor : getLateralNeighbors(coord)) {
if (canFlowTo(neighbor)) {
float avgLevel = (currentWater + storage.getWaterMatter(neighbor)) / 2.0f;
// Transfer to reach equilibrium...
}
}
}
Parallelization: WaterSimulationManager divides the world into 32x32x32 grid boxes, processes each box independently using thread-local OpenVDB accessors, preventing race conditions.
Constraints:
Water cannot flow into solid terrain (mainType == TERRAIN with solid subType)
Flow rate limited by
PhysicsManagerconstantsMatter conservation: total water quantity remains constant across transfers
Phase 2: Evaporation¶
Trigger: Water voxels with sufficient heat and matter quantity enter evaporation queue.
Conditions (from PhysicsManager):
heat >= evaporationHeatThreshold_(default: 120.0)waterMatter >= evaporationMinQuantity_(default: 120,000 units)Exposed to air (has empty neighbor above)
Process:
Queue Population:
iterateTerrainTiles()scans water voxels, adds qualifying voxels toevaporationQueue_Evaporation Processing:
processEvaporation()consumes queue:Reduces
waterMatterbyevaporationRate_(default: 8.0 units/tick)Increases
vaporMatterby same amount (matter conservation)Reduces
heatproportionally (evaporation is endothermic)If
waterMatterreaches 0, converts voxel to vapor-only
Energy Balance:
float evaporated = std::min(waterMatter, evaporationRate);
waterMatter -= evaporated;
vaporMatter += evaporated;
heat -= evaporated * HEAT_PER_EVAPORATION; // Cooling effect
Architectural Notes: Evaporation queue prevents immediate state changes during iteration, ensuring consistency when using parallel grid box processing.
Phase 3: Vapor Transport¶
Trigger: Vapor voxels (vaporMatter > 0) diffuse through atmosphere.
Process:
Diffusion: Vapor spreads to neighboring empty voxels following concentration gradient
Vertical Rise: Vapor has slight upward bias (buoyancy), accumulating at higher altitudes
Wind Effects (if implemented): Horizontal transport based on wind velocity fields
Diffusion Algorithm:
// Simplified vapor diffusion
for (auto coord : vaporVoxels) {
float currentVapor = storage.getVaporMatter(coord);
float avgConcentration = currentVapor;
for (Coord neighbor : getNeighbors(coord)) {
avgConcentration += storage.getVaporMatter(neighbor);
}
avgConcentration /= 7.0f; // Current + 6 neighbors
// Transfer toward average (diffusion)
float transfer = (currentVapor - avgConcentration) * DIFFUSION_RATE;
// Apply transfers...
}
Constraints:
Vapor cannot enter solid terrain
Vapor accumulates in enclosed spaces (caves, buildings)
Diffusion rate slower than liquid water flow (gas vs liquid dynamics)
Phase 4: Condensation¶
Trigger: Vapor voxels at high altitude or low temperature become saturated.
Conditions:
vaporMatterexceeds saturation threshold (altitude-dependent)heatfalls below condensation temperaturePresence of condensation nuclei (terrain, particles)
Process:
Saturation Check: Calculate saturation capacity based on temperature and altitude
Nucleation: Vapor condenses on solid surfaces or forms droplets in free air
Matter Conversion: Excess vapor converts to liquid water
Heat Release: Condensation is exothermic, releases heat to surroundings
float saturationCapacity = calculateSaturation(altitude, heat);
if (vaporMatter > saturationCapacity) {
float condensed = vaporMatter - saturationCapacity;
vaporMatter -= condensed;
waterMatter += condensed;
heat += condensed * HEAT_PER_CONDENSATION; // Warming effect
}
Cloud Formation: High concentrations of vapor at altitude create visible cloud layers (rendered via vaporMatter density).
Phase 5: Precipitation (Rain)¶
Trigger: Water voxels at high altitude with no solid support below fall as rain.
Conditions:
waterMatter > 0at altitudey > RAIN_THRESHOLDVoxel below is empty or also water (no solid support)
Sufficient accumulation (prevents single-drop rain)
Process:
Rain Queue:
iterateTerrainTiles()identifies falling water, adds towaterFallingQueue_Falling Physics:
processWaterFalling()applies:Vertical Acceleration: Water accelerates downward at
gravityrateTerminal Velocity: Capped by air resistance
Collision Detection: Checks each voxel during fall path
Splashing: On impact with solid terrain or water body:
Transfers
waterMatterto impact locationLateral splash if impact velocity high
Kinetic energy converts to heat (impact warming)
Rain Animation:
// Rain falling process
for (auto rainEvent : waterFallingQueue_) {
Coord current = rainEvent.position;
float velocity = rainEvent.velocity;
while (current.y > 0) {
Coord below = current.offset(0, -1, 0);
if (storage.isOccupied(below)) {
// Impact!
handleSplash(below, rainEvent.waterAmount, velocity);
break;
}
// Continue falling
current = below;
velocity = std::min(velocity + gravity, TERMINAL_VELOCITY);
}
}
Weather Patterns: Combination of evaporation (ocean/lakes), vapor transport (wind), condensation (altitude/cooling), and precipitation (rain) creates emergent weather systems.
Water Cycle State Diagram¶
The complete water cycle state machine with all transitions, conditions, and attributes:
![digraph water_cycle_states {
rankdir=LR;
node [shape=box, style="rounded,filled"];
edge [fontsize=8];
// States with attributes
Terrain [label="Solid Terrain\n\nAttributes:\n• terrainMatter > 0\n• mainType = TERRAIN\n• subType = solid\n• heat (float)\n• position (x,y,z)\n\nInvariants:\n• Cannot move\n• Blocks flow\n• Condensation nuclei",
fillcolor="#8B7355", fontcolor=white, style="rounded,filled"];
WaterStatic [label="Liquid Water (Static)\n\nAttributes:\n• waterMatter > 0\n• heat (float)\n• pressure (calculated)\n• velocity ≈ 0\n\nConditions:\n• Has support below OR\n at equilibrium\n• y ≤ RAIN_THRESHOLD",
fillcolor="#4682B4", fontcolor=white, style="rounded,filled"];
WaterFlowing [label="Liquid Water (Flowing)\n\nAttributes:\n• waterMatter > 0\n• pressure gradient\n• flow_direction (x,y,z)\n• flow_rate (float)\n\nBehavior:\n• Downward priority\n• Lateral equilibrium\n• Rate-limited",
fillcolor="#1E90FF", fontcolor=white, style="rounded,filled"];
WaterFalling [label="Liquid Water (Falling)\n\nAttributes:\n• waterMatter > 0\n• velocity (increasing)\n• acceleration = gravity\n• terminal_velocity (max)\n\nPhysics:\n• velocity += gravity\n• Collision detection\n• Kinetic energy",
fillcolor="#00BFFF", fontcolor=white, style="rounded,filled"];
VaporDispersed [label="Water Vapor (Dispersed)\n\nAttributes:\n• vaporMatter > 0\n• concentration (float)\n• heat (float)\n• altitude (y)\n\nConditions:\n• vaporMatter < saturation\n• No nucleation\n\nBehavior:\n• Diffusion\n• Cannot enter solids",
fillcolor="#E0FFFF", fontcolor=black, style="rounded,filled"];
VaporRising [label="Water Vapor (Rising)\n\nAttributes:\n• vaporMatter > 0\n• vertical_bias (upward)\n• buoyancy (float)\n• altitude (increasing)\n\nBehavior:\n• Accumulates at height\n• Forms cloud layers",
fillcolor="#B0E0E6", fontcolor=black, style="rounded,filled"];
VaporSaturated [label="Water Vapor (Saturated)\n\nAttributes:\n• vaporMatter > saturation\n• excess = vapor - capacity\n• saturation = f(altitude, heat)\n\nConditions:\n• Ready to condense\n• Nuclei present",
fillcolor="#87CEEB", fontcolor=black, style="rounded,filled"];
// State transitions
// Water flow
WaterStatic -> WaterFlowing [label="Pressure Gradient\n\nTrigger:\n• Neighbor pressure diff\n• No solid obstacle\n\nRate: ∝ pressure_diff", color="#1E90FF", fontcolor="#1E90FF"];
WaterFlowing -> WaterStatic [label="Equilibrium Reached\n\nCondition:\n• Pressure balanced\n• Flow complete", color="#4682B4", fontcolor="#4682B4"];
// Evaporation
WaterStatic -> VaporDispersed [label="EVAPORATION\n\nConditions (ALL):\n• heat ≥ 120.0\n• waterMatter ≥ 120,000\n• Exposed to air\n\nProcess:\n• waterMatter -= rate (8.0)\n• vaporMatter += rate\n• heat -= rate × HEAT_EVAP\n\nEffect: Endothermic (cooling)",
color="#FF6347", fontsize=9, penwidth=2];
WaterFlowing -> VaporDispersed [label="EVAPORATION", color="#FF6347", style=dashed];
// Vapor diffusion
VaporDispersed -> VaporDispersed [label="DIFFUSION\n\nProcess:\n• Spread to neighbors\n• Follow gradient\n• Rate: DIFFUSION_RATE\n\nConservation:\n• Total vapor constant",
color="#87CEEB", style=dotted];
// Vapor rise
VaporDispersed -> VaporRising [label="Buoyancy\n\nTrigger:\n• Heat buoyancy\n• Space above\n\nEffect:\n• Altitude increase\n• Cloud formation", color="#4169E1"];
// Saturation
VaporRising -> VaporSaturated [label="Altitude/Cooling\n\nCondition:\n• vapor > saturation OR\n• heat < threshold\n\nSaturation ∝ 1/altitude", color="#4682B4"];
VaporDispersed -> VaporSaturated [label="Cooling", color="#4682B4", style=dashed];
// Condensation
VaporSaturated -> WaterStatic [label="CONDENSATION\n\nConditions:\n• vapor > capacity\n• Nuclei present\n\nProcess:\n• vaporMatter -= excess\n• waterMatter += excess\n• heat += excess × HEAT_COND\n\nEffect: Exothermic (warming)",
color="#32CD32", fontsize=9, penwidth=2];
// Rain formation
WaterStatic -> WaterFalling [label="Rain Formation\n\nConditions (ALL):\n• waterMatter > 0\n• y > RAIN_THRESHOLD\n• No support below\n• Sufficient accumulation\n\nQueued: waterFallingQueue_", color="#FF8C00"];
// Rain descent
WaterFalling -> WaterFalling [label="Falling\n\nPhysics:\n• vel += gravity\n• vel ≤ TERMINAL_VEL\n• y -= 1 per tick\n\nCollision: Checked",
color="#FFA500", style=dotted];
// Rain impact
WaterFalling -> WaterStatic [label="IMPACT/SPLASH\n\nTrigger:\n• Hit terrain/water\n• velocity > 0\n\nEffects:\n• Transfer waterMatter\n• KE → heat\n• Lateral splash if high velocity\n\nConservation:\n• Matter preserved\n• Energy → thermal",
color="#8B4513", fontsize=9, penwidth=2];
WaterFalling -> WaterFlowing [label="Impact → Flow", color="#8B4513", style=dashed];
// Terrain interactions
Terrain -> WaterStatic [label="Container/Support", color="#696969", style=dotted];
Terrain -> VaporSaturated [label="Nucleation Site", color="#696969", style=dotted];
// Conservation note
{rank=same; WaterStatic; VaporDispersed;}
{rank=same; WaterFlowing; VaporRising;}
{rank=same; WaterFalling; VaporSaturated;}
}](../../_images/graphviz-59b022fd6903cb45f22f5e2ba4a485bc16cd7e5e.png)
State Machine Properties:
Matter Conservation:
Σ(waterMatter) + Σ(vaporMatter) = CONSTANTacross all transitionsEnergy Conservation: Evaporation (endothermic) ⇔ Condensation (exothermic) + Kinetic energy transfers
Parallelization: Grid box partitioning (32³ voxels) with thread-local OpenVDB accessors
Queue-Based: Evaporation and rain use queues (
evaporationQueue_,waterFallingQueue_) to prevent race conditions
Cycle Feedback Loops¶
The water cycle exhibits several feedback mechanisms:
Positive Feedback:
Evaporative Cooling → Less Evaporation: Evaporation cools water, reducing further evaporation rate
Condensation Warming → More Evaporation: Condensation releases heat, increasing nearby evaporation
Negative Feedback:
Rain Replenishment: Rain refills water bodies, sustaining evaporation sources
Altitude Limit: Water vapor cannot rise indefinitely, limiting cloud height
Energy Conservation: The cycle is thermodynamically consistent:
Evaporation absorbs heat (endothermic)
Condensation releases heat (exothermic)
Net energy transfer from hot regions (surface) to cool regions (altitude)
Matter Conservation: Total water across all phases (liquid + vapor) remains constant:
totalWater = Σ(waterMatter) + Σ(vaporMatter) // Constant per game world
Implementation Notes: The parallel processing architecture (WaterSimulationManager) ensures all phases process efficiently at scale, handling millions of voxels with minimal latency.
Design Patterns¶
The architecture employs several key design patterns to achieve both performance and flexibility:
Hybrid Storage Pattern¶
The system uses a dual-storage approach combining OpenVDB (sparse, disk-friendly) with ECS (dense, CPU-friendly):
Static Data: Terrain attributes that rarely change (type, mass, structure) live in
TerrainStorageOpenVDB gridsDynamic Data: Transient runtime state (velocity, animation, AI state) lives in
entt::registrycomponentsBenefit: Memory efficiency for large worlds while maintaining fast access for active simulation
Lazy Activation Pattern¶
TerrainGridRepository implements “cold storage” optimization:
Inactive Terrain: Stored only in OpenVDB (no ECS entity) - minimal memory footprint
Active Terrain: When terrain gains dynamic behavior (velocity, movement), ECS entity is created automatically
Auto-Deactivation:
tick()method monitors active terrains and deactivates idle onesBenefit: Millions of static voxels consume minimal memory; only active regions pay ECS overhead
Bidirectional Mapping Pattern¶
TerrainGridRepository maintains two hash maps for O(1) lookups in both directions:
byCoord_(robin_map): Coordinate → Entity ID mappingbyEntity_(unordered_map): Entity ID → Coordinate mappingUse Cases: Fast “what entity at (x,y,z)?” and “where is entity E?” queries
Benefit: Enables efficient spatial queries and entity tracking without scanning
Thread-Local Caching Pattern¶
TerrainStorage uses ThreadLocalAccessorCache for OpenVDB access:
// Each thread gets its own accessor (no locking)
class ThreadLocalAccessorCache {
thread_local static std::unordered_map<void*, std::shared_ptr<void>> cache;
};
Benefit: O(1) voxel access without mutex overhead in parallel algorithms
Trade-off: Small per-thread memory cost for massive concurrency gains
Usage: Critical for
EcosystemEngine’s parallel water simulation (32x32x32 grid boxes processed concurrently)
Sparse Storage Pattern¶
OpenVDB’s sparse voxel database (SVD) only stores non-default values:
Empty Space: Consumes near-zero memory (only tree structure metadata)
Active Voxels: Stored in 8³ leaf nodes with compression
Benefit: Worlds with vast empty regions (sky, underground) are memory-efficient
Trade-off: Random access slightly slower than dense arrays; sequential iteration via iterators is optimal
Grid Box Partitioning Pattern¶
EcosystemEngine’s WaterSimulationManager divides the world into cache-friendly chunks:
Grid Boxes: World divided into 32x32x32 voxel regions
Parallel Processing: Each box processed by separate thread with thread-local OpenVDB accessors
Round-Robin Scheduling: Fair task distribution with priority aging to prevent starvation
Benefit: Cache-friendly access patterns, minimal thread contention, scales with core count
Data Flow¶
State Sharing: Both engines read and write to the
VoxelGridandentt::registry.Terrain Access Pattern: - Direct terrain queries use
TerrainGridRepository::getTerrainIdIfExists()for O(1) lookups. - Terrain modifications go through the repository to maintain consistency between EnTT and grid storage. - For moving entities,TerrainGridRepository::moveTerrain()updates both coordinate maps and storage grids.Synchronization:
The
PhysicsEnginetypically runs on the main thread or a dedicated physics thread.The
EcosystemEngineuses a worker thread pool for parallel processing of grid chunks viaGridBoxProcessor.Synchronization is achieved through:
entityGridMutex(std::shared_mutex) for EntityGrid read/write protectionterrainGridLocked_flag for terrain grid stateExplicit locking of TerrainStorage when needed
OpenVDB Integration:
All grids use OpenVDB’s sparse data structure for memory efficiency.
Grid transforms are applied uniformly (1.0-voxel spacing by default).
Iterators (
cbeginValueOn()) efficiently traverse only active voxels.