# Snow Trail SDL Project

## Model Usage Guide

**When interacting with this codebase, I follow a step-by-step, concise approach:**

1. **Start with exploration**: Read files to understand context before making changes
2. **Build incrementally**: Make small, targeted changes and verify them
3. **Test after changes**: Run `cargo check` and relevant tests
4. **Keep explanations brief**: Code should speak for itself; comments only for complex logic
5. **Follow existing patterns**: Mimic the style, structure, and conventions in the codebase

**For task management**, I use the todo list to track multi-step work:
- Mark tasks as `in_progress` when starting
- Mark tasks as `completed` immediately after finishing
- Add new tasks when scope expands
- Never batch multiple completions

## Project Overview

This is a pure Rust game engine implementation (not a game yet) that serves as a migration from a Godot-based project. It's a 3D game using SDL3 for windowing/input, wgpu for rendering, rapier3d for physics, and features a low-res retro aesthetic with dithering.

The project implements an ECS (Entity Component System) architecture without engine dependencies, providing core systems for rendering, physics, input handling, and entity management.

### Key Technologies
- **SDL3**: Windowing and input handling (latest stable bindings)
- **wgpu**: Modern GPU API abstraction for rendering (Vulkan/Metal/DX12/OpenGL backends)
- **rapier3d**: Fast 3D physics engine
- **glam**: Fast vector/matrix math library
- **gltf**: Loading 3D models in glTF format
- **exr**: Loading EXR heightmap files for physics colliders
- **kurbo**: Bezier curve evaluation for movement acceleration curves

### Architecture: Pure ECS
- **Entities**: Just IDs (`EntityHandle = u64`), managed by `EntityManager`
- **Components**: Pure data structures stored in component storages (HashMap)
- **Systems**: Functions that query entities with specific component combinations
- **No Rc<RefCell<>>** - Clean ownership model with components as data in hashmaps
- **Component Storages** owned by single `World` struct

## Building and Running

### Build Commands
```bash
cargo build
cargo build --release
cargo check
cargo test
cargo run
cargo fmt
```

### Shader Compilation
The project uses a custom shader compilation system via the `wesl` crate:
- WGSL/WESL shaders are compiled at build time via `build.rs`
- Shaders are loaded at runtime via `std::fs::read_to_string()`, allowing hot-reloading by restarting the application
- Build artifact: `standard` shader package

### Runtime Behavior
- Window resolution: 800×600 (resizable)
- Rendering resolution: Low-res framebuffer (160×120) upscaled to window
- Target FPS: 60 FPS with fixed timestep physics (1/60s)
- Default mode: Noclip camera active
- Toggle modes: Press 'N' to toggle noclip/follow modes

## Development Conventions

### Code Style (from CLAUDE.md)
- **NO inline comments unless ABSOLUTELY necessary** - Code must be self-documenting
- **Doc comments (`///`)** only for public APIs and complex algorithms
- **All `use` statements must be at the file level** (module top), not inside function bodies
- **NO inline paths** - always add `use` statements at the top of files
- **Formatting**: `brace_style = "AlwaysNextLine"`, `control_brace_style = "AlwaysNextLine"`

### File Structure
```
src/
├── main.rs              - SDL3 event loop, game loop orchestration, system execution order
├── entity.rs            - EntityManager for entity lifecycle (spawn/despawn/query)
├── world.rs             - World struct owning all component storages
├── camera.rs            - 3D camera with rotation and follow behavior
├── physics.rs           - PhysicsManager singleton (rapier3d world)
├── player.rs            - Player entity spawning function
├── terrain.rs           - Terrain entity spawning, glTF loading, EXR heightmap loading
├── render.rs            - wgpu renderer, pipelines, bind groups, DrawCall execution
├── postprocess.rs       - Low-res framebuffer and blit shader for upscaling
├── mesh.rs              - Vertex/Mesh structs, plane/cube mesh generation, glTF loading
├── shader.rs            - Standard mesh shader (WGSL) with diffuse+ambient lighting
├── state.rs             - Generic StateMachine implementation
├── event.rs             - Type-safe event bus (complementary to ECS)
├── picking.rs           - Ray casting for mouse picking (unused currently)
├── heightmap.rs         - EXR heightmap loading utilities
├── draw.rs              - DrawManager (legacy, kept for compatibility)
├── texture_loader.rs    - Texture loading utilities
└── systems/             - ECS systems (input, state_machine, physics_sync, render, camera)
    ├── input.rs
    ├── state_machine.rs
    ├── physics_sync.rs
    ├── render.rs
    ├── camera_follow.rs
    ├── camera_input.rs
    └── camera_noclip.rs
├── components/          - ECS component definitions
    ├── input.rs
    ├── mesh.rs
    ├── movement.rs
    ├── physics.rs
    ├── player_tag.rs
    ├── jump.rs
    ├── camera.rs
    └── camera_follow.rs
├── utility/             - Utility modules
    ├── input.rs         - InputState (raw SDL input handling)
    ├── time.rs          - Time singleton (game time tracking)
    └── transform.rs     - Transform struct (position/rotation/scale data type)
├── debug/               - Debug utilities
    ├── noclip.rs        - Noclip camera controller
    └── render_collider_debug.rs
└── shaders/             - WGSL/WESL shader files
    ├── shared.wesl      - Shared shader utilities
    ├── standard.wesl    - Standard mesh rendering with directional lighting
    ├── terrain.wesl     - Terrain rendering with shadow mapping
    └── blit.wgsl        - Fullscreen blit for upscaling low-res framebuffer
```

