From 97e0b43faadca164cac36aacec9a7ae8e198614f Mon Sep 17 00:00:00 2001 From: ze2j <46556066+ze2j@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:45:05 +0100 Subject: [PATCH] Add ArrayMesh::surface_remove --- doc/classes/ArrayMesh.xml | 7 + doc/classes/RenderingServer.xml | 8 + drivers/gles3/storage/mesh_storage.cpp | 241 +++++++++++------- drivers/gles3/storage/mesh_storage.h | 4 + scene/3d/mesh_instance_3d.cpp | 19 +- scene/resources/mesh.cpp | 12 + scene/resources/mesh.h | 1 + .../rendering/dummy/storage/mesh_storage.cpp | 6 + .../rendering/dummy/storage/mesh_storage.h | 2 + .../renderer_rd/storage_rd/mesh_storage.cpp | 170 ++++++++---- .../renderer_rd/storage_rd/mesh_storage.h | 3 + servers/rendering/rendering_server_default.h | 1 + servers/rendering/storage/mesh_storage.h | 1 + servers/rendering_server.cpp | 1 + servers/rendering_server.h | 1 + tests/scene/test_arraymesh.h | 92 ++++++- 16 files changed, 420 insertions(+), 149 deletions(-) diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index c80dabb8c04..19006bd9755 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -165,6 +165,13 @@ Returns the primitive type of the requested surface (see [method add_surface_from_arrays]). + + + + + Removes a surface at position surf_idx, shifting greater surfaces one surf_idx slot down. + + diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 6be53b336b4..62525ad2007 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -2475,6 +2475,14 @@ Returns a mesh's surface's material. + + + + + + Removes a mesh's surface at position surf_idx, shifting greater surfaces one surf_idx slot down. + + diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 630bfdf1b7c..b20c99ee8cf 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -448,6 +448,72 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) mesh->material_cache.clear(); } +void MeshStorage::_mesh_surface_clear(Mesh *mesh, int p_surface) { + Mesh::Surface &s = *mesh->surfaces[p_surface]; + + if (s.vertex_buffer != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.vertex_buffer); + s.vertex_buffer = 0; + } + + if (s.version_count != 0) { + for (uint32_t j = 0; j < s.version_count; j++) { + glDeleteVertexArrays(1, &s.versions[j].vertex_array); + s.versions[j].vertex_array = 0; + } + } + + if (s.attribute_buffer != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.attribute_buffer); + s.attribute_buffer = 0; + } + + if (s.skin_buffer != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.skin_buffer); + s.skin_buffer = 0; + } + + if (s.index_buffer != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.index_buffer); + s.index_buffer = 0; + } + + if (s.versions) { + memfree(s.versions); //reallocs, so free with memfree. + } + + if (s.wireframe) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.wireframe->index_buffer); + memdelete(s.wireframe); + } + + if (s.lod_count) { + for (uint32_t j = 0; j < s.lod_count; j++) { + if (s.lods[j].index_buffer != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.lods[j].index_buffer); + s.lods[j].index_buffer = 0; + } + } + memdelete_arr(s.lods); + } + + if (mesh->blend_shape_count) { + for (uint32_t j = 0; j < mesh->blend_shape_count; j++) { + if (s.blend_shapes[j].vertex_buffer != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(s.blend_shapes[j].vertex_buffer); + s.blend_shapes[j].vertex_buffer = 0; + } + if (s.blend_shapes[j].vertex_array != 0) { + glDeleteVertexArrays(1, &s.blend_shapes[j].vertex_array); + s.blend_shapes[j].vertex_array = 0; + } + } + memdelete_arr(s.blend_shapes); + } + + memdelete(mesh->surfaces[p_surface]); +} + int MeshStorage::mesh_get_blend_shape_count(RID p_mesh) const { const Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_NULL_V(mesh, -1); @@ -772,69 +838,7 @@ void MeshStorage::mesh_clear(RID p_mesh) { } for (uint32_t i = 0; i < mesh->surface_count; i++) { - Mesh::Surface &s = *mesh->surfaces[i]; - - if (s.vertex_buffer != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(s.vertex_buffer); - s.vertex_buffer = 0; - } - - if (s.version_count != 0) { - for (uint32_t j = 0; j < s.version_count; j++) { - glDeleteVertexArrays(1, &s.versions[j].vertex_array); - s.versions[j].vertex_array = 0; - } - } - - if (s.attribute_buffer != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(s.attribute_buffer); - s.attribute_buffer = 0; - } - - if (s.skin_buffer != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(s.skin_buffer); - s.skin_buffer = 0; - } - - if (s.index_buffer != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(s.index_buffer); - s.index_buffer = 0; - } - - if (s.versions) { - memfree(s.versions); //reallocs, so free with memfree. - } - - if (s.wireframe) { - GLES3::Utilities::get_singleton()->buffer_free_data(s.wireframe->index_buffer); - memdelete(s.wireframe); - } - - if (s.lod_count) { - for (uint32_t j = 0; j < s.lod_count; j++) { - if (s.lods[j].index_buffer != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(s.lods[j].index_buffer); - s.lods[j].index_buffer = 0; - } - } - memdelete_arr(s.lods); - } - - if (mesh->blend_shape_count) { - for (uint32_t j = 0; j < mesh->blend_shape_count; j++) { - if (s.blend_shapes[j].vertex_buffer != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(s.blend_shapes[j].vertex_buffer); - s.blend_shapes[j].vertex_buffer = 0; - } - if (s.blend_shapes[j].vertex_array != 0) { - glDeleteVertexArrays(1, &s.blend_shapes[j].vertex_array); - s.blend_shapes[j].vertex_array = 0; - } - } - memdelete_arr(s.blend_shapes); - } - - memdelete(mesh->surfaces[i]); + _mesh_surface_clear(mesh, i); } if (mesh->surfaces) { memfree(mesh->surfaces); @@ -1033,6 +1037,56 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V v.input_mask = p_input_mask; } +void MeshStorage::mesh_surface_remove(RID p_mesh, int p_surface) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_NULL(mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + + // Clear instance data before mesh data. + for (MeshInstance *mi : mesh->instances) { + _mesh_instance_remove_surface(mi, p_surface); + } + + _mesh_surface_clear(mesh, p_surface); + + if ((uint32_t)p_surface < mesh->surface_count - 1) { + memmove(mesh->surfaces + p_surface, mesh->surfaces + p_surface + 1, sizeof(Mesh::Surface *) * (mesh->surface_count - (p_surface + 1))); + } + mesh->surfaces = (Mesh::Surface **)memrealloc(mesh->surfaces, sizeof(Mesh::Surface *) * (mesh->surface_count - 1)); + --mesh->surface_count; + + mesh->material_cache.clear(); + + mesh->skeleton_aabb_version = 0; + + if (mesh->has_bone_weights) { + mesh->has_bone_weights = false; + for (uint32_t i = 0; i < mesh->surface_count; i++) { + if (mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) { + mesh->has_bone_weights = true; + break; + } + } + } + + if (mesh->surface_count == 0) { + mesh->aabb = AABB(); + } else { + mesh->aabb = mesh->surfaces[0]->aabb; + for (uint32_t i = 1; i < mesh->surface_count; i++) { + mesh->aabb.merge_with(mesh->surfaces[i]->aabb); + } + } + + mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); + + for (Mesh *E : mesh->shadow_owners) { + Mesh *shadow_owner = E; + shadow_owner->shadow_mesh = RID(); + shadow_owner->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); + } +} + /* MESH INSTANCE API */ RID MeshStorage::mesh_instance_create(RID p_base) { @@ -1083,30 +1137,10 @@ void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int } void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { - for (uint32_t i = 0; i < mi->surfaces.size(); i++) { - if (mi->surfaces[i].version_count != 0) { - for (uint32_t j = 0; j < mi->surfaces[i].version_count; j++) { - glDeleteVertexArrays(1, &mi->surfaces[i].versions[j].vertex_array); - mi->surfaces[i].versions[j].vertex_array = 0; - } - memfree(mi->surfaces[i].versions); - } - - if (mi->surfaces[i].vertex_buffers[0] != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffers[0]); - GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffers[1]); - mi->surfaces[i].vertex_buffers[0] = 0; - mi->surfaces[i].vertex_buffers[1] = 0; - } - - if (mi->surfaces[i].vertex_buffer != 0) { - GLES3::Utilities::get_singleton()->buffer_free_data(mi->surfaces[i].vertex_buffer); - mi->surfaces[i].vertex_buffer = 0; - } + while (mi->surfaces.size()) { + _mesh_instance_remove_surface(mi, mi->surfaces.size() - 1); } - mi->surfaces.clear(); - mi->blend_weights.clear(); - mi->skeleton_version = 0; + mi->dirty = false; } void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface) { @@ -1159,6 +1193,39 @@ void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint3 mi->dirty = true; } +void MeshStorage::_mesh_instance_remove_surface(MeshInstance *mi, int p_surface) { + MeshInstance::Surface &surface = mi->surfaces[p_surface]; + + if (surface.version_count != 0) { + for (uint32_t j = 0; j < surface.version_count; j++) { + glDeleteVertexArrays(1, &surface.versions[j].vertex_array); + surface.versions[j].vertex_array = 0; + } + memfree(surface.versions); + } + + if (surface.vertex_buffers[0] != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(surface.vertex_buffers[0]); + GLES3::Utilities::get_singleton()->buffer_free_data(surface.vertex_buffers[1]); + surface.vertex_buffers[0] = 0; + surface.vertex_buffers[1] = 0; + } + + if (surface.vertex_buffer != 0) { + GLES3::Utilities::get_singleton()->buffer_free_data(surface.vertex_buffer); + surface.vertex_buffer = 0; + } + + mi->surfaces.remove_at(p_surface); + + if (mi->surfaces.is_empty()) { + mi->blend_weights.clear(); + mi->weights_dirty = false; + mi->skeleton_version = 0; + } + mi->dirty = true; +} + void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); @@ -1259,7 +1326,7 @@ void MeshStorage::update_mesh_instances() { // Precompute base weight if using blend shapes. float base_weight = 1.0; - if (mi->mesh->blend_shape_count && mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED) { + if (mi->surfaces.size() && mi->mesh->blend_shape_count && mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED) { for (uint32_t i = 0; i < mi->mesh->blend_shape_count; i++) { base_weight -= mi->blend_weights[i]; } diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 5e3a56366ea..83ac0efc5c8 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -241,6 +241,7 @@ private: mutable RID_Owner mesh_owner; void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, MeshInstance::Surface *mis = nullptr); + void _mesh_surface_clear(Mesh *mesh, int p_surface); /* Mesh Instance API */ @@ -248,6 +249,7 @@ private: void _mesh_instance_clear(MeshInstance *mi); void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface); + void _mesh_instance_remove_surface(MeshInstance *mi, int p_surface); void _blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface); SelfList::List dirty_mesh_instance_weights; SelfList::List dirty_mesh_instance_arrays; @@ -315,7 +317,9 @@ public: virtual String mesh_get_path(RID p_mesh) const override; virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override; + virtual void mesh_clear(RID p_mesh) override; + virtual void mesh_surface_remove(RID p_mesh, int p_surface) override; _FORCE_INLINE_ const RID *mesh_get_surface_count_and_materials(RID p_mesh, uint32_t &r_surface_count) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 14bc22a2170..15bd8a18b9f 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -381,21 +381,24 @@ Ref MeshInstance3D::get_active_material(int p_surface) const { void MeshInstance3D::_mesh_changed() { ERR_FAIL_COND(mesh.is_null()); - surface_override_materials.resize(mesh->get_surface_count()); + const int surface_count = mesh->get_surface_count(); + + surface_override_materials.resize(surface_count); uint32_t initialize_bs_from = blend_shape_tracks.size(); blend_shape_tracks.resize(mesh->get_blend_shape_count()); - for (uint32_t i = 0; i < blend_shape_tracks.size(); i++) { - blend_shape_properties["blend_shapes/" + String(mesh->get_blend_shape_name(i))] = i; - if (i < initialize_bs_from) { - set_blend_shape_value(i, blend_shape_tracks[i]); - } else { - set_blend_shape_value(i, 0); + if (surface_count > 0) { + for (uint32_t i = 0; i < blend_shape_tracks.size(); i++) { + blend_shape_properties["blend_shapes/" + String(mesh->get_blend_shape_name(i))] = i; + if (i < initialize_bs_from) { + set_blend_shape_value(i, blend_shape_tracks[i]); + } else { + set_blend_shape_value(i, 0); + } } } - int surface_count = mesh->get_surface_count(); for (int surface_index = 0; surface_index < surface_count; ++surface_index) { if (surface_override_materials[surface_index].is_valid()) { RS::get_singleton()->instance_set_surface_override_material(get_instance(), surface_index, surface_override_materials[surface_index]->get_rid()); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 8c0e0879029..a567670c16a 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -2007,6 +2007,17 @@ void ArrayMesh::clear_surfaces() { aabb = AABB(); } +void ArrayMesh::surface_remove(int p_surface) { + ERR_FAIL_INDEX(p_surface, surfaces.size()); + RS::get_singleton()->mesh_surface_remove(mesh, p_surface); + surfaces.remove_at(p_surface); + + clear_cache(); + _recompute_aabb(); + notify_property_list_changed(); + emit_changed(); +} + void ArrayMesh::set_custom_aabb(const AABB &p_custom) { _create_if_empty(); custom_aabb = p_custom; @@ -2275,6 +2286,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("add_surface_from_arrays", "primitive", "arrays", "blend_shapes", "lods", "flags"), &ArrayMesh::add_surface_from_arrays, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(0)); ClassDB::bind_method(D_METHOD("clear_surfaces"), &ArrayMesh::clear_surfaces); + ClassDB::bind_method(D_METHOD("surface_remove", "surf_idx"), &ArrayMesh::surface_remove); ClassDB::bind_method(D_METHOD("surface_update_vertex_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_vertex_region); ClassDB::bind_method(D_METHOD("surface_update_attribute_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_attribute_region); ClassDB::bind_method(D_METHOD("surface_update_skin_region", "surf_idx", "offset", "data"), &ArrayMesh::surface_update_skin_region); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 068bfb6708c..688311d4128 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -362,6 +362,7 @@ public: int get_surface_count() const override; + void surface_remove(int p_surface); void clear_surfaces(); void surface_set_custom_aabb(int p_idx, const AABB &p_aabb); //only recognized by driver diff --git a/servers/rendering/dummy/storage/mesh_storage.cpp b/servers/rendering/dummy/storage/mesh_storage.cpp index 3f90c80826e..f99430e9a87 100644 --- a/servers/rendering/dummy/storage/mesh_storage.cpp +++ b/servers/rendering/dummy/storage/mesh_storage.cpp @@ -57,6 +57,12 @@ void MeshStorage::mesh_free(RID p_rid) { mesh_owner.free(p_rid); } +void MeshStorage::mesh_surface_remove(RID p_mesh, int p_surface) { + DummyMesh *m = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_NULL(m); + m->surfaces.remove_at(p_surface); +} + void MeshStorage::mesh_clear(RID p_mesh) { DummyMesh *m = mesh_owner.get_or_null(p_mesh); ERR_FAIL_NULL(m); diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index 556d58f4112..48f74611e10 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -129,6 +129,8 @@ public: virtual String mesh_get_path(RID p_mesh) const override { return String(); } virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override {} + + virtual void mesh_surface_remove(RID p_mesh, int p_surface) override; virtual void mesh_clear(RID p_mesh) override; /* MESH INSTANCE */ diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 7537e70235a..21a6b8f3b27 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -505,6 +505,40 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) mesh->material_cache.clear(); } +void MeshStorage::_mesh_surface_clear(Mesh *mesh, int p_surface) { + Mesh::Surface &s = *mesh->surfaces[p_surface]; + + if (s.vertex_buffer.is_valid()) { + RD::get_singleton()->free(s.vertex_buffer); //clears arrays as dependency automatically, including all versions + } + if (s.attribute_buffer.is_valid()) { + RD::get_singleton()->free(s.attribute_buffer); + } + if (s.skin_buffer.is_valid()) { + RD::get_singleton()->free(s.skin_buffer); + } + if (s.versions) { + memfree(s.versions); //reallocs, so free with memfree. + } + + if (s.index_buffer.is_valid()) { + RD::get_singleton()->free(s.index_buffer); + } + + if (s.lod_count) { + for (uint32_t j = 0; j < s.lod_count; j++) { + RD::get_singleton()->free(s.lods[j].index_buffer); + } + memdelete_arr(s.lods); + } + + if (s.blend_shape_buffer.is_valid()) { + RD::get_singleton()->free(s.blend_shape_buffer); + } + + memdelete(mesh->surfaces[p_surface]); +} + int MeshStorage::mesh_get_blend_shape_count(RID p_mesh) const { const Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_NULL_V(mesh, -1); @@ -812,36 +846,7 @@ void MeshStorage::mesh_clear(RID p_mesh) { } for (uint32_t i = 0; i < mesh->surface_count; i++) { - Mesh::Surface &s = *mesh->surfaces[i]; - if (s.vertex_buffer.is_valid()) { - RD::get_singleton()->free(s.vertex_buffer); //clears arrays as dependency automatically, including all versions - } - if (s.attribute_buffer.is_valid()) { - RD::get_singleton()->free(s.attribute_buffer); - } - if (s.skin_buffer.is_valid()) { - RD::get_singleton()->free(s.skin_buffer); - } - if (s.versions) { - memfree(s.versions); //reallocs, so free with memfree. - } - - if (s.index_buffer.is_valid()) { - RD::get_singleton()->free(s.index_buffer); - } - - if (s.lod_count) { - for (uint32_t j = 0; j < s.lod_count; j++) { - RD::get_singleton()->free(s.lods[j].index_buffer); - } - memdelete_arr(s.lods); - } - - if (s.blend_shape_buffer.is_valid()) { - RD::get_singleton()->free(s.blend_shape_buffer); - } - - memdelete(mesh->surfaces[i]); + _mesh_surface_clear(mesh, i); } if (mesh->surfaces) { memfree(mesh->surfaces); @@ -860,6 +865,56 @@ void MeshStorage::mesh_clear(RID p_mesh) { } } +void MeshStorage::mesh_surface_remove(RID p_mesh, int p_surface) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_NULL(mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + + // Clear instance data before mesh data. + for (MeshInstance *mi : mesh->instances) { + _mesh_instance_remove_surface(mi, p_surface); + } + + _mesh_surface_clear(mesh, p_surface); + + if ((uint32_t)p_surface < mesh->surface_count - 1) { + memmove(mesh->surfaces + p_surface, mesh->surfaces + p_surface + 1, sizeof(Mesh::Surface *) * (mesh->surface_count - (p_surface + 1))); + } + mesh->surfaces = (Mesh::Surface **)memrealloc(mesh->surfaces, sizeof(Mesh::Surface *) * (mesh->surface_count - 1)); + --mesh->surface_count; + + mesh->material_cache.clear(); + + mesh->skeleton_aabb_version = 0; + + if (mesh->has_bone_weights) { + mesh->has_bone_weights = false; + for (uint32_t i = 0; i < mesh->surface_count; i++) { + if (mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) { + mesh->has_bone_weights = true; + break; + } + } + } + + if (mesh->surface_count == 0) { + mesh->aabb = AABB(); + } else { + mesh->aabb = mesh->surfaces[0]->aabb; + for (uint32_t i = 1; i < mesh->surface_count; i++) { + mesh->aabb.merge_with(mesh->surfaces[i]->aabb); + } + } + + mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); + + for (Mesh *E : mesh->shadow_owners) { + Mesh *shadow_owner = E; + shadow_owner->shadow_mesh = RID(); + shadow_owner->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); + } +} + bool MeshStorage::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_NULL_V(mesh, false); @@ -925,29 +980,10 @@ void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int } void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { - for (const RendererRD::MeshStorage::MeshInstance::Surface &surface : mi->surfaces) { - if (surface.versions) { - for (uint32_t j = 0; j < surface.version_count; j++) { - RD::get_singleton()->free(surface.versions[j].vertex_array); - } - memfree(surface.versions); - } - - for (uint32_t i = 0; i < 2; i++) { - if (surface.vertex_buffer[i].is_valid()) { - RD::get_singleton()->free(surface.vertex_buffer[i]); - } - } + while (mi->surfaces.size()) { + _mesh_instance_remove_surface(mi, mi->surfaces.size() - 1); } - mi->surfaces.clear(); - - if (mi->blend_weights_buffer.is_valid()) { - RD::get_singleton()->free(mi->blend_weights_buffer); - mi->blend_weights_buffer = RID(); - } - mi->blend_weights.clear(); - mi->weights_dirty = false; - mi->skeleton_version = 0; + mi->dirty = false; } void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface) { @@ -994,6 +1030,36 @@ void MeshStorage::_mesh_instance_add_surface_buffer(MeshInstance *mi, Mesh *mesh s->uniform_set[p_buffer_index] = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_INSTANCE); } +void MeshStorage::_mesh_instance_remove_surface(MeshInstance *mi, int p_surface) { + MeshInstance::Surface &surface = mi->surfaces[p_surface]; + + if (surface.versions) { + for (uint32_t j = 0; j < surface.version_count; j++) { + RD::get_singleton()->free(surface.versions[j].vertex_array); + } + memfree(surface.versions); + } + for (uint32_t i = 0; i < 2; i++) { + if (surface.vertex_buffer[i].is_valid()) { + RD::get_singleton()->free(surface.vertex_buffer[i]); + } + } + + mi->surfaces.remove_at(p_surface); + + if (mi->surfaces.is_empty()) { + if (mi->blend_weights_buffer.is_valid()) { + RD::get_singleton()->free(mi->blend_weights_buffer); + mi->blend_weights_buffer = RID(); + } + + mi->blend_weights.clear(); + mi->weights_dirty = false; + mi->skeleton_version = 0; + } + mi->dirty = true; +} + void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 11962b05f46..80c4b6b80e9 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -201,10 +201,12 @@ private: RD::VertexFormatID _mesh_surface_generate_vertex_format(uint64_t p_surface_format, uint64_t p_input_mask, bool p_instanced_surface, bool p_input_motion_vectors, uint32_t &r_position_stride); void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis = nullptr, uint32_t p_current_buffer = 0, uint32_t p_previous_buffer = 0); + void _mesh_surface_clear(Mesh *mesh, int p_surface); void _mesh_instance_clear(MeshInstance *mi); void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface); void _mesh_instance_add_surface_buffer(MeshInstance *mi, Mesh *mesh, MeshInstance::Surface *s, uint32_t p_surface, uint32_t p_buffer_index); + void _mesh_instance_remove_surface(MeshInstance *mi, int p_surface); mutable RID_Owner mesh_instance_owner; @@ -388,6 +390,7 @@ public: virtual String mesh_get_path(RID p_mesh) const override; virtual void mesh_clear(RID p_mesh) override; + virtual void mesh_surface_remove(RID p_mesh, int p_surface) override; virtual bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 29b1e163c78..5f3895eb5f9 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -376,6 +376,7 @@ public: FUNC2(mesh_set_shadow_mesh, RID, RID) + FUNC2(mesh_surface_remove, RID, int) FUNC1(mesh_clear, RID) /* MULTIMESH API */ diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h index 6a8b385a694..484a050ae96 100644 --- a/servers/rendering/storage/mesh_storage.h +++ b/servers/rendering/storage/mesh_storage.h @@ -74,6 +74,7 @@ public: virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) = 0; + virtual void mesh_surface_remove(RID p_mesh, int p_surface) = 0; virtual void mesh_clear(RID p_mesh) = 0; virtual bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 2051d0caac0..04c5e050927 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2346,6 +2346,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("mesh_get_surface_count", "mesh"), &RenderingServer::mesh_get_surface_count); ClassDB::bind_method(D_METHOD("mesh_set_custom_aabb", "mesh", "aabb"), &RenderingServer::mesh_set_custom_aabb); ClassDB::bind_method(D_METHOD("mesh_get_custom_aabb", "mesh"), &RenderingServer::mesh_get_custom_aabb); + ClassDB::bind_method(D_METHOD("mesh_surface_remove", "mesh", "surface"), &RenderingServer::mesh_surface_remove); ClassDB::bind_method(D_METHOD("mesh_clear", "mesh"), &RenderingServer::mesh_clear); ClassDB::bind_method(D_METHOD("mesh_surface_update_vertex_region", "mesh", "surface", "offset", "data"), &RenderingServer::mesh_surface_update_vertex_region); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 0917af73c6a..aba6a1c9b9c 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -438,6 +438,7 @@ public: virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) = 0; + virtual void mesh_surface_remove(RID p_mesh, int p_surface) = 0; virtual void mesh_clear(RID p_mesh) = 0; /* MULTIMESH API */ diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h index 67aa19c5d3e..68d2bc45b07 100644 --- a/tests/scene/test_arraymesh.h +++ b/tests/scene/test_arraymesh.h @@ -88,6 +88,25 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") { CHECK(mesh->get_blend_shape_count() == 0); } + SUBCASE("Adding blend shapes once all surfaces have been removed is allowed") { + Ref cylinder = memnew(CylinderMesh); + Array cylinder_array{}; + cylinder_array.resize(Mesh::ARRAY_MAX); + cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + + mesh->surface_remove(0); + ERR_PRINT_OFF + mesh->add_blend_shape(name_a); + ERR_PRINT_ON + CHECK(mesh->get_blend_shape_count() == 0); + + mesh->surface_remove(0); + mesh->add_blend_shape(name_a); + CHECK(mesh->get_blend_shape_count() == 1); + } + SUBCASE("Change blend shape name after adding.") { mesh->add_blend_shape(name_a); mesh->set_blend_shape_name(0, name_b); @@ -114,6 +133,35 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") { CHECK(mesh->get_blend_shape_count() == 0); } + SUBCASE("Clearing all blend shapes once all surfaces have been removed is allowed") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_b); + Ref cylinder = memnew(CylinderMesh); + Array cylinder_array{}; + cylinder_array.resize(Mesh::ARRAY_MAX); + cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); + Array blend_shape{}; + blend_shape.resize(Mesh::ARRAY_MAX); + blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX]; + blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL]; + blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT]; + Array blend_shapes{}; + blend_shapes.push_back(blend_shape); + blend_shapes.push_back(blend_shape); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes); + + mesh->surface_remove(0); + ERR_PRINT_OFF + mesh->clear_blend_shapes(); + ERR_PRINT_ON + CHECK(mesh->get_blend_shape_count() == 2); + + mesh->surface_remove(0); + mesh->clear_blend_shapes(); + CHECK(mesh->get_blend_shape_count() == 0); + } + SUBCASE("Can't add surface with incorrect number of blend shapes.") { mesh->add_blend_shape(name_a); mesh->add_blend_shape(name_b); @@ -249,13 +297,16 @@ TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") { Ref cylinder = memnew(CylinderMesh); Array cylinder_array{}; cylinder_array.resize(Mesh::ARRAY_MAX); - cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); + constexpr float cylinder_radius = 3.f; + constexpr float cylinder_height = 5.f; + cylinder->create_mesh_array(cylinder_array, cylinder_radius, cylinder_radius, cylinder_height); mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); Ref box = memnew(BoxMesh); Array box_array{}; box_array.resize(Mesh::ARRAY_MAX); - box->create_mesh_array(box_array, Vector3(2.f, 1.2f, 1.6f)); + const Vector3 box_size = Vector3(2.f, 1.2f, 1.6f); + box->create_mesh_array(box_array, box_size); mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array); SUBCASE("Set the shadow mesh.") { @@ -337,6 +388,43 @@ TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") { CHECK((mesh2->surface_get_format(0) & surface_data.format) != 0); CHECK(mesh2->get_aabb().is_equal_approx(surface_data.aabb)); } + + SUBCASE("Removing a surface decreases surface count.") { + REQUIRE(mesh->get_surface_count() == 2); + mesh->surface_remove(0); + CHECK(mesh->get_surface_count() == 1); + mesh->surface_remove(0); + CHECK(mesh->get_surface_count() == 0); + } + + SUBCASE("Remove the first surface and check the mesh's AABB.") { + REQUIRE(mesh->get_surface_count() >= 1); + mesh->surface_remove(0); + const AABB box_aabb = AABB(-box_size / 2, box_size); + CHECK(mesh->get_aabb().is_equal_approx(box_aabb)); + } + + SUBCASE("Remove the last surface and check the mesh's AABB.") { + REQUIRE(mesh->get_surface_count() >= 1); + mesh->surface_remove(mesh->get_surface_count() - 1); + const AABB cylinder_aabb = AABB(Vector3(-cylinder_radius, -cylinder_height / 2, -cylinder_radius), + Vector3(2 * cylinder_radius, cylinder_height, 2 * cylinder_radius)); + CHECK(mesh->get_aabb().is_equal_approx(cylinder_aabb)); + } + + SUBCASE("Remove all surfaces and check the mesh's AABB.") { + while (mesh->get_surface_count()) { + mesh->surface_remove(0); + } + CHECK(mesh->get_aabb() == AABB()); + } + + SUBCASE("Removing a non-existent surface causes error.") { + ERR_PRINT_OFF + mesh->surface_remove(42); + ERR_PRINT_ON + CHECK(mesh->get_surface_count() == 2); + } } } // namespace TestArrayMesh