feat: apply_mesh for chunks is smeared over frames

This commit is contained in:
Sara Gerretsen 2026-02-25 22:42:07 +01:00
parent 226c821454
commit 1e44fcd09f
5 changed files with 47 additions and 11 deletions

View file

@ -19,6 +19,23 @@ void Terrain::child_order_changed() {
}
}
void Terrain::update_meshes() {
size_t num{ 1 };
this->dirty_meshes_lock.lock();
num = num > this->dirty_meshes.size() ? this->dirty_meshes.size() : num;
this->dirty_meshes_lock.unlock();
for (size_t i{ 0 }; i < num; i++) {
this->dirty_meshes_lock.lock();
TerrainChunkMesh *mesh{ this->dirty_meshes[0] };
this->dirty_meshes.remove_at(0);
this->dirty_meshes_lock.unlock();
mesh->apply_new_mesh();
}
if (this->dirty_meshes.is_empty()) {
set_process(false);
}
}
void Terrain::_notification(int what) {
switch (what) {
default:
@ -31,6 +48,9 @@ void Terrain::_notification(int what) {
case NOTIFICATION_READY:
construct_chunk_grid();
return;
case NOTIFICATION_PROCESS:
update_meshes();
return;
case NOTIFICATION_EXIT_TREE:
this->workload_lock.lock();
this->threads_stop = true;
@ -116,6 +136,13 @@ void Terrain::push_changed(Rect2 area) {
}
}
void Terrain::mesh_dirty(TerrainChunkMesh *mesh) {
this->dirty_meshes_lock.lock();
this->dirty_meshes.push_back(mesh);
callable_mp(cast_to<Node>(this), &self_type::set_process).call_deferred(true);
this->dirty_meshes_lock.unlock();
}
void Terrain::set_side_length(size_t length) {
this->side_length = length;
if (is_inside_tree()) {

View file

@ -12,6 +12,7 @@ class Terrain : public Node {
GDCLASS(Terrain, Node);
static void _bind_methods();
void child_order_changed();
void update_meshes();
protected:
void _notification(int what);
@ -21,11 +22,16 @@ public:
void construct_chunk_grid();
float height_at(Vector2 world_coordinate);
void push_changed(Rect2 area);
void mesh_dirty(TerrainChunkMesh *mesh);
private:
Vector<TerrainChunkMesh *> workload{};
Mutex workload_lock;
bool threads_stop{ false };
Mutex workload_lock;
Vector<TerrainChunkMesh *> dirty_meshes{};
Mutex dirty_meshes_lock{};
Vector<TerrainChunkMesh *> meshes{};
Vector<TerrainModifier *> modifiers{};
LocalVector<Thread> threads{};

View file

@ -5,10 +5,6 @@
void TerrainChunkMesh::_bind_methods() {}
void TerrainChunkMesh::apply_new_mesh() {
set_mesh(this->new_mesh);
}
void TerrainChunkMesh::generate_vertices() {
ERR_FAIL_COND_EDMSG(this->terrain == nullptr, "TerrainChunkMesh::generate_vertices: no terrain assigned");
ERR_FAIL_COND_EDMSG(this->size <= 0.f, "TerrainChunkMesh::generate_vertices: size <= 0");
@ -68,6 +64,12 @@ void TerrainChunkMesh::_notification(int what) {
}
}
void TerrainChunkMesh::apply_new_mesh() {
this->lock.lock();
set_mesh(this->new_mesh);
this->lock.unlock();
}
void TerrainChunkMesh::update_mesh() {
ERR_FAIL_COND_EDMSG(this->size <= 0.f, "TerrainChunkMesh::generate: size <= 0");
ERR_FAIL_COND_EDMSG(points_per_side() <= 0, "TerrainChunkMesh::generate: points per side <= 0");
@ -80,8 +82,8 @@ void TerrainChunkMesh::update_mesh() {
this->surface->generate_tangents();
this->new_mesh = memnew(ArrayMesh);
this->surface->commit(this->new_mesh);
callable_mp(this, &self_type::apply_new_mesh).call_deferred();
this->lock.unlock();
this->terrain->mesh_dirty(this);
}
size_t TerrainChunkMesh::points_per_side() const {

View file

@ -10,7 +10,6 @@ class Terrain;
class TerrainChunkMesh : public MeshInstance3D {
GDCLASS(TerrainChunkMesh, MeshInstance3D);
static void _bind_methods();
void apply_new_mesh();
void generate_vertices();
void generate_faces();
@ -18,6 +17,7 @@ protected:
void _notification(int what);
public:
void apply_new_mesh();
void update_mesh();
size_t points_per_side() const;