From 825939db3b69ea82b5039800b35f025c585a3235 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 07:49:48 -0700 Subject: [PATCH] feat(editor): invert heights, fill entire tile with water, remove all water - Invert Heights: flips terrain upside-down around midpoint (mountains become valleys and vice versa) - Fill Entire Tile with Water: one-click fills all 256 chunks at the configured water height and liquid type - Remove ALL Water: clears water from every chunk instantly - Water panel now has three water operations: fill tile, remove under brush, remove all - fillWater() and invertHeights() methods on TerrainEditor --- tools/editor/editor_ui.cpp | 10 ++++++- tools/editor/terrain_editor.cpp | 50 +++++++++++++++++++++++++++++++++ tools/editor/terrain_editor.hpp | 6 ++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index 9622a6c4..78705331 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -1470,13 +1470,21 @@ void EditorUI::renderWaterPanel(EditorApp& app) { app.setWaterType(static_cast(typeIdx)); ImGui::Separator(); + if (ImGui::Button("Fill Entire Tile with Water", ImVec2(-1, 0))) { + app.getTerrainEditor().fillWater(app.getWaterHeight(), app.getWaterType()); + app.showToast("Tile filled with water"); + } if (ImGui::Button("Remove Water Under Brush", ImVec2(-1, 0))) { auto& brush = app.getTerrainEditor().brush(); if (brush.isActive()) { app.getTerrainEditor().removeWater(brush.getPosition(), s.radius); - app.getEditorCamera(); // trigger dirty } } + if (ImGui::Button("Remove ALL Water", ImVec2(-1, 0))) { + for (int ci = 0; ci < 256; ci++) + app.getTerrainEditor().getTerrain()->waterData[ci].layers.clear(); + app.showToast("All water removed"); + } ImGui::Separator(); ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f), "Left-click to place water"); diff --git a/tools/editor/terrain_editor.cpp b/tools/editor/terrain_editor.cpp index 5cc0d803..3c5e80dd 100644 --- a/tools/editor/terrain_editor.cpp +++ b/tools/editor/terrain_editor.cpp @@ -860,6 +860,56 @@ void TerrainEditor::createHill(const glm::vec3& center, float radius, float heig dirty_ = true; } +void TerrainEditor::invertHeights() { + if (!terrain_) return; + // Find midpoint + float minH = 1e30f, maxH = -1e30f; + for (int ci = 0; ci < 256; ci++) { + auto& chunk = terrain_->chunks[ci]; + if (!chunk.hasHeightMap()) continue; + for (int v = 0; v < 145; v++) { + minH = std::min(minH, chunk.heightMap.heights[v]); + maxH = std::max(maxH, chunk.heightMap.heights[v]); + } + } + float mid = (minH + maxH) * 0.5f; + for (int ci = 0; ci < 256; ci++) { + auto& chunk = terrain_->chunks[ci]; + if (!chunk.hasHeightMap()) continue; + for (int v = 0; v < 145; v++) + chunk.heightMap.heights[v] = mid - (chunk.heightMap.heights[v] - mid); + dirtyChunks_.push_back(ci); + } + for (int ci = 0; ci < 256; ci++) stitchEdges(ci); + dirty_ = true; +} + +void TerrainEditor::fillWater(float height, uint16_t liquidType) { + if (!terrain_) return; + for (int ci = 0; ci < 256; ci++) { + auto& water = terrain_->waterData[ci]; + if (water.layers.empty()) { + pipeline::ADTTerrain::WaterLayer wl; + wl.liquidType = liquidType; + wl.flags = 0; + wl.minHeight = height; + wl.maxHeight = height; + wl.x = 0; wl.y = 0; wl.width = 9; wl.height = 9; + wl.heights.assign(81, height); + wl.mask.assign(8, 0xFF); + water.layers.push_back(wl); + } else { + auto& wl = water.layers[0]; + wl.liquidType = liquidType; + wl.minHeight = height; + wl.maxHeight = height; + std::fill(wl.heights.begin(), wl.heights.end(), height); + } + dirtyChunks_.push_back(ci); + } + dirty_ = true; +} + void TerrainEditor::thermalErosion(int iterations, float talusAngle) { if (!terrain_) return; float unitSize = CHUNK_SIZE / 8.0f; diff --git a/tools/editor/terrain_editor.hpp b/tools/editor/terrain_editor.hpp index 4ddf48c6..291552e0 100644 --- a/tools/editor/terrain_editor.hpp +++ b/tools/editor/terrain_editor.hpp @@ -105,6 +105,12 @@ public: // Thermal erosion: material falls downhill based on angle of repose void thermalErosion(int iterations, float talusAngle); + // Invert terrain (flip heights around midpoint) + void invertHeights(); + + // Fill entire tile with water at a height + void fillWater(float height, uint16_t liquidType); + // Import/export heightmap (raw 16-bit grayscale, 129x129) bool importHeightmap(const std::string& path, float heightScale); bool exportHeightmap(const std::string& path, float heightScale);