diff --git a/doc/classes/MeshInstance.xml b/doc/classes/MeshInstance.xml index 2a70c35e60d..ab81a44631e 100644 --- a/doc/classes/MeshInstance.xml +++ b/doc/classes/MeshInstance.xml @@ -61,6 +61,28 @@ Returns the number of surface materials. + + + + + Returns [code]true[/code] if this [MeshInstance] can be merged with the specified [code]other_mesh_instance[/code], using the [method MeshInstance.merge_meshes] function. + In order to be mergeable, properties of the [MeshInstance] must match, and each surface must match, in terms of material, attributes and vertex format. + + + + + + + + + This function can merge together the data from several source [MeshInstance]s into a single destination [MeshInstance] (the MeshInstance the function is called from). This is primarily useful for improving performance by reducing the number of drawcalls and [Node]s. + Merging should only be attempted for simple meshes that do not contain animation. + The final vertices can either be returned in global space, or in local space relative to the destination [MeshInstance] global transform (the destination Node must be inside the [SceneTree] for local space to work). + The function will make a final check for compatibility between the [MeshInstance]s by default, this should always be used unless you have previously checked for compatibility using [method MeshInstance.is_mergeable_with]. If the compatibility check is omitted and the meshes are merged, you may see rendering errors. + [b]Note:[/b] The requirements for similarity between meshes are quite stringent. They can be checked using the [method MeshInstance.is_mergeable_with] function prior to calling [method MeshInstance.merge_meshes]. + Also note that any initial data in the destination [MeshInstance] data will be discarded. + + diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index 3a7f2844020..a97547ac33a 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -847,6 +847,27 @@ void MeshInstance::create_debug_tangents() { } } +bool MeshInstance::merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility) { + // 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"); + return _merge_meshes(mis, p_use_global_space, p_check_compatibility); +} + bool MeshInstance::is_mergeable_with(Node *p_other) const { const MeshInstance *mi = Object::cast_to(p_other); @@ -1153,7 +1174,7 @@ bool MeshInstance::_triangle_is_degenerate(const Vector3 &p_a, const Vector3 &p_ // If p_check_compatibility is set to false you MUST have performed a prior check using // is_mergeable_with, otherwise you could get mismatching surface formats leading to graphical errors etc. -bool MeshInstance::merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility) { +bool MeshInstance::_merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility) { if (p_list.size() < 1) { // should not happen but just in case return false; @@ -1302,6 +1323,10 @@ void MeshInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance::create_debug_tangents); ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); + ClassDB::bind_method(D_METHOD("is_mergeable_with", "other_mesh_instance"), &MeshInstance::is_mergeable_with); + ClassDB::bind_method(D_METHOD("merge_meshes", "mesh_instances", "use_global_space", "check_compatibility"), &MeshInstance::merge_meshes, DEFVAL(Vector()), DEFVAL(false), DEFVAL(true)); + ClassDB::set_method_flags("MeshInstance", "merge_meshes", METHOD_FLAGS_DEFAULT); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path"); diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 8508b67eb77..b0740d700c9 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -96,6 +96,7 @@ protected: private: // merging + bool _merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility); bool _is_mergeable_with(const MeshInstance &p_other) const; void _merge_into_mesh_data(const MeshInstance &p_mi, const Transform &p_dest_tr_inv, int p_surface_id, PoolVector &r_verts, PoolVector &r_norms, PoolVector &r_tangents, PoolVector &r_colors, PoolVector &r_uvs, PoolVector &r_uv2s, PoolVector &r_inds); bool _ensure_indices_valid(PoolVector &r_indices, const PoolVector &p_verts) const; @@ -146,7 +147,7 @@ public: // merging bool is_mergeable_with(Node *p_other) const; - bool merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility); + bool merge_meshes(Vector p_list, bool p_use_global_space, bool p_check_compatibility); virtual AABB get_aabb() const; virtual PoolVector get_faces(uint32_t p_usage_flags) const; diff --git a/scene/3d/room_manager.cpp b/scene/3d/room_manager.cpp index 0d54ab98554..25db2700e5a 100644 --- a/scene/3d/room_manager.cpp +++ b/scene/3d/room_manager.cpp @@ -2190,7 +2190,14 @@ void RoomManager::_merge_meshes_in_room(Room *p_room) { _merge_log("\t\t" + merged->get_name()); - if (merged->merge_meshes(merge_list, true, false)) { + // merge function takes a vector of variants + Vector variant_merge_list; + variant_merge_list.resize(merge_list.size()); + for (int i = 0; i < merge_list.size(); i++) { + variant_merge_list.set(i, merge_list[i]); + } + + if (merged->merge_meshes(variant_merge_list, true, false)) { // set all the source meshes to portal mode ignore so not shown for (int i = 0; i < merge_list.size(); i++) { merge_list[i]->set_portal_mode(CullInstance::PORTAL_MODE_IGNORE);