Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Mesh merging - refactor to be backward compatible for CPU / GPU storage #92105

Merged
merged 1 commit into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions scene/3d/merge_group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -827,7 +827,7 @@ void MergeGroup::_merge_list(const LocalVector<MeshInstance *> &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;
Expand Down
65 changes: 1 addition & 64 deletions scene/3d/mesh_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -854,71 +854,8 @@ void MeshInstance::create_debug_tangents() {
}
}

bool MeshInstance::split_by_surface(Vector<Variant> 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<MeshInstance *> mis;

for (int n = 0; n < p_destination_mesh_instances.size(); n++) {
MeshInstance *mi = Object::cast_to<MeshInstance>(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<Variant> 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<MeshInstance *> mis;

for (int n = 0; n < p_list.size(); n++) {
MeshInstance *mi = Object::cast_to<MeshInstance>(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 {
Expand Down
1 change: 0 additions & 1 deletion scene/3d/mesh_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ class MeshInstance : public GeometryInstance {
// Merging.
bool is_mergeable_with(Node *p_other, bool p_shadows_only) const;
bool merge_meshes(Vector<Variant> p_list, bool p_use_global_space, bool p_check_compatibility, bool p_shadows_only);
bool split_by_surface(Vector<Variant> p_destination_mesh_instances);

virtual AABB get_aabb() const;
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
Expand Down
81 changes: 81 additions & 0 deletions scene/resources/merging_tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,87 @@
#include "modules/csg/csg_shape.h"
#endif

bool MergingTool::wrapped_split_by_surface(const MeshInstance &p_source_mi, Vector<Variant> 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<MeshInstance *> mis;

for (int n = 0; n < p_destination_mesh_instances.size(); n++) {
MeshInstance *mi = Object::cast_to<MeshInstance>(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<Mesh> 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<Variant> 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<MeshInstance *> mis;

for (int n = 0; n < p_list.size(); n++) {
MeshInstance *mi = Object::cast_to<MeshInstance>(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<Mesh> 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<Material> &p_mat) {
if (p_mat.is_null()) {
return true;
Expand Down
12 changes: 11 additions & 1 deletion scene/resources/merging_tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Variant> 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<Variant> 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);
Expand Down
Loading