From 8c20f732cec5e3bf0160b3e95378b07e3846731e Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 7 May 2026 12:15:45 -0700 Subject: [PATCH] fix(editor): random-populate ground-snaps spawns to actual terrain Previously every random-populated creature and object dropped at baseZ (the manifest's flat height), which sat below the carved terrain on hills and floated above it in valleys. After generateCompleteZone the terrain is far from flat, so the spawns showed up clipping or floating. Added an inline groundZ(x, y) helper that casts a downward ray from baseZ+500y at the spawn's (x, y) and uses the hit position's Z. Falls back to baseZ if the ray misses terrain (shouldn't happen inside the loaded tile bbox but is safer than NaN). Both creature and object loops now consult groundZ. Snap-to-ground is the runtime placer's default behavior anyway, so this lines up with the rest of the editor's expectations. --- tools/editor/editor_app.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index 88bf78c8..dacc143b 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -1738,6 +1738,18 @@ void EditorApp::randomPopulateZone(int creatureCount, int objectCount, "World/Generic/Stump.wmo", "World/Generic/Mushroom.wmo", }; + // Ground-snap helper: cast a downward ray from far above the (x,y) + // position to find the actual terrain height. Without this, spawns + // sit at baseZ which can be metres below or above the carved + // terrain after generation. + auto groundZ = [&](float x, float y) -> float { + rendering::Ray ray; + ray.origin = glm::vec3(x, y, baseZ + 500.0f); + ray.direction = glm::vec3(0, 0, -1); + glm::vec3 hit; + if (terrainEditor_.raycastTerrain(ray, hit)) return hit.z; + return baseZ; // fall back to base if the ray misses + }; int placedCreatures = 0, placedObjects = 0; for (int n = 0; n < creatureCount; ++n) { const auto& [name, baseLvl] = kRandomCreatures[ @@ -1746,7 +1758,7 @@ void EditorApp::randomPopulateZone(int creatureCount, int objectCount, s.name = name; s.position.x = rangeF(wMinX, wMaxX); s.position.y = rangeF(wMinY, wMaxY); - s.position.z = baseZ; + s.position.z = groundZ(s.position.x, s.position.y); int lvl = std::max(1, static_cast(baseLvl) + rangeI(-1, 2)); s.level = static_cast(lvl); s.health = 50 + s.level * 10; @@ -1764,7 +1776,7 @@ void EditorApp::randomPopulateZone(int creatureCount, int objectCount, o.type = PlaceableType::WMO; o.position.x = rangeF(wMinX, wMaxX); o.position.y = rangeF(wMinY, wMaxY); - o.position.z = baseZ; + o.position.z = groundZ(o.position.x, o.position.y); o.rotation = glm::vec3(0.0f, rangeF(0.0f, 6.28f), 0.0f); o.scale = rangeF(0.8f, 1.4f); o.uniqueId = ++maxUid;