Преглед на файлове

Merge pull request #76371 from ze2j/array_mesh_surface_remove

Add `ArrayMesh::surface_remove`
Thaddeus Crews преди 8 месеца
родител
ревизия
b715fabd70

+ 7 - 0
doc/classes/ArrayMesh.xml

@@ -165,6 +165,13 @@
 				Returns the primitive type of the requested surface (see [method add_surface_from_arrays]).
 			</description>
 		</method>
+		<method name="surface_remove">
+			<return type="void" />
+			<param index="0" name="surf_idx" type="int" />
+			<description>
+				Removes a surface at position surf_idx, shifting greater surfaces one surf_idx slot down.
+			</description>
+		</method>
 		<method name="surface_set_name">
 			<return type="void" />
 			<param index="0" name="surf_idx" type="int" />

+ 8 - 0
doc/classes/RenderingServer.xml

@@ -2508,6 +2508,14 @@
 				Returns a mesh's surface's material.
 			</description>
 		</method>
+		<method name="mesh_surface_remove">
+			<return type="void" />
+			<param index="0" name="mesh" type="RID" />
+			<param index="1" name="surface" type="int" />
+			<description>
+				Removes a mesh's surface at position surf_idx, shifting greater surfaces one surf_idx slot down.
+			</description>
+		</method>
 		<method name="mesh_surface_set_material">
 			<return type="void" />
 			<param index="0" name="mesh" type="RID" />

+ 154 - 87
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);
@@ -1034,6 +1038,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) {
@@ -1084,30 +1138,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) {
@@ -1160,6 +1194,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);
 
@@ -1260,7 +1327,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];
 			}

+ 4 - 0
drivers/gles3/storage/mesh_storage.h

@@ -241,6 +241,7 @@ private:
 	mutable RID_Owner<Mesh, true> 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<MeshInstance>::List dirty_mesh_instance_weights;
 	SelfList<MeshInstance>::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);

+ 11 - 8
scene/3d/mesh_instance_3d.cpp

@@ -381,21 +381,24 @@ Ref<Material> 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());

+ 12 - 0
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);

+ 1 - 0
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

+ 6 - 0
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);

+ 2 - 0
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 */

+ 118 - 52
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);
@@ -861,6 +866,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);
@@ -926,29 +981,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]);
-			}
-		}
-	}
-	mi->surfaces.clear();
-
-	if (mi->blend_weights_buffer.is_valid()) {
-		RD::get_singleton()->free(mi->blend_weights_buffer);
-		mi->blend_weights_buffer = RID();
+	while (mi->surfaces.size()) {
+		_mesh_instance_remove_surface(mi, mi->surfaces.size() - 1);
 	}
-	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) {
@@ -995,6 +1031,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);
 

+ 3 - 0
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<MeshInstance> 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;
 

+ 1 - 0
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 */

+ 1 - 0
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;

+ 1 - 0
servers/rendering_server.cpp

@@ -2354,6 +2354,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);

+ 1 - 0
servers/rendering_server.h

@@ -444,6 +444,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 */

+ 90 - 2
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<CylinderMesh> 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<CylinderMesh> 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<CylinderMesh> 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<BoxMesh> 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