From da3c95f325df410e599627b43a7c62243b53a39c Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Sat, 18 May 2024 16:57:27 +0100 Subject: [PATCH] Mesh merging - refactor to be backward compatible for CPU / GPU storage Allows the old `merge_meshes()` function to work from the editor. --- scene/3d/merge_group.cpp | 4 +- scene/3d/mesh_instance.cpp | 65 +------------------------ scene/3d/mesh_instance.h | 1 - scene/resources/merging_tool.cpp | 81 ++++++++++++++++++++++++++++++++ scene/resources/merging_tool.h | 12 ++++- 5 files changed, 95 insertions(+), 68 deletions(-) diff --git a/scene/3d/merge_group.cpp b/scene/3d/merge_group.cpp index e3ccdc08027..dc2e8c143a3 100644 --- a/scene/3d/merge_group.cpp +++ b/scene/3d/merge_group.cpp @@ -302,7 +302,7 @@ void MergeGroup::_split_mesh_by_surface(MeshInstance *p_mi, int p_num_surfaces) _node_changed(parent); - p_mi->split_by_surface(siblings); + MergingTool::wrapped_split_by_surface(*p_mi, siblings, Mesh::STORAGE_MODE_CPU); // Failed to split. if (parent->get_child_count() <= first_sibling_id) { @@ -827,7 +827,7 @@ void MergeGroup::_merge_list(const LocalVector &p_mis, bool p_sh varlist.push_back(Variant(p_mis[n])); } - if (!merged->merge_meshes(varlist, false, false, p_shadows)) { + if (!MergingTool::wrapped_merge_meshes(*merged, varlist, false, false, p_shadows, Mesh::STORAGE_MODE_CPU)) { _log("MERGE_MESHES failed."); _delete_node(merged); return; diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index 829511f92ef..f0490694b94 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -854,71 +854,8 @@ void MeshInstance::create_debug_tangents() { } } -bool MeshInstance::split_by_surface(Vector p_destination_mesh_instances) { - ERR_FAIL_COND_V_MSG(!is_inside_tree(), false, "Source MeshInstance must be inside the SceneTree."); - - // For simplicity we are requiring that the destination MeshInstances have the same parent - // as the source. This means we can use identical transforms. - Node *parent = get_parent(); - ERR_FAIL_NULL_V_MSG(parent, false, "Source MeshInstance must have a parent node."); - - // Bound function only support variants, so we need to convert to a list of MeshInstances. - Vector mis; - - for (int n = 0; n < p_destination_mesh_instances.size(); n++) { - MeshInstance *mi = Object::cast_to(p_destination_mesh_instances[n]); - if (mi) { - if (mi != this) { - ERR_FAIL_COND_V_MSG(mi->get_parent() != parent, false, "Destination MeshInstances must be siblings of the source MeshInstance."); - - mis.push_back(mi); - } else { - ERR_FAIL_V_MSG(false, "Source MeshInstance cannot be a destination."); - } - } else { - ERR_FAIL_V_MSG(false, "Only MeshInstances can be split."); - } - } - - ERR_FAIL_COND_V_MSG(!get_mesh().is_valid(), false, "Mesh is invalid."); - ERR_FAIL_COND_V_MSG(mis.size() != get_mesh()->get_surface_count(), false, "Number of surfaces and number of destination MeshInstances must match."); - - // Go through each surface, and fill the relevant mesh instance. - const Mesh *source_mesh = get_mesh().ptr(); - DEV_ASSERT(source_mesh); - - ERR_FAIL_COND_V_MSG(source_mesh->get_surface_count() <= 1, false, "Source MeshInstance must contain multiple surfaces."); - - for (int s = 0; s < source_mesh->get_surface_count(); s++) { - MergingTool::split_surface_to_mesh_instance(*this, s, *mis[s]); - } - - return true; -} - bool MeshInstance::merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility, bool p_shadows_only) { - // Bound function only support variants, so we need to convert to a list of MeshInstances. - Vector mis; - - for (int n = 0; n < p_list.size(); n++) { - MeshInstance *mi = Object::cast_to(p_list[n]); - if (mi) { - if (mi != this) { - mis.push_back(mi); - } else { - ERR_PRINT("Destination MeshInstance cannot be a source."); - } - } else { - ERR_PRINT("Only MeshInstances can be merged."); - } - } - - ERR_FAIL_COND_V(!mis.size(), "Array contains no MeshInstances"); - - if (p_shadows_only) { - return MergingTool::merge_shadow_meshes(*this, mis, p_use_global_space, p_check_compatibility); - } - return MergingTool::merge_meshes(*this, mis, p_use_global_space, p_check_compatibility); + return MergingTool::wrapped_merge_meshes(*this, p_list, p_use_global_space, p_check_compatibility, p_shadows_only, Mesh::STORAGE_MODE_GPU); } bool MeshInstance::is_mergeable_with(Node *p_other, bool p_shadows_only) const { diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index e30fee312c2..6d680e11668 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -140,7 +140,6 @@ public: // Merging. bool is_mergeable_with(Node *p_other, bool p_shadows_only) const; bool merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility, bool p_shadows_only); - bool split_by_surface(Vector p_destination_mesh_instances); virtual AABB get_aabb() const; virtual PoolVector get_faces(uint32_t p_usage_flags) const; diff --git a/scene/resources/merging_tool.cpp b/scene/resources/merging_tool.cpp index ebfbb8025dc..67c45ef97e4 100644 --- a/scene/resources/merging_tool.cpp +++ b/scene/resources/merging_tool.cpp @@ -40,6 +40,87 @@ #include "modules/csg/csg_shape.h" #endif +bool MergingTool::wrapped_split_by_surface(const MeshInstance &p_source_mi, Vector p_destination_mesh_instances, Mesh::StorageMode p_storage_mode) { + ERR_FAIL_COND_V_MSG(!p_source_mi.is_inside_tree(), false, "Source MeshInstance must be inside the SceneTree."); + ERR_FAIL_COND_V_MSG(!p_source_mi.get_mesh().is_valid(), false, "Source MeshInstance must have a valid mesh to split."); + + // For simplicity we are requiring that the destination MeshInstances have the same parent + // as the source. This means we can use identical transforms. + Node *parent = p_source_mi.get_parent(); + ERR_FAIL_NULL_V_MSG(parent, false, "Source MeshInstance must have a parent node."); + + // Bound function only support variants, so we need to convert to a list of MeshInstances. + Vector mis; + + for (int n = 0; n < p_destination_mesh_instances.size(); n++) { + MeshInstance *mi = Object::cast_to(p_destination_mesh_instances[n]); + + ERR_FAIL_NULL_V_MSG(mi, false, "Can only be split to MeshInstances."); + ERR_FAIL_COND_V_MSG(mi == &p_source_mi, false, "Source MeshInstance cannot be a destination."); + ERR_FAIL_COND_V_MSG(mi->get_parent() != parent, false, "Destination MeshInstances must be siblings of the source MeshInstance."); + mis.push_back(mi); + } + + ERR_FAIL_COND_V_MSG(mis.size() != p_source_mi.get_mesh()->get_surface_count(), false, "Number of source surfaces and number of destination MeshInstances must match."); + + // Go through each surface, and fill the relevant mesh instance. + const Mesh *source_mesh = p_source_mi.get_mesh().ptr(); + DEV_ASSERT(source_mesh); + + ERR_FAIL_COND_V_MSG(source_mesh->get_surface_count() <= 1, false, "Source MeshInstance must contain multiple surfaces."); + + for (int s = 0; s < source_mesh->get_surface_count(); s++) { + MeshInstance &dest_mi = *mis[s]; + if (split_surface_to_mesh_instance(p_source_mi, s, dest_mi)) { + // Change storage mode if required. +#ifdef TOOLS_ENABLED + Ref rmesh = dest_mi.get_mesh(); + if (rmesh.is_valid()) { + _mesh_set_storage_mode(rmesh.ptr(), p_storage_mode); + } +#endif + } + } + + return true; +} + +bool MergingTool::wrapped_merge_meshes(MeshInstance &r_dest_mi, Vector p_list, bool p_use_global_space, bool p_check_compatibility, bool p_shadows_only, Mesh::StorageMode p_storage_mode) { + // Bound function only support variants, so we need to convert to a list of MeshInstances. + Vector mis; + + for (int n = 0; n < p_list.size(); n++) { + MeshInstance *mi = Object::cast_to(p_list[n]); + if (mi) { + ERR_FAIL_COND_V_MSG(mi == &r_dest_mi, false, "Destination MeshInstance cannot be a source."); + mis.push_back(mi); + } else { + ERR_PRINT("Only MeshInstances can be merged."); + } + } + + ERR_FAIL_COND_V_MSG(!mis.size(), false, "Array contains no MeshInstances"); + + bool result; + if (p_shadows_only) { + result = merge_shadow_meshes(r_dest_mi, mis, p_use_global_space, p_check_compatibility); + } else { + result = merge_meshes(r_dest_mi, mis, p_use_global_space, p_check_compatibility); + } + + // Change storage mode if required. + if (result) { +#ifdef TOOLS_ENABLED + Ref rmesh = r_dest_mi.get_mesh(); + if (rmesh.is_valid()) { + _mesh_set_storage_mode(rmesh.ptr(), p_storage_mode); + } +#endif + } + + return result; +} + bool MergingTool::_is_material_opaque(const Ref &p_mat) { if (p_mat.is_null()) { return true; diff --git a/scene/resources/merging_tool.h b/scene/resources/merging_tool.h index 625ffd2d18d..28fe207c564 100644 --- a/scene/resources/merging_tool.h +++ b/scene/resources/merging_tool.h @@ -46,9 +46,19 @@ struct CSGBrush; // #define GODOT_MERGING_VERBOSE #endif -// NOTE : These merging and joining functions DO NOT move children, or delete source nodes. That is the responsibility of the caller. +// NOTE : These merging and joining functions DO NOT move children, or delete source nodes. +// That is the responsibility of the caller. + class MergingTool { public: + //////////////////////////////////////////////////////////////////////////////////// + // WRAPPED versions are accessible via script via MeshInstance. + // These have to cope with Variants as lists of MeshInstances is not easy from script. + static bool wrapped_merge_meshes(MeshInstance &r_dest_mi, Vector p_list, bool p_use_global_space, bool p_check_compatibility, bool p_shadows_only, Mesh::StorageMode p_storage_mode); + static bool wrapped_split_by_surface(const MeshInstance &p_source_mi, Vector p_destination_mesh_instances, Mesh::StorageMode p_storage_mode); + + //////////////////////////////////////////////////////////////////////////////////// + // Are two mesh instances mergeable with each other? static bool is_mergeable_with(const MeshInstance &p_mi, const MeshInstance &p_other, bool p_check_surface_material_match); static bool is_shadow_mergeable_with(const MeshInstance &p_mi, const MeshInstance &p_other);