feat(editor): auto-texture roads with cobblestone on flatten

- paintAlongPath(): paints a texture along a line segment with
  configurable width and quadratic edge falloff
- Road mode now automatically applies Elwynn cobblestone texture
  when flattening a road path (flatten + texture in one operation)
- Quick distance check skips chunks far from the path for performance
- Alpha blending uses max() so overlapping paths don't wash out
This commit is contained in:
Kelsi 2026-05-05 08:16:40 -07:00
parent 8974cef9c0
commit 6de6da766d
3 changed files with 67 additions and 3 deletions

View file

@ -607,11 +607,15 @@ void EditorUI::renderBrushPanel(EditorApp& app) {
ImGui::SameLine();
if (ImGui::Button("Set End + Apply##path", ImVec2(140, 0)) && brush4.isActive() && pathStartSet) {
pathEnd = brush4.getPosition();
if (pathMode == 0)
if (pathMode == 0) {
app.getTerrainEditor().carveRiver(pathStart, pathEnd, pathWidth, pathDepth);
else
app.showToast("River carved");
} else {
app.getTerrainEditor().flattenRoad(pathStart, pathEnd, pathWidth);
app.showToast(pathMode == 0 ? "River carved" : "Road flattened");
app.getTexturePainter().paintAlongPath(pathStart, pathEnd, pathWidth,
"Tileset\\Elwynn\\ElwynnCobblestoneBase.blp");
app.showToast("Road flattened + textured");
}
pathStartSet = false;
}
if (pathStartSet)

View file

@ -233,6 +233,62 @@ void TexturePainter::autoPaintBySlope(float slopeThreshold, const std::string& s
}
}
void TexturePainter::paintAlongPath(const glm::vec3& start, const glm::vec3& end,
float width, const std::string& texturePath) {
if (!terrain_ || texturePath.empty()) return;
uint32_t texId = ensureTextureInList(texturePath);
glm::vec2 lineStart(start.x, start.y);
glm::vec2 lineEnd(end.x, end.y);
glm::vec2 lineDir = glm::normalize(lineEnd - lineStart);
float lineLen = glm::length(lineEnd - lineStart);
for (int ci = 0; ci < 256; ci++) {
auto& chunk = terrain_->chunks[ci];
if (!chunk.hasHeightMap() || chunk.layers.empty()) continue;
int cx = ci % 16, cy = ci / 16;
float tileNW_X = (32.0f - static_cast<float>(terrain_->coord.y)) * 533.33333f;
float tileNW_Y = (32.0f - static_cast<float>(terrain_->coord.x)) * 533.33333f;
float chunkCenterX = tileNW_X - (cy + 0.5f) * (533.33333f / 16.0f);
float chunkCenterY = tileNW_Y - (cx + 0.5f) * (533.33333f / 16.0f);
// Quick distance check
glm::vec2 cc(chunkCenterX, chunkCenterY);
glm::vec2 toCC = cc - lineStart;
float proj = glm::dot(toCC, lineDir);
proj = std::clamp(proj, 0.0f, lineLen);
glm::vec2 closest = lineStart + lineDir * proj;
if (glm::length(cc - closest) > width + 40.0f) continue;
int layerIdx = ensureLayerOnChunk(ci, texId);
if (layerIdx < 0) continue;
size_t alphaOffset = chunk.layers[layerIdx].offsetMCAL;
if (alphaOffset + 4096 > chunk.alphaMap.size()) continue;
float texelSize = (533.33333f / 16.0f) / 64.0f;
for (int ty = 0; ty < 64; ty++) {
for (int tx = 0; tx < 64; tx++) {
float wx = tileNW_X - cy * (533.33333f / 16.0f) - (ty + 0.5f) * texelSize;
float wy = tileNW_Y - cx * (533.33333f / 16.0f) - (tx + 0.5f) * texelSize;
glm::vec2 p(wx, wy);
glm::vec2 toP = p - lineStart;
float t = glm::dot(toP, lineDir);
t = std::clamp(t, 0.0f, lineLen);
glm::vec2 near = lineStart + lineDir * t;
float dist = glm::length(p - near);
if (dist < width) {
float falloff = 1.0f - (dist / width);
falloff = falloff * falloff;
uint8_t alpha = static_cast<uint8_t>(std::min(255.0f, falloff * 255.0f));
chunk.alphaMap[alphaOffset + ty * 64 + tx] = std::max(
chunk.alphaMap[alphaOffset + ty * 64 + tx], alpha);
}
}
}
}
}
std::vector<int> TexturePainter::erase(const glm::vec3& center, float radius,
float strength, float falloff) {
if (!terrain_ || activeTexture_.empty()) return {};

View file

@ -24,6 +24,10 @@ public:
// Auto-paint steep slopes with rock texture
void autoPaintBySlope(float slopeThreshold, const std::string& steepTexture);
// Paint a texture along a line (for roads/paths after flattening)
void paintAlongPath(const glm::vec3& start, const glm::vec3& end,
float width, const std::string& texturePath);
// 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);