mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-07 17:43:51 +00:00
feat(editor): river tool now fills water along the carved path
The river path tool used to carve the channel and texture the banks but never added a water layer — users had to manually run "Fill Water" afterward, which floods the entire tile. The fix is a new TerrainEditor::fillWaterAlongPath() method that adds water layers only to chunks the river segment passes through (within width + chunk-half-diagonal of the line). Per-chunk water height is set to the chunk's post-carve minimum terrain height + 0.5y offset so the water sits visibly in the channel without overflowing onto banks. The river apply path now invokes carveRiver → paintAlongPath → fillWaterAlongPath in sequence. Toast updated to mention all three. Multi-point rivers are still next on the list — the underlying math takes a single segment today, so a polyline river needs a UI revamp to capture N points + a per-segment loop. Ack'd, not done this commit.
This commit is contained in:
parent
ecba93d4a4
commit
158ab192f0
3 changed files with 84 additions and 1 deletions
|
|
@ -1049,7 +1049,13 @@ void EditorUI::renderBrushPanel(EditorApp& app) {
|
|||
app.getTerrainEditor().carveRiver(pathStart_, pathEnd_, pathWidth_, pathDepth_);
|
||||
app.getTexturePainter().paintAlongPath(pathStart_, pathEnd_, pathWidth_ * 1.5f,
|
||||
"Tileset\\Ashenvale\\AshenvaleSand.blp");
|
||||
app.showToast("River carved + banks textured");
|
||||
// After carving, fill water in the chunks along
|
||||
// the river path so the channel actually looks
|
||||
// like a river. liquidType 0 = water (1=ocean,
|
||||
// 2=magma, 3=slime).
|
||||
app.getTerrainEditor().fillWaterAlongPath(
|
||||
pathStart_, pathEnd_, pathWidth_, 0);
|
||||
app.showToast("River carved + banks textured + water filled");
|
||||
} else {
|
||||
app.getTerrainEditor().flattenRoad(pathStart_, pathEnd_, pathWidth_);
|
||||
app.getTexturePainter().paintAlongPath(pathStart_, pathEnd_, pathWidth_,
|
||||
|
|
|
|||
|
|
@ -1133,6 +1133,75 @@ void TerrainEditor::fillWater(float height, uint16_t liquidType) {
|
|||
dirty_ = true;
|
||||
}
|
||||
|
||||
void TerrainEditor::fillWaterAlongPath(const glm::vec3& start, const glm::vec3& end,
|
||||
float width, uint16_t liquidType) {
|
||||
if (!terrain_) return;
|
||||
if (!std::isfinite(start.x) || !std::isfinite(start.y) ||
|
||||
!std::isfinite(end.x) || !std::isfinite(end.y) ||
|
||||
!std::isfinite(width) || width <= 0.0f) return;
|
||||
glm::vec2 lineStart(start.x, start.y);
|
||||
glm::vec2 lineEnd(end.x, end.y);
|
||||
float lineLen = glm::length(lineEnd - lineStart);
|
||||
if (lineLen < 1e-4f) return;
|
||||
glm::vec2 lineDir = (lineEnd - lineStart) / lineLen;
|
||||
|
||||
// Each chunk is 33.33 yards across, so a chunk's diagonal half is
|
||||
// ~23.6 yards. Treat any chunk whose center lies within
|
||||
// `width + chunkHalfDiag` of the river segment as "intersected" so
|
||||
// the water layer covers any cell the carved channel touches.
|
||||
constexpr float kChunkSize = 33.33333f;
|
||||
const float chunkHalfDiag = kChunkSize * 0.7071f; // sqrt(2)/2 of size
|
||||
|
||||
int filled = 0;
|
||||
for (int ci = 0; ci < 256; ci++) {
|
||||
auto& chunk = terrain_->chunks[ci];
|
||||
if (!chunk.hasHeightMap()) continue;
|
||||
// Chunk center in world coords. position[0]=wowY, [1]=wowX so the
|
||||
// 2D vector here is (X, Y) for the chunk's center.
|
||||
glm::vec2 chunkCenter(chunk.position[1] + kChunkSize * 0.5f,
|
||||
chunk.position[0] + kChunkSize * 0.5f);
|
||||
glm::vec2 toC = chunkCenter - lineStart;
|
||||
float t = glm::dot(toC, lineDir);
|
||||
t = std::clamp(t, 0.0f, lineLen);
|
||||
glm::vec2 closest = lineStart + lineDir * t;
|
||||
float dist = glm::length(chunkCenter - closest);
|
||||
if (dist > width + chunkHalfDiag) continue;
|
||||
|
||||
// Find min terrain height in this chunk after carving — water
|
||||
// goes to that level + a small offset so it visibly fills the
|
||||
// channel without overflowing onto banks.
|
||||
float minH = 1e30f;
|
||||
for (int v = 0; v < 145; v++) {
|
||||
float absH = chunk.position[2] + chunk.heightMap.heights[v];
|
||||
if (absH < minH) minH = absH;
|
||||
}
|
||||
if (minH > 1e29f) continue;
|
||||
float waterH = minH + 0.5f;
|
||||
|
||||
auto& water = terrain_->waterData[ci];
|
||||
if (water.layers.empty()) {
|
||||
pipeline::ADTTerrain::WaterLayer wl;
|
||||
wl.liquidType = liquidType;
|
||||
wl.flags = 0;
|
||||
wl.minHeight = waterH;
|
||||
wl.maxHeight = waterH;
|
||||
wl.x = 0; wl.y = 0; wl.width = 9; wl.height = 9;
|
||||
wl.heights.assign(81, waterH);
|
||||
wl.mask.assign(8, 0xFF);
|
||||
water.layers.push_back(wl);
|
||||
} else {
|
||||
auto& wl = water.layers[0];
|
||||
wl.liquidType = liquidType;
|
||||
wl.minHeight = waterH;
|
||||
wl.maxHeight = waterH;
|
||||
std::fill(wl.heights.begin(), wl.heights.end(), waterH);
|
||||
}
|
||||
dirtyChunks_.push_back(ci);
|
||||
filled++;
|
||||
}
|
||||
if (filled > 0) dirty_ = true;
|
||||
}
|
||||
|
||||
void TerrainEditor::smoothBeaches(float waterHeight, float beachWidth) {
|
||||
if (!terrain_) return;
|
||||
recordGeneratorUndo();
|
||||
|
|
|
|||
|
|
@ -127,6 +127,14 @@ public:
|
|||
// Fill entire tile with water at a height
|
||||
void fillWater(float height, uint16_t liquidType);
|
||||
|
||||
// Add a water layer only to chunks the line segment passes through
|
||||
// (within `width`). Used by the river tool so rivers actually fill
|
||||
// with water without flooding the rest of the tile. Per-chunk water
|
||||
// height is computed from the post-carve terrain so the water sits
|
||||
// in the carved channel.
|
||||
void fillWaterAlongPath(const glm::vec3& start, const glm::vec3& end,
|
||||
float width, uint16_t liquidType);
|
||||
|
||||
// Smooth terrain near water level to create natural beaches
|
||||
void smoothBeaches(float waterHeight, float beachWidth);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue