feat: bounds-based mesh reloading

This commit is contained in:
Sara Gerretsen 2026-02-25 22:02:02 +01:00
parent 953e4abe5b
commit 226c821454
7 changed files with 93 additions and 38 deletions

View file

@ -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()) {

View file

@ -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<TerrainChunkMesh *> workload{};

View file

@ -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;
}
}

View file

@ -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);

View file

@ -2,12 +2,12 @@
#include "core/config/engine.h"
#include "core/variant/variant.h"
#include "macros.h"
#include "terrain/terrain.h"
#include <algorithm>
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) {

View file

@ -5,6 +5,7 @@
#include "macros.h"
#include "scene/3d/marker_3d.h"
#include "scene/resources/curve.h"
#include <cmath>
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:

View file

@ -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")