# Structure ## Modules ### `src/entity.rs` Manages entity lifecycle with `EntityHandle` (u64 alias) and `EntityManager` tracking alive entities. Entities are opaque IDs; all data lives in component storages on `World`. ### `src/world.rs` Core ECS container holding `EntityManager`, 20+ typed `Storage` collections (one per component type), and intent queues (`follow_player_intents`, `stop_following_intents`, `camera_transition_intents`). One-frame intents are inserted and consumed within a single frame. World also holds singleton state: `snow_layer`, `debug_mode`, `gizmo_mesh`. ### `src/components/` Component types define entity data. Key storages: - **Transforms** (`Transform`): position, rotation, scale - **Physics** (`PhysicsComponent`): rapier3d rigidbody/collider handles - **Movement** (`MovementComponent`): walking speed, acceleration, damping state - **Jump** (`JumpComponent`): jump height, air control, jump curve - **State machines** (stored per-entity): `IdleState`, `WalkingState`, `JumpingState`, `FallingState`, `LeapingState`, `RollingState` - **Input** (`InputComponent`): current move direction, jump/parry key states - **Camera** (`CameraComponent`): FOV, aspect, yaw/pitch; `CameraTransition` for animated transitions - **Dialog** (`DialogBubbleComponent`, `DialogProjectileComponent`, `DialogSourceComponent`): Ink story state, projectile tracking, outcome events - **Rendering** (`MeshComponent`): mesh reference, pipeline, instance buffer, dissolve/snow-light flags - **Misc**: `FollowComponent`, `RotateComponent`, `DissolveComponent`, `TriggerComponent`, `ParticleEmitterConfig` ### `src/states/state.rs` Per-entity state machine: `StateMachine` holds current `TypeId`, registered state types, and transitions. `State` trait defines lifecycle (`on_enter`, `on_physics_update`, `on_exit`). Transitions are condition predicates checked each update. Player entity uses this for locomotion states. ### `src/systems/` Flat list of update functions called in main loop. No cross-system coupling; all communication via world state and intents. - **Camera**: `camera_input_system` → generates intents; `camera_intent_system` consumes them; `camera_follow_system` (follows player), `camera_noclip_system`, `camera_transition_system`, `camera_ground_clamp_system` - **Input**: `player_input_system` reads SDL3 `InputState`, writes `InputComponent` - **Physics**: `state_machine_physics_system` (fixed-step), `PhysicsManager::physics_step()`, `physics_sync_system` (copies rapier bodies back to transforms), `trigger_system` (AABB overlap → events) - **Dialog**: `dialog_system` (ticks story state), `dialog_projectile_system` (moves projectiles), `dialog_camera_system` (focuses camera on speaker), `dialog_bubble_render_system` (generates billboard/text draw calls) - **Rendering**: `render_system` collects `DrawCall` from meshes/transforms; `spotlight_sync_system` syncs light positions to shader uniform - **State machine**: `state_machine_system` (per-frame), `state_machine_physics_system` (fixed-step) tick state lifecycle - **Trees**: `tree_occlusion_system` (culls trees behind camera), `tree_dissolve_update_system` (animates dissolve), `tree_instance_buffer_update_system` (writes GPU buffer) - **Snow**: `snow_system` deforms snow layer at physics contacts; `particle_intent_system`/`particle_update_system` manage particle emitters - **Rotate**: `rotate_system` rotates entities by delta ### `src/bundles/` Factory functions to spawn pre-configured entity groups: - `PlayerBundle`: player character with all locomotion components - `TestCharBundle`: test NPC - `CameraBundle`: camera entity - `TerrainBundle`: terrain mesh + collider - `SpotlightBundle` + `spawn_spotlights()`: light entities from scene data ### `src/render/` GPU rendering pipeline via wgpu. Singleton `Renderer` stored in thread-local (global.rs). - **`Renderer`**: wgpu device/queue/surface, framebuffer, pipelines, bind groups, shadow map texture, spotlight data - **`DrawCall`** (types.rs): vertex/index buffers, model matrix, pipeline enum, instance buffer, entity ref - **Pipelines** (pipeline.rs): `create_main_pipeline()`, `create_snow_clipmap_pipeline()`, `create_wireframe_pipeline()`, `create_shadow_pipeline()`, `create_debug_lines_pipeline()` - **`Uniforms`** (types.rs): model/view/projection, 4 spotlights, camera position, height scale, player position, time, tile scale, debug mode - **Shadow**: `render_shadow_pass()` renders scene depth to shadow map from each spotlight - **Snow**: `SnowLayer` deforms snow heightfield via compute; `ClipmapConfig` manages multi-level clipmap grid; `deform_at_position()` marks terrain changed - **Snow light**: `SnowLightAccumulation` ping-pong texture accumulates light contributions from spotlights onto snow surface - **Billboard pipeline** + **Text pipeline**: render dialog bubbles and text overlays - **Particle pipeline**: billboarded particles with per-instance color/velocity - **Font atlas**: pre-rasterized glyph texture from embedded font file ### `src/loaders/` Load scene data from glTF files: - **`scene.rs`** `Space::load_space()`: loads meshes, lights, player spawn, test char spawn from single glTF - **`mesh.rs`** `Mesh::load_gltf_with_instances()`: parses glTF buffers, vertex/index data, multi-instance mesh batches; `InstanceData` (position/rotation/scale/dissolve); `Vertex` (position/normal/UV) - **`lights.rs`**: extracts spotlight transforms/params from glTF nodes - **`empty.rs`**: extracts named empty (spawn point) transforms from glTF - **`heightmap.rs`**: loads EXR heightfield texture for terrain collision - **`terrain.rs`**: builds rapier heightfield collider from EXR matrix ### `src/physics.rs` Thread-local `PhysicsManager` wrapping rapier3d. `physics_step()` runs one integration step; `add_rigidbody()`, `add_collider()` register bodies; `raycast()` queries. `HeightfieldData` caches terrain height matrix. ### `src/utility/` - **`transform.rs`** `Transform`: matrix conversions to/from nalgebra `Isometry3`; getters/setters for position/rotation/scale - **`input.rs`** `InputState`: SDL3 key states (WASD, Space, Shift, Ctrl) and mouse delta; `handle_event()` updates state; `clear_just_pressed()` resets one-frame flags - **`time.rs`**: `Time::get_time_elapsed()` returns seconds since init (static Instant) ### `src/debug/` - **`mode.rs`** `DebugMode` enum: None, Normals, UV, Depth, Wireframe, Colliders, ShadowMap, SnowLight; `cycle()` steps through - **`collider_debug.rs`**: renders rapier collider AABBs as line meshes - **`gizmo.rs`**: renders 3D transform gizmo (position/rotation/scale) for editor ### `src/editor/` - **`inspector.rs`** `Inspector`: wraps Dear ImGui context; `render()` draws frame to texture; `build_ui()` draws entity inspector panels - **`mod.rs`** `EditorState`: manages editor active state, selected entity, mouse capture; `editor_loop()` calls inspector, handles picking ### `src/picking.rs`, `src/postprocess.rs`, `src/texture.rs`, `src/paths.rs` Utility modules: ray casting for mouse pick, fullscreen blit/framebuffer downsampling, dither/flowmap texture loading, paths to asset files. ## Data Flow 1. **Initialization** (`main.rs` `init()`): SDL3 window → wgpu renderer (Vulkan) → World creation → load scene (Space from glTF) → spawn bundles (player, terrain, camera, lights) → initialize physics 2. **Main loop** (`main.rs` `main()` with 60 Hz fixed physics + variable-rate graphics): - **Per-frame** (delta): Input events → `player_input_system` (fills `InputComponent`) + `camera_input_system` (generates intents) - **Intent processing**: `camera_intent_system` consumes intents, updates `CameraComponent`/`CameraTransition` - **Camera systems**: follow player, noclip, transition, clamp to ground - **Fixed physics** (1/60s accumulator): - `state_machine_physics_system`: tick state's `on_physics_update()` - `PhysicsManager::physics_step()`: rapier integration - `physics_sync_system`: copy rigidbody poses to `Transform` - `trigger_system`: detect collisions, emit events - `dialog_system`, `dialog_projectile_system`: Ink story tick, projectile movement - **Per-frame systems**: `state_machine_system` (non-physics update), rotate, particle, tree dissolve, snow deformation, spotlight sync - **Render collection**: `render_system` → `Vec` from all meshes; snow layer adds clipmap draw calls; debug adds collider/gizmo calls - **Submission**: `submit_frame()` renders draw calls, dialog bubbles/text, particles to framebuffer; blit to screen; optional ImGui overlay 3. **Frame cleanup**: `InputState::clear_just_pressed()` (flags reset for next frame) ## Key Types | Type | Module | Description | |------|--------|-------------| | `EntityHandle` | entity | u64 opaque entity ID | | `Storage` | world | HashMap storage for per-entity component data | | `World` | world | ECS container: entities, 20+ storages, intent queues, singleton state | | `Transform` | utility/transform | Position (Vec3), rotation (Quat), scale (Vec3); matrix conversions | | `StateMachine` | states/state | Per-entity state machine: current state TypeId, registered states, transitions | | `State` trait | states/state | Lifecycle: `on_enter`, `on_physics_update`, `on_exit`, `on_update` | | `PhysicsComponent` | components/physics | Rapier3d rigidbody + optional collider handles | | `MovementComponent` | components/movement | Walking speed, acceleration, damping, context (floored, last floor time) | | `JumpComponent` | components/jump | Jump height, duration, air control, context (in progress, origin height) | | `CameraComponent` | components/camera | FOV, aspect, yaw/pitch angles, is_active flag | | `InputComponent` | components/input | Current frame: move direction, jump/parry key states (flags) | | `MeshComponent` | components/mesh | Mesh ref, pipeline enum, instance buffer, dissolve/snow-light flags | | `DialogBubbleComponent` | components/dialog | Ink story, current text, dialog phase (displaying/projectile in flight), parry button | | `DrawCall` | render/types | GPU command: vertex/index buffers, model matrix, pipeline, instance count, entity ID | | `Uniforms` | render/types | Per-frame shader data: matrices, spotlight array, debug flags | | `Renderer` | render/mod | GPU state: device, queue, surface, all pipelines, framebuffer, texture samplers | | `Space` | loaders/scene | Loaded scene: mesh batches, spotlight data, spawn positions | | `Mesh` | loaders/mesh | GPU vertex/index buffers, AABB, CPU vertex data for physics | | `SnowLayer` | render/snow | Snow heightfield: deform bind groups, depth texture, clipmap grid levels | | `SnowLightAccumulation` | render/snow_light | Ping-pong textures + pipeline accumulating spotlight contributions onto snow | | `InputState` | utility/input | SDL3 event state: key flags, mouse delta, relative mode | | `PhysicsManager` | physics | Rapier3d bodies/colliders/pipeline; thread-local singleton | | `DebugMode` | debug/mode | Enum: None, Normals, UV, Depth, Wireframe, Colliders, ShadowMap, SnowLight | ## Entry Points 1. **`main()` in src/main.rs** - Calls `init()` → initializes SDL3, wgpu, loads world from scene glTF, spawns entities - Runs infinite loop: event poll → systems (camera/input/physics/render) → frame submit → sleep to 60 Hz 2. **`Game::init()` in src/main.rs** - SDL3 window + Vulkan surface - `Renderer::new()` initializes all GPU pipelines and textures - `init_world()` loads space, spawns bundles, sets up terrain/snow/lights 3. **`World::new()` in src/world.rs** - Creates empty storages for all 20+ component types and intent queues ## Dependencies - **Engine**: wgpu (GPU), rapier3d (physics), glam (math), nalgebra (nalgebra for physics conversions), SDL3 (windowing/input) - **Content**: bladeink (Ink story scripting), image crate (EXR heightmaps), gltf (scene loading) - **Editor**: Dear ImGui via imgui crate + SDL3 integration - **Internal**: All systems read/write `World`; systems are called in fixed sequence from main loop; no circular dependencies - **Thread-local singletons**: `Renderer` (render/global.rs), `PhysicsManager` (physics.rs), `GLOBAL_RENDERER`, `GLOBAL_PHYSICS` ## Initialization Order 1. SDL3 init → window creation → Vulkan adapter/device 2. `Renderer::new()` → wgpu device/queue, create all pipelines, load textures (dither, flowmap, font atlas, shadow map, blue noise) 3. Load scene from glTF → meshes, lights, spawns 4. Spawn bundles: player (with input/movement/jump/state machine), terrain (mesh + heightfield collider), camera (follows player), lights (spotlights) 5. Initialize snow layer (deform by tree positions) 6. Initialize snow light accumulation (bind to spotlight data) 7. Main loop: process events → run systems in sequence → submit frame