mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-05 16:43:52 +00:00
feat(editor): auto-paint by slope for natural cliff texturing
- Auto-Paint by Slope: paints rock texture on steep terrain surfaces with configurable slope threshold (0.1 - 0.9) - Uses per-vertex normal Z component to detect steepness - Alpha blending based on slope gradient for smooth transitions - Workflow: sculpt terrain → recalc normals → auto-paint slope → rock appears naturally on cliffs while flat areas keep their biome texture
This commit is contained in:
parent
8aee357a34
commit
59c6dab2b3
3 changed files with 53 additions and 0 deletions
|
|
@ -756,6 +756,17 @@ void EditorUI::renderTexturePaintPanel(EditorApp& app) {
|
|||
selectedTexture_.c_str());
|
||||
|
||||
// Auto-paint by height
|
||||
if (ImGui::CollapsingHeader("Auto-Paint by Slope")) {
|
||||
static float slopeThresh = 0.4f;
|
||||
ImGui::SliderFloat("Slope Threshold", &slopeThresh, 0.1f, 0.9f);
|
||||
ImGui::TextColored(ImVec4(0.6f,0.6f,0.6f,1), "Paints rock on steep slopes");
|
||||
if (ImGui::Button("Apply Slope Paint", ImVec2(-1, 0))) {
|
||||
app.getTexturePainter().autoPaintBySlope(slopeThresh,
|
||||
"Tileset\\Barrens\\BarrensRock01.blp");
|
||||
app.showToast("Slope paint applied");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -194,6 +194,45 @@ void TexturePainter::autoPaintByHeight(const std::vector<HeightBand>& bands) {
|
|||
}
|
||||
}
|
||||
|
||||
void TexturePainter::autoPaintBySlope(float slopeThreshold, const std::string& steepTexture) {
|
||||
if (!terrain_) return;
|
||||
uint32_t steepTexId = ensureTextureInList(steepTexture);
|
||||
|
||||
for (int ci = 0; ci < 256; ci++) {
|
||||
auto& chunk = terrain_->chunks[ci];
|
||||
if (!chunk.hasHeightMap() || chunk.layers.empty()) continue;
|
||||
|
||||
// Compute average slope from normals
|
||||
float maxSlope = 0.0f;
|
||||
for (int v = 0; v < 145; v++) {
|
||||
float nz = static_cast<float>(chunk.normals[v * 3 + 2]) / 127.0f;
|
||||
float slope = 1.0f - std::abs(nz);
|
||||
maxSlope = std::max(maxSlope, slope);
|
||||
}
|
||||
|
||||
if (maxSlope > slopeThreshold) {
|
||||
// Add steep texture as a layer
|
||||
int layerIdx = ensureLayerOnChunk(ci, steepTexId);
|
||||
if (layerIdx > 0) {
|
||||
size_t off = chunk.layers[layerIdx].offsetMCAL;
|
||||
if (off + 4096 <= chunk.alphaMap.size()) {
|
||||
for (int ty = 0; ty < 64; ty++) {
|
||||
for (int tx = 0; tx < 64; tx++) {
|
||||
// Sample slope at this texel
|
||||
int vi = (ty / 8) * 17 + (tx / 8);
|
||||
if (vi >= 145) vi = 144;
|
||||
float nz = static_cast<float>(chunk.normals[vi * 3 + 2]) / 127.0f;
|
||||
float slope = 1.0f - std::abs(nz);
|
||||
float alpha = std::clamp((slope - slopeThreshold * 0.5f) / (1.0f - slopeThreshold * 0.5f), 0.0f, 1.0f);
|
||||
chunk.alphaMap[off + ty * 64 + tx] = static_cast<uint8_t>(alpha * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> TexturePainter::erase(const glm::vec3& center, float radius,
|
||||
float strength, float falloff) {
|
||||
if (!terrain_ || activeTexture_.empty()) return {};
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ public:
|
|||
struct HeightBand { float maxHeight; std::string texturePath; };
|
||||
void autoPaintByHeight(const std::vector<HeightBand>& bands);
|
||||
|
||||
// Auto-paint steep slopes with rock texture
|
||||
void autoPaintBySlope(float slopeThreshold, const std::string& steepTexture);
|
||||
|
||||
// Paint the active texture at the given world position
|
||||
// Returns list of modified chunk indices
|
||||
std::vector<int> paint(const glm::vec3& center, float radius, float strength, float falloff);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue