Start of terrain sculpting implementation
This commit is contained in:
@@ -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/<terrainId>/`).
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user