Browse Source

Merge pull request #57661 from lawnjelly/bind_mesh_merging

Rémi Verschelde 3 years ago
parent
commit
6a524b2cc0
4 changed files with 58 additions and 3 deletions
  1. 22 0
      doc/classes/MeshInstance.xml
  2. 26 1
      scene/3d/mesh_instance.cpp
  3. 2 1
      scene/3d/mesh_instance.h
  4. 8 1
      scene/3d/room_manager.cpp

+ 22 - 0
doc/classes/MeshInstance.xml

@@ -61,6 +61,28 @@
 				Returns the number of surface materials.
 			</description>
 		</method>
+		<method name="is_mergeable_with" qualifiers="const">
+			<return type="bool" />
+			<argument index="0" name="other_mesh_instance" type="Node" />
+			<description>
+				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.
+			</description>
+		</method>
+		<method name="merge_meshes">
+			<return type="bool" />
+			<argument index="0" name="mesh_instances" type="Array" default="[  ]" />
+			<argument index="1" name="use_global_space" type="bool" default="false" />
+			<argument index="2" name="check_compatibility" type="bool" default="true" />
+			<description>
+				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.
+			</description>
+		</method>
 		<method name="set_surface_material">
 			<return type="void" />
 			<argument index="0" name="surface" type="int" />

+ 26 - 1
scene/3d/mesh_instance.cpp

@@ -847,6 +847,27 @@ void MeshInstance::create_debug_tangents() {
 	}
 }
 
+bool MeshInstance::merge_meshes(Vector<Variant> 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<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");
+	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<MeshInstance>(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<MeshInstance *> p_list, bool p_use_global_space, bool p_check_compatibility) {
+bool MeshInstance::_merge_meshes(Vector<MeshInstance *> 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<Variant>()), 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");

+ 2 - 1
scene/3d/mesh_instance.h

@@ -96,6 +96,7 @@ protected:
 
 private:
 	// merging
+	bool _merge_meshes(Vector<MeshInstance *> 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<Vector3> &r_verts, PoolVector<Vector3> &r_norms, PoolVector<real_t> &r_tangents, PoolVector<Color> &r_colors, PoolVector<Vector2> &r_uvs, PoolVector<Vector2> &r_uv2s, PoolVector<int> &r_inds);
 	bool _ensure_indices_valid(PoolVector<int> &r_indices, const PoolVector<Vector3> &p_verts) const;
@@ -146,7 +147,7 @@ public:
 
 	// merging
 	bool is_mergeable_with(Node *p_other) const;
-	bool merge_meshes(Vector<MeshInstance *> p_list, bool p_use_global_space, bool p_check_compatibility);
+	bool merge_meshes(Vector<Variant> p_list, bool p_use_global_space, bool p_check_compatibility);
 
 	virtual AABB get_aabb() const;
 	virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;

+ 8 - 1
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> 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);