From 226c8214549f5a79c1f2a6415145d711e82aa8c9 Mon Sep 17 00:00:00 2001 From: Sara Date: Wed, 25 Feb 2026 22:02:02 +0100 Subject: [PATCH] feat: bounds-based mesh reloading --- modules/terrain/terrain.cpp | 13 ++++++ modules/terrain/terrain.h | 3 +- modules/terrain/terrain_chunk.cpp | 4 +- modules/terrain/terrain_chunk.h | 3 ++ modules/terrain/terrain_modifier.cpp | 69 +++++++++++++++++++++------- modules/terrain/terrain_modifier.h | 14 +++--- project/scenes/terrain_test.tscn | 25 +++++----- 7 files changed, 93 insertions(+), 38 deletions(-) diff --git a/modules/terrain/terrain.cpp b/modules/terrain/terrain.cpp index 78973aae..632075a8 100644 --- a/modules/terrain/terrain.cpp +++ b/modules/terrain/terrain.cpp @@ -78,6 +78,7 @@ void Terrain::construct_chunk_grid() { this->meshes.clear(); size_t const chunks_per_side{ this->side_length / this->chunk_size }; Vector3 const origin{ (float)this->chunk_size / 2.f, 0.f, (float)this->chunk_size / 2.f }; + this->workload_lock.lock(); for (size_t y{ 0 }; y < chunks_per_side; ++y) { for (size_t x{ 0 }; x < chunks_per_side; ++x) { TerrainChunkMesh *chunk{ memnew(TerrainChunkMesh) }; @@ -88,8 +89,10 @@ void Terrain::construct_chunk_grid() { add_child(chunk); chunk->set_owner(this); this->meshes.push_back(chunk); + this->workload.push_back(chunk); } } + this->workload_lock.unlock(); } float Terrain::height_at(Vector2 world_coordinate) { @@ -103,6 +106,16 @@ float Terrain::height_at(Vector2 world_coordinate) { return height; } +void Terrain::push_changed(Rect2 area) { + for (TerrainChunkMesh *mesh : this->meshes) { + this->workload_lock.lock(); + if (area.intersects(mesh->get_bounds()) && !this->workload.has(mesh)) { + workload.push_back(mesh); + } + this->workload_lock.unlock(); + } +} + void Terrain::set_side_length(size_t length) { this->side_length = length; if (is_inside_tree()) { diff --git a/modules/terrain/terrain.h b/modules/terrain/terrain.h index 12d98ec7..bfa10944 100644 --- a/modules/terrain/terrain.h +++ b/modules/terrain/terrain.h @@ -1,5 +1,6 @@ #pragma once +#include "core/math/rect2.h" #include "core/os/mutex.h" #include "core/os/thread.h" #include "core/templates/vector.h" @@ -18,8 +19,8 @@ protected: public: void construct_chunk_grid(); - void area_dirty(Vector2 from, Vector2 to); float height_at(Vector2 world_coordinate); + void push_changed(Rect2 area); private: Vector workload{}; diff --git a/modules/terrain/terrain_chunk.cpp b/modules/terrain/terrain_chunk.cpp index e1999f10..f3c9a6a8 100644 --- a/modules/terrain/terrain_chunk.cpp +++ b/modules/terrain/terrain_chunk.cpp @@ -60,8 +60,10 @@ void TerrainChunkMesh::_notification(int what) { default: return; case NOTIFICATION_READY: - set_process_thread_group(ProcessThreadGroup::PROCESS_THREAD_GROUP_SUB_THREAD); this->safe_position = get_global_position(); + float const sizef{ (float)get_size() }; + this->bounds.position = { this->safe_position.x - sizef / 2.f, this->safe_position.z - sizef / 2.f }; + this->bounds.size = { sizef, sizef }; return; } } diff --git a/modules/terrain/terrain_chunk.h b/modules/terrain/terrain_chunk.h index e1105444..5e0ab458 100644 --- a/modules/terrain/terrain_chunk.h +++ b/modules/terrain/terrain_chunk.h @@ -1,5 +1,6 @@ #pragma once +#include "core/math/rect2.h" #include "macros.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/resources/mesh.h" @@ -28,8 +29,10 @@ private: Terrain *terrain{ nullptr }; size_t detail{ 1 }; size_t size{ 1 }; + Rect2 bounds{}; public: + GET_SET_FNS(Rect2, bounds); GET_SET_FNS(Terrain *, terrain); GET_SET_FNS(size_t, detail); GET_SET_FNS(size_t, size); diff --git a/modules/terrain/terrain_modifier.cpp b/modules/terrain/terrain_modifier.cpp index 05807a17..54c8a79c 100644 --- a/modules/terrain/terrain_modifier.cpp +++ b/modules/terrain/terrain_modifier.cpp @@ -2,12 +2,12 @@ #include "core/config/engine.h" #include "core/variant/variant.h" #include "macros.h" +#include "terrain/terrain.h" #include void TerrainModifier::_bind_methods() { BIND_HPROPERTY(Variant::INT, blend_mode, PROPERTY_HINT_ENUM, BlendMode_hint()); BIND_PROPERTY(Variant::FLOAT, blend_distance); - ADD_SIGNAL(MethodInfo(sig_changed)); } void TerrainModifier::_notification(int what) { @@ -20,9 +20,6 @@ void TerrainModifier::_notification(int what) { } this->thread_safe_global_position = get_global_position(); case NOTIFICATION_TRANSFORM_CHANGED: - if (Engine::get_singleton()->is_editor_hint()) { - emit_signal(sig_changed); - } this->thread_safe_global_position = get_global_position(); return; } @@ -56,13 +53,10 @@ float TerrainModifier::blend(float under, float over) { } } -void TerrainModifier::changed() { - this->dirty = true; - emit_signal(sig_changed); -} - -void TerrainModifier::changed_deferred() { - callable_mp(this, &self_type::changed).call_deferred(); +void TerrainModifier::push_changed(Rect2 area) { + if (this->terrain) { + this->terrain->push_changed(area); + } } float TerrainModifier::evaluate_at(Vector2 world_coordinate, float before) { @@ -77,7 +71,6 @@ Vector3 TerrainModifier::get_thread_safe_global_position() const { void TerrainModifier::set_blend_distance(float value) { this->blend_distance = value; - emit_signal(sig_changed); } float TerrainModifier::get_blend_distance() const { @@ -86,15 +79,12 @@ float TerrainModifier::get_blend_distance() const { void TerrainModifier::set_blend_mode(BlendMode mode) { this->blend_mode = mode; - emit_signal(sig_changed); } TerrainModifier::BlendMode TerrainModifier::get_blend_mode() const { return this->blend_mode; } -String const TerrainModifier::sig_changed{ "changed" }; - void SharedMutex::lock_shared() { this->lock.lock(); this->shared_count++; @@ -127,15 +117,60 @@ void TerrainModifierDistance::_bind_methods() { } void TerrainModifierDistance::curves_changed() { - this->lock.lock_shared(); + this->lock.lock_exclusive(); if (this->distance_height_curve.is_valid()) { this->distance_height_curve->bake(); } if (this->distance_weight_curve.is_valid()) { this->distance_weight_curve->bake(); } + this->lock.unlock_exclusive(); + if (!update_bounds()) { + push_changed(get_bounds()); + } +} + +bool TerrainModifierDistance::update_bounds() { + Rect2 const before{ get_bounds() }; + Rect2 bounds{}; + Vector3 position{ get_thread_safe_global_position() }; + bounds.position = { position.x, position.z }; + bounds.size = { 0, 0 }; + this->lock.lock_shared(); + if (this->distance_weight_curve.is_valid()) { + float const max_radius{ this->distance_weight_curve->get_max_domain() }; + float const max_diameter{ 2.f * max_radius }; + bounds.size = { max_diameter, max_diameter }; + bounds.position -= { max_radius, max_radius }; + } this->lock.unlock_shared(); - changed(); + this->lock.lock_exclusive(); + bool const changed{ before != bounds }; + if (changed) { + set_bounds(bounds); + push_changed(before); + push_changed(bounds); + } + this->lock.unlock_exclusive(); + return changed; +} + +void TerrainModifierDistance::_notification(int what) { + switch (what) { + default: + return; + case NOTIFICATION_READY: + update_bounds(); + set_notify_transform(true); + return; + case NOTIFICATION_TRANSFORM_CHANGED: + if (is_inside_tree()) { + if (!update_bounds()) { + push_changed(get_bounds()); + } + } + return; + } } float TerrainModifierDistance::distance_at(Vector2 const &world_coordinate) { diff --git a/modules/terrain/terrain_modifier.h b/modules/terrain/terrain_modifier.h index a45dcfd8..45a7e331 100644 --- a/modules/terrain/terrain_modifier.h +++ b/modules/terrain/terrain_modifier.h @@ -5,6 +5,7 @@ #include "macros.h" #include "scene/3d/marker_3d.h" #include "scene/resources/curve.h" +#include class Terrain; class TerrainModifier : public Marker3D { @@ -16,9 +17,8 @@ public: protected: void _notification(int what); - void changed(); - void changed_deferred(); float blend(float under, float over); + void push_changed(Rect2 bounds); public: virtual float evaluate_at(Vector2 world_coordinate, float before); @@ -27,8 +27,11 @@ private: float blend_distance{ 10.0 }; BlendMode blend_mode{ Add }; Vector3 thread_safe_global_position{}; - bool dirty{ false }; Terrain *terrain{ nullptr }; + Rect2 bounds{ { -INFINITY, -INFINITY }, { INFINITY, INFINITY } }; + +protected: + GET_SET_FNS(Rect2, bounds); public: Vector3 get_thread_safe_global_position() const; @@ -36,10 +39,7 @@ public: float get_blend_distance() const; void set_blend_mode(BlendMode mode); BlendMode get_blend_mode() const; - GET_SET_FNS(bool, dirty); GET_SET_FNS(Terrain *, terrain); - - static String const sig_changed; }; MAKE_TYPE_INFO(TerrainModifier::BlendMode, Variant::INT); @@ -59,8 +59,10 @@ class TerrainModifierDistance : public TerrainModifier { GDCLASS(TerrainModifierDistance, TerrainModifier); static void _bind_methods(); void curves_changed(); + bool update_bounds(); protected: + void _notification(int what); virtual float distance_at(Vector2 const &world_coordinate); public: diff --git a/project/scenes/terrain_test.tscn b/project/scenes/terrain_test.tscn index 3022edec..aa0c90aa 100644 --- a/project/scenes/terrain_test.tscn +++ b/project/scenes/terrain_test.tscn @@ -13,16 +13,16 @@ background_mode = 2 sky = SubResource("Sky_w3uoq") [sub_resource type="Curve" id="Curve_kbmr5"] -_limits = [0.0, 1.0, 0.0, 200.0] -_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(123.87009, 0.38367254), -0.016318396, -0.016318396, 0, 0, Vector2(200, 0), 0.0, 0.0, 0, 0] -point_count = 3 +_limits = [0.0, 1.0, 0.0, 500.0] +_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(76.609314, 0.6878389), -0.006221681, -0.006221681, 0, 0, Vector2(238.22075, 0.107827306), -0.0011518027, -0.0011518027, 0, 0, Vector2(500, 0), 0.0, 0.0, 0, 0] +point_count = 4 [sub_resource type="Curve" id="Curve_w3uoq"] [sub_resource type="Curve" id="Curve_chm2y"] -_limits = [0.0, 1.0, 0.0, 200.0] -_data = [Vector2(0, 1), 0.0, -0.010352392, 0, 0, Vector2(200, 0), 0.00017739221, -0.05797184, 0, 0] -point_count = 2 +_limits = [0.0, 1.0, 0.0, 300.0] +_data = [Vector2(0, 1), 0.0, -0.0043322877, 0, 0, Vector2(92.849304, 0.5972115), -0.0058290004, -0.0058290004, 0, 0, Vector2(300, 0), 0.00017739221, -0.05797184, 0, 0] +point_count = 3 [sub_resource type="Curve" id="Curve_o3i6r"] _limits = [-30.0, 0.0, 0.0, 100.0] @@ -30,8 +30,8 @@ _data = [Vector2(0, 0), 0.0, -0.56894803, 0, 0] point_count = 1 [sub_resource type="Curve" id="Curve_nonsf"] -_limits = [0.0, 1.0, 0.0, 200.0] -_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(91.651596, 0.37567776), -0.0118739465, -0.0118739465, 0, 0, Vector2(200, 0), 0.0, 0.0, 0, 0] +_limits = [0.0, 1.0, 0.0, 500.0] +_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(101.02984, 0.7462511), -0.0056187166, -0.0056187166, 0, 0, Vector2(500, 0), 0.0, 0.0, 0, 0] point_count = 3 [sub_resource type="Curve" id="Curve_4kj3c"] @@ -48,23 +48,22 @@ environment = SubResource("Environment_o3i6r") [node name="Terrain" type="Terrain" parent="." unique_id=1169843565] side_length = 1000 -chunk_size = 100 -thread_count = 4 +thread_count = 2 [node name="TerrainModifierDistance" type="TerrainModifierDistance" parent="Terrain" unique_id=1885116624] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 701.1817, 109.88881, 615.8812) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 544.8632, 188.56836, 713.1275) blend_distance = 4.0 distance_weight_curve = SubResource("Curve_kbmr5") distance_height_curve = SubResource("Curve_w3uoq") [node name="TerrainModifierDistance3" type="TerrainModifierDistance" parent="Terrain" unique_id=1846439541] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 138.05537, 108.75946, 327.30096) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 534.7505, 273.91986, 453.44415) blend_distance = 4.0 distance_weight_curve = SubResource("Curve_chm2y") distance_height_curve = SubResource("Curve_o3i6r") [node name="TerrainModifierDistance2" type="TerrainModifierDistance" parent="Terrain" unique_id=2110821264] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 183.81352, -28.866524, 195.71535) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 788.26746, -122.60801, 675.6073) blend_mode = 1 distance_weight_curve = SubResource("Curve_nonsf") distance_height_curve = SubResource("Curve_4kj3c")