### System Execution Order (main.rs game loop)
1. **SDL Events → InputState**: Poll events, handle raw input
2. **InputState → InputComponent**: `player_input_system()` converts raw input to gameplay commands
3. **State Machine Update**: `state_machine_physics_system()` and `state_machine_system()`
4. **Physics Simulation**: Fixed timestep physics step
5. **Physics → Transforms**: `physics_sync_system()` syncs physics bodies to transforms
6. **Rendering**: `render_system()` generates DrawCalls, renderer executes pipeline
7. **Cleanup**: Clear just-pressed states

### ECS Component Storages
All storages are owned by the `World` struct:
- `TransformStorage` - Position, rotation, scale
- `MeshStorage` - Mesh data + render pipeline
- `PhysicsStorage` - Rapier3d rigidbody/collider handles
- `MovementStorage` - Movement config + state
- `JumpStorage` - Jump mechanics state
- `InputStorage` - Gameplay input commands
- `PlayerTagStorage` - Marker for player entities
- `StateMachineStorage` - Behavior state machines
- `CameraStorage` - Camera components
- `CameraFollowStorage` - Camera follow behavior

### Input Handling (Two-Layer Pipeline)
**Layer 1: Raw Input** (`utility/input.rs` - `InputState`):
- Global singleton for SDL event handling
- Tracks raw hardware state (W/A/S/D pressed, mouse delta, etc.)
- Handles SDL events via `handle_event()` method

**Layer 2: Gameplay Commands** (`components/input.rs` - `InputComponent`):
- Per-entity ECS component
- Stores processed gameplay commands (move_direction, jump_pressed)
- Filled by `player_input_system()` which reads `InputState`

**Input Flow**: SDL Events → InputState → InputComponent → Movement Systems

**Current Controls**:
- `W/A/S/D`: Movement
- `Space`: Jump
- `Shift`: Speed boost (noclip mode)
- `I`: Toggle mouse capture
- `Escape`: Quit game
- `N`: Toggle noclip/follow mode
- Mouse motion: Camera look (yaw/pitch)

### Rendering Pipeline
- **Low-res framebuffer** (160×120) renders to texture
- **Bayer 8×8 dithering** reduces colors to 3-bit RGB (8 colors)
- **Final blit pass** upscales framebuffer to window using nearest-neighbor sampling
- **Depth buffer** for 3D rendering with proper occlusion
- **Multiple render pipelines**: Standard mesh and terrain pipelines
- **Directional lighting**: Diffuse + ambient (basic Phong model)

### Terrain System
- **glTF mesh** exported from Blender 5.0 with baked height values in vertices
- **EXR heightmap** loaded for physics colliders (single-channel R32Float format)
- **Heightfield collider** created directly from EXR data
- **No runtime displacement** - vertices rendered directly
- **Separate terrain pipeline** for terrain-specific rendering

### Build System
- **`build.rs`**: Custom build script using `wesl` crate
- **Shader compilation**: `package::standard` → `standard` artifact
- **No external dependencies** needed for shader compilation at build time

### Dependencies Rationale
- **sdl3**: Modern SDL3 bindings for future-proofing
- **wgpu**: Modern GPU API with cross-platform support
- **rapier3d**: Fast physics engine with good Rust integration
- **gltf**: Standard 3D model format for asset pipeline
- **exr**: High-dynamic-range heightmap loading for physics
- **kurbo**: Bezier curves for smooth movement acceleration
- **bytemuck**: Safe byte casting for GPU buffer uploads

## Current Implementation Status

