Mesh merging - refactor to be backward compatible for CPU / GPU storage
Allows the old `merge_meshes()` function to work from the editor.
This commit is contained in:
parent
182a95ada6
commit
da3c95f325
5 changed files with 95 additions and 68 deletions
|
@ -302,7 +302,7 @@ void MergeGroup::_split_mesh_by_surface(MeshInstance *p_mi, int p_num_surfaces)
|
||||||
|
|
||||||
_node_changed(parent);
|
_node_changed(parent);
|
||||||
|
|
||||||
p_mi->split_by_surface(siblings);
|
MergingTool::wrapped_split_by_surface(*p_mi, siblings, Mesh::STORAGE_MODE_CPU);
|
||||||
|
|
||||||
// Failed to split.
|
// Failed to split.
|
||||||
if (parent->get_child_count() <= first_sibling_id) {
|
if (parent->get_child_count() <= first_sibling_id) {
|
||||||
|
@ -827,7 +827,7 @@ void MergeGroup::_merge_list(const LocalVector<MeshInstance *> &p_mis, bool p_sh
|
||||||
varlist.push_back(Variant(p_mis[n]));
|
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.");
|
_log("MERGE_MESHES failed.");
|
||||||
_delete_node(merged);
|
_delete_node(merged);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -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) {
|
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.
|
return MergingTool::wrapped_merge_meshes(*this, p_list, p_use_global_space, p_check_compatibility, p_shadows_only, Mesh::STORAGE_MODE_GPU);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MeshInstance::is_mergeable_with(Node *p_other, bool p_shadows_only) const {
|
bool MeshInstance::is_mergeable_with(Node *p_other, bool p_shadows_only) const {
|
||||||
|
|
|
@ -140,7 +140,6 @@ public:
|
||||||
// Merging.
|
// Merging.
|
||||||
bool is_mergeable_with(Node *p_other, bool p_shadows_only) const;
|
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 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 AABB get_aabb() const;
|
||||||
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
|
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
|
||||||
|
|
|
@ -40,6 +40,87 @@
|
||||||
#include "modules/csg/csg_shape.h"
|
#include "modules/csg/csg_shape.h"
|
||||||
#endif
|
#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) {
|
bool MergingTool::_is_material_opaque(const Ref<Material> &p_mat) {
|
||||||
if (p_mat.is_null()) {
|
if (p_mat.is_null()) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -46,9 +46,19 @@ struct CSGBrush;
|
||||||
// #define GODOT_MERGING_VERBOSE
|
// #define GODOT_MERGING_VERBOSE
|
||||||
#endif
|
#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 {
|
class MergingTool {
|
||||||
public:
|
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?
|
// 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_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);
|
static bool is_shadow_mergeable_with(const MeshInstance &p_mi, const MeshInstance &p_other);
|
||||||
|
|
Loading…
Reference in a new issue