You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Physics: terrain has no collision — wire Jolt HeightFieldShape into TerrainComponent #428
Terrain renders fully (quadtree LOD, tessellation, splatmaps, streaming) but has no physics collision. Jolt's heightfield shape is explicitly rejected today:
OloEngine/src/OloEngine/Physics3D/JoltShapes.cpp:542 — GetShapeType: HeightField shape type {0} not supported, defaulting to Box
OloEngine/src/OloEngine/Physics3D/JoltBinaryStream.cpp:394 reads a HeightField case, but no shape is ever constructed for terrain.
No terrain-collider component exists under OloEngine/src/OloEngine/Terrain/.
Consequence: the player falls through terrain, NPCs can't walk on it, vehicles can't drive on it.
Why it matters
Genre-blocking for an FPS — collision against the ground is table stakes. This is the single most urgent Tier 1 gap.
Proposed approach
Build JPH::HeightFieldShapeSettings from the terrain heightmap (CPU heights already available via TerrainData::GetHeightAt), matching world scale and tile origin.
Add a TerrainColliderComponent (or extend TerrainComponent) that creates a static Jolt body from the heightfield on scene start / terrain edit. (Remember the cross-binding touch-points if adding a component.)
For streamed terrain (TerrainStreamer), create/destroy heightfield bodies per loaded tile; update the dirty region on sculpt/erosion edits (partial heightfield update).
Resolve JoltShapes.cpp::GetShapeType to handle HeightField for serialization round-trip.
Acceptance criteria
A character controller and a vehicle rest on / drive over terrain without falling through.
Raycasts (AI perception, weapons) hit the terrain surface.
Streamed tiles add/remove collision in step with their visual load/unload.
Sculpt/erosion edits update collision for the affected region.
Tier 1 — architectural. From the "production-ready open-world FPS" gap analysis.
Implemented in PR #468 (branch feature/physics-terrain-heightfield).
What's done
JoltShapes::CreateTerrainHeightFieldShape builds a static JPH::HeightFieldShape from the terrain's CPU height field, mapping worldSize/heightScale/entity scale exactly onto Jolt's offset + scale·(x, sample, z) surface so collision coincides with the rendered mesh. Odd resolutions are padded to Jolt's block-size multiple; bit-depth is adaptive (~2 cm).
ShapeType::HeightField + GetShapeType round-trip resolved (it was defaulting to Box).
JoltScene static terrain-body lifecycle on NON_MOVING; body UserData = entity UUID so raycasts (AI perception / weapons) resolve back to the terrain entity.
TerrainComponent.m_CollisionEnabled (serialized, default on); round-trips through scene YAML + save-game (format v5→v6).
Built GPU-free from CPU heights at OnPhysics3DStart, so it also works headless / on the dedicated server; re-synced on runtime Regenerate.
Evidence (104/104 tests green) — new TerrainCollisionTest.cpp drives the real Scene::OnPhysics3DStart + Jolt step path, headless:
DynamicBodyRestsOnFlatTerrainSurface — a dropped body rests one radius above a flat terrain translated to y=10 (proves no fall-through + transform application).
DynamicBodyRestsOnProceduralSurface — a dropped body's rest height tracks the local procedural surface (proves the shape follows the height field; uses an odd resolution to exercise the padding path).
RaycastHitsTerrainSurface — a downward ray hits the terrain at the expected surface height and resolves to the terrain entity.
All cross-binding coverage guards, save-game round-trips, SceneSerializer, and LuaDrivesTerrainRegeneration still pass.
Acceptance criteria status
✅ A character controller / dynamic body rests on terrain without falling through (single-tile).
✅ Raycasts (AI perception, weapons) hit the terrain surface.
Advanced by #468 (merged 2026-06-29); worktree removed. Static terrain collision delivered: JoltShapes::CreateTerrainHeightFieldShape builds a static JPH::HeightFieldShape body GPU-free from CPU heights at OnPhysics3DStart, ShapeType::HeightField round-trips, TerrainComponent.m_CollisionEnabled persists through scene YAML + save-game, and TerrainCollisionTest proves rest-on-surface + raycast-hit (incl. odd-resolution padding). Two acceptance criteria deferred to a separately-filed follow-up: streamed per-tile collision (TerrainStreamer) and sculpt/erosion dirty-region partial updates (HeightFieldShape::SetHeights). Leaving open pending confirmation those are tracked in the new follow-up issue(s).
Core scope appears resolved by #468 (merged 2026-06-29): feat(physics): wire Jolt HeightFieldShape into terrain collision (#428) — builds JPH::HeightFieldShapeSettings from the terrain heightmap and creates a static collision body (acceptance criteria 1–2). The streamed-tile + sculpt-edit heightfield-update follow-up (acceptance criteria 3–4) is tracked separately in #469. Flagging the core scope for closure.
Activity
coderabbitai commentedon Jun 28, 2026
🔗 Related PRs
#78 - Feature/physics integration [merged]
#225 - Compute exact mesh collider volume and centroid [merged]
#340 - feat(physics): add CollideConnected toggle for joints (
#308item 1) [merged]🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
drsnuggles8 commentedon Jun 29, 2026
Implemented in PR #468 (branch
feature/physics-terrain-heightfield).What's done
JoltShapes::CreateTerrainHeightFieldShapebuilds a staticJPH::HeightFieldShapefrom the terrain's CPU height field, mappingworldSize/heightScale/entity scale exactly onto Jolt'soffset + scale·(x, sample, z)surface so collision coincides with the rendered mesh. Odd resolutions are padded to Jolt's block-size multiple; bit-depth is adaptive (~2 cm).ShapeType::HeightField+GetShapeTyperound-trip resolved (it was defaulting toBox).JoltScenestatic terrain-body lifecycle onNON_MOVING; bodyUserData= entity UUID so raycasts (AI perception / weapons) resolve back to the terrain entity.TerrainComponent.m_CollisionEnabled(serialized, default on); round-trips through scene YAML + save-game (format v5→v6).OnPhysics3DStart, so it also works headless / on the dedicated server; re-synced on runtimeRegenerate.Evidence (104/104 tests green) — new
TerrainCollisionTest.cppdrives the realScene::OnPhysics3DStart+ Jolt step path, headless:DynamicBodyRestsOnFlatTerrainSurface— a dropped body rests one radius above a flat terrain translated to y=10 (proves no fall-through + transform application).DynamicBodyRestsOnProceduralSurface— a dropped body's rest height tracks the local procedural surface (proves the shape follows the height field; uses an odd resolution to exercise the padding path).RaycastHitsTerrainSurface— a downward ray hits the terrain at the expected surface height and resolves to the terrain entity.All cross-binding coverage guards, save-game round-trips,
SceneSerializer, andLuaDrivesTerrainRegenerationstill pass.Acceptance criteria status
Leaving this open for you to close once PR #468 merges.
fix(physics): tear down terrain collision body on removal; harden inp…
Merge pull request #468 from drsnuggles8/feature/physics-terrain-heig…
drsnuggles8 commentedon Jun 29, 2026
Advanced by #468 (merged 2026-06-29); worktree removed. Static terrain collision delivered: JoltShapes::CreateTerrainHeightFieldShape builds a static JPH::HeightFieldShape body GPU-free from CPU heights at OnPhysics3DStart, ShapeType::HeightField round-trips, TerrainComponent.m_CollisionEnabled persists through scene YAML + save-game, and TerrainCollisionTest proves rest-on-surface + raycast-hit (incl. odd-resolution padding). Two acceptance criteria deferred to a separately-filed follow-up: streamed per-tile collision (TerrainStreamer) and sculpt/erosion dirty-region partial updates (HeightFieldShape::SetHeights). Leaving open pending confirmation those are tracked in the new follow-up issue(s).
drsnuggles8 commentedon Jun 30, 2026
Core scope appears resolved by #468 (merged 2026-06-29):
feat(physics): wire Jolt HeightFieldShape into terrain collision (#428)— buildsJPH::HeightFieldShapeSettingsfrom the terrain heightmap and creates a static collision body (acceptance criteria 1–2). The streamed-tile + sculpt-edit heightfield-update follow-up (acceptance criteria 3–4) is tracked separately in #469. Flagging the core scope for closure.