### Implemented Features
✅ Full ECS architecture (entities, components, systems)
✅ SDL3 windowing and input handling
✅ wgpu rendering with low-res framebuffer
✅ Multiple render pipelines (standard mesh + terrain)
✅ Bayer dithering for retro aesthetic
✅ glTF mesh loading
✅ EXR heightmap loading for physics
✅ rapier3d physics integration
✅ State machine system (generic implementation)
✅ Event bus (complementary to ECS)
✅ Camera system (free look + follow modes)
✅ Noclip mode for development
✅ Two-layer input pipeline

### In Progress
⚠️ Movement system (apply InputComponent → physics velocities)
⚠️ Ground detection and collision response
⚠️ Player state machine configuration (transitions not yet set up)
⚠️ Camera follow behavior (partial implementation)
⚠️ Snow deformation compute shaders
⚠️ Debug UI system

### Known Limitations
- Player entity spawns but is inactive (noclip camera used for testing)
- Movement physics not yet connected to input
- Ground detection not implemented
- State machine transitions not configured
- Camera follow needs refinement

## Content Creation Workflow

### Blender 5.0 (blender/)
- **Terrain modeling**: `terrain.blend`
- **Player character**: `player_mesh.blend`
- **Export formats**:
  - glTF: `meshes/` for rendering (baked heights in vertices)
  - EXR: `textures/` single-channel float heightmap for physics

### GIMP (gimp/)
- **Dither patterns**: `dither_patterns.xcf` (Bayer matrix patterns)

### Export Process
1. Model terrain in Blender 5.0
2. Export as glTF with baked height values
3. Export same terrain as EXR heightmap
4. Both files represent same data (visual/physics sync guaranteed)

## Future Development

### Next Steps (from CLAUDE.md)
1. Implement `movement_system` to apply `InputComponent` to physics velocities
2. Configure player state machine transitions (idle → walking → jumping → falling)
3. Implement ground detection (raycasting with QueryPipeline)
4. Add camera follow system (tracks player entity)
5. Integrate snow deformation compute shaders
6. Implement debug UI system for parameter tweaking

### Testing Strategy
- Systems are pure functions (easy to test)
- Create multiple `World` instances for isolation
- Query patterns are predictable
- State machine transitions are testable

## Technical Notes

### EXR Heightmap Loading (Physics Only)
```rust
use exr::prelude::{ReadChannels, ReadLayers};

let builder = exr::Image::new("heightmap.exr")
    .no_deep_data()
    .largest_resolution_level()
    .all_channels()
    .all_layers()
    .all_attributes();
```

### Component Storage Pattern
```rust
pub struct TransformStorage {
    pub components: HashMap<EntityHandle, Transform>,
}

impl TransformStorage {
    pub fn with_mut<F, R>(&mut self, entity: EntityHandle, f: F) -> Option<R>
    where
        F: FnOnce(&mut Transform) -> R,
    {
        self.components.get_mut(&entity).map(f)
    }
}
```

### State Machine Integration
- TypeId-based state identification
- Transitions as closures (can capture entity ID)
- State callbacks receive `&mut World` for component access
- Safe pattern: Remove → Update → Insert to avoid borrow conflicts

### Event System (Complementary to ECS)
- Handles irregular, one-time occurrences
- Cross-system messaging without tight coupling
- Global `add_listener()` and `emit()` functions
- Example: FootstepEvent for snow deformation

### Shader Hot-Reloading
- Shaders loaded at runtime via `std::fs::read_to_string()`
- Restart application to reload shaders
- No recompilation needed

## Quick Reference

### Running the Project
```bash
cd /home/jonas/projects/snow_trail_sdl
cargo run
```

### Building for Release
```bash
cargo build --release
```

### Formatting Code
```bash
cargo fmt
```

### Toggling Modes at Runtime
- Press **N** to toggle between noclip and follow modes
- Press **I** to toggle mouse capture
- Press **Escape** to quit

### Working with Shaders
- Edit `.wesl` files in `src/shaders/`
- Changes take effect on application restart
- Build script compiles `package::standard` → `standard` artifact

### Adding New Components
1. Define component struct (pure data, no Rc<RefCell>)
2. Add storage to `world.rs` (HashMap<EntityHandle, Component>)
3. Add storage to `World` struct
4. Update `World::despawn()` to clean up component
5. Create systems that query and modify the component

### Adding New Systems
1. Add function in `systems/` directory
2. Import at top of `main.rs`
3. Add to system execution order in game loop
4. Systems receive `&mut World` (or `&World` for read-only)
