From d59d69b0c5c2d8f10975c60c7dd56f6cbd7c881f Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 05:51:03 -0700 Subject: [PATCH] feat(editor): height clamp tool for controlled terrain range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clamp Heights: sets min/max height bounds across entire tile (DragFloatRange2 slider for min/max, -500 to 2000 range) - Useful workflow: Generate noise → Smooth → Clamp to desired range - Prevents terrain from going underground or too high - All affected chunks marked dirty for mesh regeneration --- tools/editor/editor_ui.cpp | 9 ++++++++- tools/editor/terrain_editor.cpp | 21 +++++++++++++++++++++ tools/editor/terrain_editor.hpp | 3 +++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index 87802e90..fb29ff63 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -392,8 +392,15 @@ void EditorUI::renderBrushPanel(EditorApp& app) { app.getTerrainEditor().smoothEntireTile(smoothPasses); app.showToast("Tile smoothed"); } + ImGui::Separator(); + static float clampMin = 0.0f, clampMax = 500.0f; + ImGui::DragFloatRange2("Clamp Range", &clampMin, &clampMax, 1.0f, -500.0f, 2000.0f); + if (ImGui::Button("Clamp Heights", ImVec2(-1, 0))) { + app.getTerrainEditor().clampHeights(clampMin, clampMax); + app.showToast("Heights clamped"); + } ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1), - "Generate terrain, then smooth for natural look"); + "Generate, smooth, then clamp for controlled range"); } ImGui::Separator(); diff --git a/tools/editor/terrain_editor.cpp b/tools/editor/terrain_editor.cpp index 05b17092..06aaf134 100644 --- a/tools/editor/terrain_editor.cpp +++ b/tools/editor/terrain_editor.cpp @@ -652,6 +652,27 @@ void TerrainEditor::smoothEntireTile(int iterations) { dirty_ = true; } +void TerrainEditor::clampHeights(float minH, float maxH) { + if (!terrain_) return; + for (int ci = 0; ci < 256; ci++) { + auto& chunk = terrain_->chunks[ci]; + if (!chunk.hasHeightMap()) continue; + bool modified = false; + for (int v = 0; v < 145; v++) { + float absH = chunk.position[2] + chunk.heightMap.heights[v]; + if (absH < minH) { + chunk.heightMap.heights[v] = minH - chunk.position[2]; + modified = true; + } else if (absH > maxH) { + chunk.heightMap.heights[v] = maxH - chunk.position[2]; + modified = true; + } + } + if (modified) dirtyChunks_.push_back(ci); + } + dirty_ = true; +} + void TerrainEditor::applyErode(float dt) { float factor = std::min(1.0f, brush_.settings().strength * dt * 0.3f); diff --git a/tools/editor/terrain_editor.hpp b/tools/editor/terrain_editor.hpp index b3b5dfbb..ed80bee8 100644 --- a/tools/editor/terrain_editor.hpp +++ b/tools/editor/terrain_editor.hpp @@ -57,6 +57,9 @@ public: // Global smooth pass across entire tile (N iterations) void smoothEntireTile(int iterations); + // Clamp all heights to a min/max range + void clampHeights(float minH, float maxH); + // Import/export heightmap (raw 16-bit grayscale, 129x129) bool importHeightmap(const std::string& path, float heightScale); bool exportHeightmap(const std::string& path, float heightScale);