diff --git a/src/features/editScene/TerrainRequirements.md b/src/features/editScene/TerrainRequirements.md index c0d4171..03ae9db 100644 --- a/src/features/editScene/TerrainRequirements.md +++ b/src/features/editScene/TerrainRequirements.md @@ -1217,30 +1217,107 @@ JPH::EActivation::DontActivate)`. Definition of done: ✅ rigid bodies collide flush with visible terrain; debug draw off by default with toggle; removing and re-adding terrain is leak-free. +**Terrain cleanup on entity/component removal** (post-M2 fix): +`TerrainSystem::update()` tracks whether any matching terrain entity exists. +If `m_active` but no entity carries `TerrainComponent`, `deactivate()` is +called. Without this, deleting the entity or removing the component leaves +terrain and colliders orphaned. + ### Milestone 3 — Serialization + heightmap editing + splat painting -- Scene serialization for `TerrainComponent` (save/load JSON round-trip). -**Terrain cleanup on entity/component removal** (added post-M2): -`TerrainSystem::update()` now tracks whether any matching terrain entity exists. -If `m_active` is true but the query returns no entity (because the terrain -entity was deleted or its TerrainComponent was removed), `deactivate()` is -called automatically. Without this, removing the component or deleting the -entity would leave the terrain and colliders orphaned in the scene. -- Binary heightmap file save/load alongside scene JSON. -- Brush tools (raise/lower/smooth/flatten) with curve-based brush profile. -- Splat paint brush (select layer, set opacity, paint blend values via - `TerrainLayerBlendMap`). -- Sparse 256x256 fixup chunk cache and file I/O (lazy creation, sentinel-based - override semantics). -- Mark affected pages dirty and rebuild them via CustomTerrainDefiner - re-sampling. -- Heightmap editor UI (brush radius, strength, type selector). -- `AuxMap` infrastructure (create, load, save, edit with brushes). -- Fixup chunk cleanup (clear all, delete individual chunks by world position). +**Scene serialization**: +- `SceneSerializer::serializeEntity()` / `deserializeEntity()` support for + `TerrainComponent` (save/load JSON round-trip with all parameters, layers, + road config, aux maps). +- Binary heightmap file save/load alongside scene JSON + (`heightmaps//`). -Definition of done: user can sculpt the terrain in the editor; save scene, -reload, terrain restored with edited heightmap and layer paint; visual and -collision update after sculpting. +**Heightmap data manager** (`HeightmapData` class inside `TerrainSystem`): +- Loads/stores the `terrainSize × terrainSize` float array from/to binary + files. +- Provides `getHeightAt(worldX, worldZ)` combining base heightmap + fixup + chunks with bilinear sampling. +- `setHeightAt(worldX, worldZ, value)` writes to base heightmap or a fixup + chunk (permanent base edit vs road compliance fixup). +- `CustomTerrainDefiner` calls `HeightmapData::getHeightAt()` instead of + procedural — falls back to procedural if no file loaded yet. + +**Fixup chunk cache and I/O**: +- Sparse 256×256 fixup chunks, lazy-created, sentinel-based override. +- `loadFixupChunk()` / `saveFixupChunk()` / `clearAllFixups()`. +- "Clear all fixups" button; individual chunk delete by world-position + right-click. + +**3D world point selection and sculpting UX**: + +The TerrainEditor panel gains a **Sculpting** section below the terrain +properties, grouping all brush controls together: + +``` +┌─ Sculpting ─────────────────────────────────────┐ +│ [✓] Sculpt Mode │ +│ Tool: [ Raise ▾ ] Radius: [====] 5.0 │ +│ Strength: [====] 0.5 Curve: [====] │ +│ Layer: [ Layer 1 ▾ ] (splat paint only) │ +└──────────────────────────────────────────────────┘ +``` + +**Sculpt Mode checkbox**: When checked, left-click on the terrain applies the +selected tool. When unchecked, clicks behave normally (entity selection, +camera). ESC also exits sculpt mode. + +**Tool selector**: Combo box — Raise, Lower, Smooth, Flatten, Splat Paint. +Switching the tool immediately changes behavior; no separate "activate" step. + +**Painting**: In sculpt mode, holding left mouse on the terrain continuously +applies the current tool at the brush position (determined by raycast against +terrain via `TerrainSystem::raycastTerrain()`). Releasing the mouse stops +painting. The brush follows the cursor — drag to paint a stroke. A +translucent circle decal on the terrain surface shows brush position and +radius. + +**Camera interaction while sculpting**: +- Right mouse always controls camera (orbit/pan), even in sculpt mode. +- Middle mouse or Ctrl+Left also moves camera in sculpt mode, so the user + can reposition without toggling the mode off. + +**Brush tools**: +- **Raise**: adds height (strength × radius curve) at brush center. +- **Lower**: subtracts height. +- **Smooth**: averages heights within brush radius. +- **Flatten**: pulls heights toward the height under brush center (plateau). +- **Splat Paint**: paints selected layer's blend weight via + `TerrainLayerBlendMap::setBlendValue()` + `dirtyRect()` + `update()`. + Layer selector appears when this tool is active. + +**Brush application and dirty page marking**: +- During a stroke, `HeightmapData::setHeightAt()` is called for each affected + sample within the brush radius, weighted by the radius curve. +- At mouse release (end of stroke), determine which terrain pages overlap the + modified area and mark them dirty. +- `CustomTerrainDefiner` re-samples `HeightmapData::getHeightAtWorld()` on the + next `define()` call for dirty pages. +- If a modified page already has a physics collider, queue its removal and + recreation with updated heights. + + +**Heightmap file I/O and AuxMap controls**: +- "Save Heightmap" / "Load Heightmap" buttons in the TerrainEditor panel. +- AuxMap management: create, rename, delete, select active map for editing. + AuxMap editing uses the same brush tools but writes to the selected aux map + instead of the base heightmap. +- "Clear All Fixups" button — deletes all fixup chunks and marks affected + pages dirty for rebuild. +**Camera pivot above terrain** (on-demand, not automatic): +- "Snap Camera Above Terrain" button or hotkey in TerrainEditor. +- Raycast downward from camera at current XZ, get terrain height, place camera + at `(cam.x, terrainHeight + 5.0, cam.z)`. +- Only on user request — never auto-corrects. + +**Definition of done**: user can sculpt terrain with visual brush feedback; +save scene, reload, terrain restored with edited heightmap and layer paint; +visual and collision update after sculpting; camera can be snapped above +terrain on demand. ### Milestone 4 — Procedural roads - Node/edge editing UI (add, remove, split, join, select, move). @@ -1249,8 +1326,8 @@ collision update after sculpting. `Procedural::TriangleBuffer`. - End cap generation for endpoints. - Seam suppression via vertex shifting. -- LOD + visibility distance per road mesh segment. -- Road physics colliders (JPH::MeshShape per segment). +- LOD + visibility distance per road mesh. +- Road physics colliders (JPH::MeshShape per road mesh). - "Comply terrain to roads" writing fixup chunks with smooth falloff. Definition of done: user can draw a road network; road geometry appears with