diff --git a/modules/terrain/terrain_modifier.cpp b/modules/terrain/terrain_modifier.cpp index 30674478..2d5e2a85 100644 --- a/modules/terrain/terrain_modifier.cpp +++ b/modules/terrain/terrain_modifier.cpp @@ -262,21 +262,23 @@ void TerrainModifierPath::_notification(int what) { } } -float TerrainModifierPath::evaluate_line(Vector3 a, Vector3 b, Vector2 world_coordinate, float &out_dot, float &out_distance) { +float TerrainModifierPath::evaluate_line(Vector3 a, bool a_end, Vector3 b, bool b_end, Vector2 world_coordinate, float &out_dot, float &out_distance) { Vector2 a2{ a.x, a.z }, b2{ b.x, b.z }; Vector2 const relative_coordinate{ world_coordinate - a2 }; Vector2 const difference2{ b2 - a2 }; - float const w{ difference2.normalized().dot(relative_coordinate) / difference2.length() }; + float w{ difference2.normalized().dot(relative_coordinate) / difference2.length() }; Vector3 const difference{ b - a }; Vector3 const closest_on_line{ a + difference * (w > 0 ? (w < 1 ? w : 1) : 0) }; Vector2 const right{ -difference.z, difference.x }; out_dot = right.normalized().dot(relative_coordinate); out_distance = world_coordinate.distance_to({ closest_on_line.x, closest_on_line.z }); - if (a.y > b.y) { - return a.y + (b.y - a.y) * (w > 0 ? w : 0); - } else { - return a.y + (b.y - a.y) * (w < 1 ? w : 1); + if (a_end) { + w = w > 0 ? w : 0; } + if (b_end) { + w = w < 1 ? w : 1; + } + return a.y + (b.y - a.y) * w; } float TerrainModifierPath::evaluate_at(Vector2 world_coordinate, float before) { @@ -285,23 +287,26 @@ float TerrainModifierPath::evaluate_at(Vector2 world_coordinate, float before) { this->lock.unlock_shared(); return before; } + float const max_distance{ this->curve_left->get_max_domain() > this->curve_right->get_max_domain() ? this->curve_left->get_max_domain() : this->curve_right->get_max_domain() }; float out_score{ -INFINITY }; float out_height{ 0 }; - for (int i{ 0 }; i < this->points.size(); i++) { - if (this->closed || i < this->points.size() - 1) { - float dot, distance; - float height{ evaluate_line(this->points[i], this->points[Math::wrapi(i + 1, 0, this->points.size())], world_coordinate, dot, distance) }; - float left{ this->curve_left->sample(distance) }; - float right{ this->curve_right->sample(distance) }; - float ndot{ dot / distance }; - float separation{ ndot / 2.f + 0.5f }; - float weight{ left * (1 - separation) + right * separation }; - float blended_height{ Math::lerp(before, height, weight) }; - float score{ weight - (Math::abs(ndot) == 1) * 100000.f }; - if (score > out_score) { - out_score = score; - out_height = blended_height; - } + long const count{ this->closed ? this->points.size() : this->points.size() - 1 }; + for (int i{ 0 }; i < count; i++) { + float dot, distance; + float const height{ evaluate_line(this->points[i], !this->closed && i == 0, this->points[Math::wrapi(i + 1, 0, this->points.size())], !this->closed && i == count - 1, world_coordinate, dot, distance) }; + float const left{ this->curve_left->sample(distance) }; + float const right{ this->curve_right->sample(distance) }; + float const ndot{ dot / distance }; + float separation{ ndot / 2.f + 0.5f }; + if (closed || (i > 0 && i < count - 1)) { + separation = Math::round(separation); + } + float const weight{ left * (1 - separation) + right * separation }; + float const blended_height{ Math::lerp(before, height, weight) }; + float const score{ weight - (1 - Math::abs(ndot)) * 2.f - (distance / max_distance) }; + if (score > out_score) { + out_score = score; + out_height = blended_height; } } this->lock.unlock_shared(); @@ -311,11 +316,20 @@ float TerrainModifierPath::evaluate_at(Vector2 world_coordinate, float before) { void TerrainModifierPath::path_changed() { this->lock.lock_exclusive(); this->points.clear(); + this->min_height = INFINITY; + this->max_height = -INFINITY; Vector3 last{ INFINITY, INFINITY, INFINITY }; for (Variant var : get_children()) { if (TerrainModifierPathPoint * point{ cast_to(var) }) { - if (var != last) { - this->points.push_back(point->get_global_position()); + Vector3 position{ point->get_global_position() }; + if (position != last) { + this->points.push_back(position); + if (position.y > this->max_height) { + this->max_height = position.y; + } + if (position.y < this->min_height) { + this->min_height = position.y; + } } } last = var; diff --git a/modules/terrain/terrain_modifier.h b/modules/terrain/terrain_modifier.h index 92087eb8..4585a6f6 100644 --- a/modules/terrain/terrain_modifier.h +++ b/modules/terrain/terrain_modifier.h @@ -86,7 +86,7 @@ class TerrainModifierPath : public TerrainModifier { protected: void _notification(int what); - float evaluate_line(Vector3 a, Vector3 b, Vector2 world_coordinate, float &out_dot, float &out_distance); + float evaluate_line(Vector3 a, bool a_end, Vector3 b, bool b_end, Vector2 world_coordinate, float &out_dot, float &out_distance); public: float evaluate_at(Vector2 world_coordinate, float before) override;