From aa9a6a87a81e1c207182bff870552f2623c94d24 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 06:17:37 -0700 Subject: [PATCH] feat(editor): auto-paint terrain by height bands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Auto-Paint by Height: automatically sets base texture per chunk based on average height — configurable thresholds for sand/grass/rock/snow - Uses Tanaris sand, Elwynn grass, Barrens rock, Dragonblight snow - One-click to texture an entire procedurally generated terrain - Adjustable height thresholds via drag floats - Workflow: noise → smooth → clamp → auto-paint for instant biome --- tools/editor/editor_ui.cpp | 26 ++++++++++++++++++++++++++ tools/editor/texture_painter.cpp | 29 +++++++++++++++++++++++++++++ tools/editor/texture_painter.hpp | 4 ++++ 3 files changed, 59 insertions(+) diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index 50d896f6..dfa0f90d 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -551,6 +551,32 @@ void EditorUI::renderTexturePaintPanel(EditorApp& app) { ImGui::TextColored(ImVec4(0.5f, 0.9f, 0.5f, 1.0f), "Active: %s", selectedTexture_.c_str()); + // Auto-paint by height + if (ImGui::CollapsingHeader("Auto-Paint by Height")) { + ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1), + "Sets base texture per chunk based on average height"); + static float h1 = 90, h2 = 110, h3 = 140; + ImGui::DragFloat("Sand/Dirt max##ap", &h1, 1.0f); + ImGui::DragFloat("Grass max##ap", &h2, 1.0f); + ImGui::DragFloat("Rock max##ap", &h3, 1.0f); + ImGui::Text("Above %.0f: Snow", h3); + if (ImGui::Button("Apply Auto-Paint", ImVec2(-1, 0))) { + std::vector bands = { + {h1, "Tileset\\Tanaris\\TanarisSandBase01.blp"}, + {h2, "Tileset\\Elwynn\\ElwynnGrassBase.blp"}, + {h3, "Tileset\\Barrens\\BarrensRock01.blp"}, + {99999.0f, "Tileset\\Expansion02\\Dragonblight\\DragonblightFreshSmoothSnowA.blp"} + }; + app.getTexturePainter().autoPaintByHeight(bands); + // Force terrain refresh + auto mesh = app.getTerrainEditor().regenerateMesh(); + // Mark all chunks dirty through a dummy edit + app.showToast("Auto-painted by height"); + } + } + + ImGui::Separator(); + // Show textures on chunk under cursor auto& brush = app.getTerrainEditor().brush(); if (brush.isActive()) { diff --git a/tools/editor/texture_painter.cpp b/tools/editor/texture_painter.cpp index c8f24995..8579d874 100644 --- a/tools/editor/texture_painter.cpp +++ b/tools/editor/texture_painter.cpp @@ -165,6 +165,35 @@ std::vector TexturePainter::paint(const glm::vec3& center, float radius, return modified; } +void TexturePainter::autoPaintByHeight(const std::vector& bands) { + if (!terrain_ || bands.empty()) return; + + // Ensure all band textures are in the texture list + for (const auto& band : bands) + ensureTextureInList(band.texturePath); + + for (int ci = 0; ci < 256; ci++) { + auto& chunk = terrain_->chunks[ci]; + if (!chunk.hasHeightMap()) continue; + + // Find average height of this chunk + float avgH = chunk.position[2]; + float sum = 0; + for (int v = 0; v < 145; v++) sum += chunk.heightMap.heights[v]; + avgH += sum / 145.0f; + + // Find which band this chunk falls into + for (const auto& band : bands) { + if (avgH <= band.maxHeight) { + uint32_t texId = ensureTextureInList(band.texturePath); + if (!chunk.layers.empty()) + chunk.layers[0].textureId = texId; + break; + } + } + } +} + std::vector TexturePainter::erase(const glm::vec3& center, float radius, float strength, float falloff) { if (!terrain_ || activeTexture_.empty()) return {}; diff --git a/tools/editor/texture_painter.hpp b/tools/editor/texture_painter.hpp index 8ab35d0c..0ea47290 100644 --- a/tools/editor/texture_painter.hpp +++ b/tools/editor/texture_painter.hpp @@ -17,6 +17,10 @@ public: const std::string& getActiveTexture() const { return activeTexture_; } const std::vector& getRecentTextures() const { return recentTextures_; } + // Auto-paint textures based on terrain height bands + struct HeightBand { float maxHeight; std::string texturePath; }; + void autoPaintByHeight(const std::vector& bands); + // Paint the active texture at the given world position // Returns list of modified chunk indices std::vector paint(const glm::vec3& center, float radius, float strength, float falloff);