fix(terrain): NaN guards + zero-length checks on river/road/ridge generators

Same defensive pattern as paintAlongPath. carveRiver, flattenRoad,
and createRidge all called glm::normalize on a possibly-zero
direction vector, then divided by lineLen later. NaN endpoints
short-circuited dist comparisons and applied the height delta
to every vertex on every chunk.
This commit is contained in:
Kelsi 2026-05-06 07:43:39 -07:00
parent 869cee70b1
commit 2c5710b910

View file

@ -785,11 +785,16 @@ void TerrainEditor::mirrorY() {
void TerrainEditor::carveRiver(const glm::vec3& start, const glm::vec3& end,
float width, float depth) {
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 ||
!std::isfinite(depth)) return;
recordGeneratorUndo();
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);
if (lineLen < 1e-4f) return;
glm::vec2 lineDir = (lineEnd - lineStart) / lineLen;
for (int ci = 0; ci < 256; ci++) {
auto& chunk = terrain_->chunks[ci];
@ -1370,11 +1375,16 @@ void TerrainEditor::createIsland(float centerHeight, float edgeDropoff) {
void TerrainEditor::createRidge(const glm::vec3& start, const glm::vec3& end,
float width, float height) {
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 ||
!std::isfinite(height)) return;
recordGeneratorUndo();
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);
if (lineLen < 1e-4f) return;
glm::vec2 lineDir = (lineEnd - lineStart) / lineLen;
for (int ci = 0; ci < 256; ci++) {
auto& chunk = terrain_->chunks[ci];
@ -1406,11 +1416,17 @@ void TerrainEditor::createRidge(const glm::vec3& start, const glm::vec3& end,
void TerrainEditor::flattenRoad(const glm::vec3& start, const glm::vec3& end, float width) {
if (!terrain_) return;
if (!std::isfinite(start.x) || !std::isfinite(start.y) || !std::isfinite(start.z) ||
!std::isfinite(end.x) || !std::isfinite(end.y) || !std::isfinite(end.z) ||
!std::isfinite(width) || width <= 0.0f) return;
recordGeneratorUndo();
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);
// Zero-length road would normalize to NaN and propagate NaN through
// every dist comparison — paint the height delta on every vertex.
if (lineLen < 1e-4f) return;
glm::vec2 lineDir = (lineEnd - lineStart) / lineLen;
// Interpolate height along the path
auto heightAtT = [&](float t) -> float {