Browse Source

Merge pull request #61729 from lawnjelly/faster_mesh_merging

Optimized Mesh Merging
Rémi Verschelde 3 years ago
parent
commit
6ec7e2c230
3 changed files with 138 additions and 61 deletions
  1. 33 0
      core/local_vector.h
  2. 102 58
      scene/3d/mesh_instance.cpp
  3. 3 3
      scene/3d/mesh_instance.h

+ 33 - 0
core/local_vector.h

@@ -33,6 +33,7 @@
 
 
 #include "core/error_macros.h"
 #include "core/error_macros.h"
 #include "core/os/memory.h"
 #include "core/os/memory.h"
+#include "core/pool_vector.h"
 #include "core/sort_array.h"
 #include "core/sort_array.h"
 #include "core/vector.h"
 #include "core/vector.h"
 
 
@@ -240,6 +241,17 @@ public:
 		return ret;
 		return ret;
 	}
 	}
 
 
+	operator PoolVector<T>() const {
+		PoolVector<T> pl;
+		if (size()) {
+			pl.resize(size());
+			typename PoolVector<T>::Write w = pl.write();
+			T *dest = w.ptr();
+			memcpy(dest, data, sizeof(T) * count);
+		}
+		return pl;
+	}
+
 	Vector<uint8_t> to_byte_array() const { //useful to pass stuff to gpu or variant
 	Vector<uint8_t> to_byte_array() const { //useful to pass stuff to gpu or variant
 		Vector<uint8_t> ret;
 		Vector<uint8_t> ret;
 		ret.resize(count * sizeof(T));
 		ret.resize(count * sizeof(T));
@@ -255,6 +267,19 @@ public:
 			data[i] = p_from.data[i];
 			data[i] = p_from.data[i];
 		}
 		}
 	}
 	}
+	LocalVector(const Vector<T> &p_from) {
+		resize(p_from.size());
+		for (U i = 0; i < count; i++) {
+			data[i] = p_from[i];
+		}
+	}
+	LocalVector(const PoolVector<T> &p_from) {
+		resize(p_from.size());
+		typename PoolVector<T>::Read r = p_from.read();
+		for (U i = 0; i < count; i++) {
+			data[i] = r[i];
+		}
+	}
 	inline LocalVector &operator=(const LocalVector &p_from) {
 	inline LocalVector &operator=(const LocalVector &p_from) {
 		resize(p_from.size());
 		resize(p_from.size());
 		for (U i = 0; i < p_from.count; i++) {
 		for (U i = 0; i < p_from.count; i++) {
@@ -269,6 +294,14 @@ public:
 		}
 		}
 		return *this;
 		return *this;
 	}
 	}
+	inline LocalVector &operator=(const PoolVector<T> &p_from) {
+		resize(p_from.size());
+		typename PoolVector<T>::Read r = p_from.read();
+		for (U i = 0; i < count; i++) {
+			data[i] = r[i];
+		}
+		return *this;
+	}
 
 
 	_FORCE_INLINE_ ~LocalVector() {
 	_FORCE_INLINE_ ~LocalVector() {
 		if (data) {
 		if (data) {

+ 102 - 58
scene/3d/mesh_instance.cpp

@@ -949,7 +949,7 @@ bool MeshInstance::_is_mergeable_with(const MeshInstance &p_other) const {
 	return true;
 	return true;
 }
 }
 
 
-void MeshInstance::_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) {
+void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transform &p_dest_tr_inv, int p_surface_id, LocalVector<Vector3> &r_verts, LocalVector<Vector3> &r_norms, LocalVector<real_t> &r_tangents, LocalVector<Color> &r_colors, LocalVector<Vector2> &r_uvs, LocalVector<Vector2> &r_uv2s, LocalVector<int> &r_inds) {
 	_merge_log("\t\t\tmesh data from " + p_mi.get_name());
 	_merge_log("\t\t\tmesh data from " + p_mi.get_name());
 
 
 	// get the mesh verts in local space
 	// get the mesh verts in local space
@@ -961,13 +961,18 @@ void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transfo
 
 
 	Array arrays = rmesh->surface_get_arrays(p_surface_id);
 	Array arrays = rmesh->surface_get_arrays(p_surface_id);
 
 
-	PoolVector<Vector3> verts = arrays[VS::ARRAY_VERTEX];
-	PoolVector<Vector3> normals = arrays[VS::ARRAY_NORMAL];
-	PoolVector<real_t> tangents = arrays[VS::ARRAY_TANGENT];
-	PoolVector<Color> colors = arrays[VS::ARRAY_COLOR];
-	PoolVector<Vector2> uvs = arrays[VS::ARRAY_TEX_UV];
-	PoolVector<Vector2> uv2s = arrays[VS::ARRAY_TEX_UV2];
-	PoolVector<int> indices = arrays[VS::ARRAY_INDEX];
+	LocalVector<Vector3> verts = PoolVector<Vector3>(arrays[VS::ARRAY_VERTEX]);
+	if (!verts.size()) {
+		// early out if there are no vertices, no point in doing anything else
+		return;
+	}
+
+	LocalVector<Vector3> normals = PoolVector<Vector3>(arrays[VS::ARRAY_NORMAL]);
+	LocalVector<real_t> tangents = PoolVector<real_t>(arrays[VS::ARRAY_TANGENT]);
+	LocalVector<Color> colors = PoolVector<Color>(arrays[VS::ARRAY_COLOR]);
+	LocalVector<Vector2> uvs = PoolVector<Vector2>(arrays[VS::ARRAY_TEX_UV]);
+	LocalVector<Vector2> uv2s = PoolVector<Vector2>(arrays[VS::ARRAY_TEX_UV2]);
+	LocalVector<int> indices = PoolVector<int>(arrays[VS::ARRAY_INDEX]);
 
 
 	// The attributes present must match the first mesh for the attributes
 	// The attributes present must match the first mesh for the attributes
 	// to remain in sync. Here we reject meshes with different attributes.
 	// to remain in sync. Here we reject meshes with different attributes.
@@ -1004,7 +1009,7 @@ void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transfo
 	}
 	}
 
 
 	// the first index of this mesh is offset from the verts we already have stored in the merged mesh
 	// the first index of this mesh is offset from the verts we already have stored in the merged mesh
-	int first_index = r_verts.size();
+	int starting_index = r_verts.size();
 
 
 	// transform verts to world space
 	// transform verts to world space
 	Transform tr = p_mi.get_global_transform();
 	Transform tr = p_mi.get_global_transform();
@@ -1018,78 +1023,117 @@ void MeshInstance::_merge_into_mesh_data(const MeshInstance &p_mi, const Transfo
 	Basis normal_basis = tr.basis.inverse();
 	Basis normal_basis = tr.basis.inverse();
 	normal_basis.transpose();
 	normal_basis.transpose();
 
 
-	for (int n = 0; n < verts.size(); n++) {
+	int num_verts = verts.size();
+
+	// verts
+	DEV_ASSERT(num_verts > 0);
+	int first_vert = r_verts.size();
+	r_verts.resize(first_vert + num_verts);
+	Vector3 *dest_verts = &r_verts[first_vert];
+
+	for (int n = 0; n < num_verts; n++) {
 		Vector3 pt_world = tr.xform(verts[n]);
 		Vector3 pt_world = tr.xform(verts[n]);
-		r_verts.push_back(pt_world);
+		*dest_verts++ = pt_world;
+	}
 
 
-		if (normals.size()) {
+	// normals
+	if (normals.size()) {
+		int first_norm = r_norms.size();
+		r_norms.resize(first_norm + num_verts);
+		Vector3 *dest_norms = &r_norms[first_norm];
+		for (int n = 0; n < num_verts; n++) {
 			Vector3 pt_norm = normal_basis.xform(normals[n]);
 			Vector3 pt_norm = normal_basis.xform(normals[n]);
 			pt_norm.normalize();
 			pt_norm.normalize();
-			r_norms.push_back(pt_norm);
+			*dest_norms++ = pt_norm;
 		}
 		}
+	}
 
 
-		if (tangents.size()) {
+	// tangents
+	if (tangents.size()) {
+		int first_tang = r_tangents.size();
+		r_tangents.resize(first_tang + (num_verts * 4));
+		real_t *dest_tangents = &r_tangents[first_tang];
+
+		for (int n = 0; n < num_verts; n++) {
 			int tstart = n * 4;
 			int tstart = n * 4;
 			Vector3 pt_tangent = Vector3(tangents[tstart], tangents[tstart + 1], tangents[tstart + 2]);
 			Vector3 pt_tangent = Vector3(tangents[tstart], tangents[tstart + 1], tangents[tstart + 2]);
 			real_t fourth = tangents[tstart + 3];
 			real_t fourth = tangents[tstart + 3];
 
 
 			pt_tangent = normal_basis.xform(pt_tangent);
 			pt_tangent = normal_basis.xform(pt_tangent);
 			pt_tangent.normalize();
 			pt_tangent.normalize();
-			r_tangents.push_back(pt_tangent.x);
-			r_tangents.push_back(pt_tangent.y);
-			r_tangents.push_back(pt_tangent.z);
-			r_tangents.push_back(fourth);
+			*dest_tangents++ = pt_tangent.x;
+			*dest_tangents++ = pt_tangent.y;
+			*dest_tangents++ = pt_tangent.z;
+			*dest_tangents++ = fourth;
 		}
 		}
+	}
 
 
-		if (colors.size()) {
-			r_colors.push_back(colors[n]);
+	// colors
+	if (colors.size()) {
+		int first_col = r_colors.size();
+		r_colors.resize(first_col + num_verts);
+		Color *dest_colors = &r_colors[first_col];
+
+		for (int n = 0; n < num_verts; n++) {
+			*dest_colors++ = colors[n];
 		}
 		}
+	}
 
 
-		if (uvs.size()) {
-			r_uvs.push_back(uvs[n]);
+	// uvs
+	if (uvs.size()) {
+		int first_uv = r_uvs.size();
+		r_uvs.resize(first_uv + num_verts);
+		Vector2 *dest_uvs = &r_uvs[first_uv];
+
+		for (int n = 0; n < num_verts; n++) {
+			*dest_uvs++ = uvs[n];
 		}
 		}
+	}
 
 
-		if (uv2s.size()) {
-			r_uv2s.push_back(uv2s[n]);
+	// uv2s
+	if (uv2s.size()) {
+		int first_uv2 = r_uv2s.size();
+		r_uv2s.resize(first_uv2 + num_verts);
+		Vector2 *dest_uv2s = &r_uv2s[first_uv2];
+
+		for (int n = 0; n < num_verts; n++) {
+			*dest_uv2s++ = uv2s[n];
 		}
 		}
 	}
 	}
 
 
 	// indices
 	// indices
-	for (int n = 0; n < indices.size(); n++) {
-		int ind = indices[n] + first_index;
-		r_inds.push_back(ind);
+	if (indices.size()) {
+		int first_ind = r_inds.size();
+		r_inds.resize(first_ind + indices.size());
+		int *dest_inds = &r_inds[first_ind];
+
+		for (unsigned int n = 0; n < indices.size(); n++) {
+			int ind = indices[n] + starting_index;
+			*dest_inds++ = ind;
+		}
 	}
 	}
 }
 }
 
 
-bool MeshInstance::_ensure_indices_valid(PoolVector<int> &r_indices, const PoolVector<Vector3> &p_verts) const {
+bool MeshInstance::_ensure_indices_valid(LocalVector<int> &r_indices, const PoolVector<Vector3> &p_verts) const {
 	// no indices? create some
 	// no indices? create some
 	if (!r_indices.size()) {
 	if (!r_indices.size()) {
 		_merge_log("\t\t\t\tindices are blank, creating...");
 		_merge_log("\t\t\t\tindices are blank, creating...");
 
 
 		// indices are blank!! let's create some, assuming the mesh is using triangles
 		// indices are blank!! let's create some, assuming the mesh is using triangles
 		r_indices.resize(p_verts.size());
 		r_indices.resize(p_verts.size());
-		PoolVector<int>::Write write = r_indices.write();
-		int *pi = write.ptr();
 
 
 		// this is assuming each triangle vertex is unique
 		// this is assuming each triangle vertex is unique
-		for (int n = 0; n < p_verts.size(); n++) {
-			*pi = n;
-			pi++;
+		for (unsigned int n = 0; n < r_indices.size(); n++) {
+			r_indices[n] = n;
 		}
 		}
 	}
 	}
 
 
 	if (!_check_for_valid_indices(r_indices, p_verts, nullptr)) {
 	if (!_check_for_valid_indices(r_indices, p_verts, nullptr)) {
-		LocalVector<int, int32_t> new_inds;
+		LocalVector<int> new_inds;
 		_check_for_valid_indices(r_indices, p_verts, &new_inds);
 		_check_for_valid_indices(r_indices, p_verts, &new_inds);
 
 
 		// copy the new indices
 		// copy the new indices
-		r_indices.resize(new_inds.size());
-		PoolVector<int>::Write write = r_indices.write();
-		int *pi = write.ptr();
-
-		for (int n = 0; n < new_inds.size(); n++) {
-			pi[n] = new_inds[n];
-		}
+		r_indices = new_inds;
 
 
 		return false;
 		return false;
 	}
 	}
@@ -1098,7 +1142,7 @@ bool MeshInstance::_ensure_indices_valid(PoolVector<int> &r_indices, const PoolV
 }
 }
 
 
 // check for invalid tris, or make a list of the valid triangles, depending on whether r_inds is set
 // check for invalid tris, or make a list of the valid triangles, depending on whether r_inds is set
-bool MeshInstance::_check_for_valid_indices(const PoolVector<int> &p_inds, const PoolVector<Vector3> &p_verts, LocalVector<int, int32_t> *r_inds) const {
+bool MeshInstance::_check_for_valid_indices(const LocalVector<int> &p_inds, const PoolVector<Vector3> &p_verts, LocalVector<int> *r_inds) const {
 	int nTris = p_inds.size();
 	int nTris = p_inds.size();
 	nTris /= 3;
 	nTris /= 3;
 	int indCount = 0;
 	int indCount = 0;
@@ -1220,13 +1264,13 @@ bool MeshInstance::_merge_meshes(Vector<MeshInstance *> p_list, bool p_use_globa
 	}
 	}
 
 
 	for (int s = 0; s < first->get_mesh()->get_surface_count(); s++) {
 	for (int s = 0; s < first->get_mesh()->get_surface_count(); s++) {
-		PoolVector<Vector3> verts;
-		PoolVector<Vector3> normals;
-		PoolVector<real_t> tangents;
-		PoolVector<Color> colors;
-		PoolVector<Vector2> uvs;
-		PoolVector<Vector2> uv2s;
-		PoolVector<int> inds;
+		LocalVector<Vector3> verts;
+		LocalVector<Vector3> normals;
+		LocalVector<real_t> tangents;
+		LocalVector<Color> colors;
+		LocalVector<Vector2> uvs;
+		LocalVector<Vector2> uv2s;
+		LocalVector<int> inds;
 
 
 		for (int n = 0; n < p_list.size(); n++) {
 		for (int n = 0; n < p_list.size(); n++) {
 			// Ignore if the mesh is incompatible
 			// Ignore if the mesh is incompatible
@@ -1242,9 +1286,9 @@ bool MeshInstance::_merge_meshes(Vector<MeshInstance *> p_list, bool p_use_globa
 		}
 		}
 
 
 		// sanity check on the indices
 		// sanity check on the indices
-		for (int n = 0; n < inds.size(); n++) {
+		for (unsigned int n = 0; n < inds.size(); n++) {
 			int i = inds[n];
 			int i = inds[n];
-			if (i >= verts.size()) {
+			if ((unsigned int)i >= verts.size()) {
 				WARN_PRINT_ONCE("Mesh index out of range, invalid mesh, aborting");
 				WARN_PRINT_ONCE("Mesh index out of range, invalid mesh, aborting");
 				return false;
 				return false;
 			}
 			}
@@ -1252,23 +1296,23 @@ bool MeshInstance::_merge_meshes(Vector<MeshInstance *> p_list, bool p_use_globa
 
 
 		Array arr;
 		Array arr;
 		arr.resize(Mesh::ARRAY_MAX);
 		arr.resize(Mesh::ARRAY_MAX);
-		arr[Mesh::ARRAY_VERTEX] = verts;
+		arr[Mesh::ARRAY_VERTEX] = PoolVector<Vector3>(verts);
 		if (normals.size()) {
 		if (normals.size()) {
-			arr[Mesh::ARRAY_NORMAL] = normals;
+			arr[Mesh::ARRAY_NORMAL] = PoolVector<Vector3>(normals);
 		}
 		}
 		if (tangents.size()) {
 		if (tangents.size()) {
-			arr[Mesh::ARRAY_TANGENT] = tangents;
+			arr[Mesh::ARRAY_TANGENT] = PoolVector<real_t>(tangents);
 		}
 		}
 		if (colors.size()) {
 		if (colors.size()) {
-			arr[Mesh::ARRAY_COLOR] = colors;
+			arr[Mesh::ARRAY_COLOR] = PoolVector<Color>(colors);
 		}
 		}
 		if (uvs.size()) {
 		if (uvs.size()) {
-			arr[Mesh::ARRAY_TEX_UV] = uvs;
+			arr[Mesh::ARRAY_TEX_UV] = PoolVector<Vector2>(uvs);
 		}
 		}
 		if (uv2s.size()) {
 		if (uv2s.size()) {
-			arr[Mesh::ARRAY_TEX_UV2] = uv2s;
+			arr[Mesh::ARRAY_TEX_UV2] = PoolVector<Vector2>(uv2s);
 		}
 		}
-		arr[Mesh::ARRAY_INDEX] = inds;
+		arr[Mesh::ARRAY_INDEX] = PoolVector<int>(inds);
 
 
 		am->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, Array(), Mesh::ARRAY_COMPRESS_DEFAULT);
 		am->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, Array(), Mesh::ARRAY_COMPRESS_DEFAULT);
 	} // for s through surfaces
 	} // for s through surfaces

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

@@ -98,9 +98,9 @@ private:
 	// merging
 	// merging
 	bool _merge_meshes(Vector<MeshInstance *> p_list, bool p_use_global_space, bool p_check_compatibility);
 	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;
 	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;
-	bool _check_for_valid_indices(const PoolVector<int> &p_inds, const PoolVector<Vector3> &p_verts, LocalVector<int, int32_t> *r_inds) const;
+	void _merge_into_mesh_data(const MeshInstance &p_mi, const Transform &p_dest_tr_inv, int p_surface_id, LocalVector<Vector3> &r_verts, LocalVector<Vector3> &r_norms, LocalVector<real_t> &r_tangents, LocalVector<Color> &r_colors, LocalVector<Vector2> &r_uvs, LocalVector<Vector2> &r_uv2s, LocalVector<int> &r_inds);
+	bool _ensure_indices_valid(LocalVector<int> &r_indices, const PoolVector<Vector3> &p_verts) const;
+	bool _check_for_valid_indices(const LocalVector<int> &p_inds, const PoolVector<Vector3> &p_verts, LocalVector<int> *r_inds) const;
 	bool _triangle_is_degenerate(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, real_t p_epsilon) const;
 	bool _triangle_is_degenerate(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, real_t p_epsilon) const;
 	void _merge_log(String p_string) const;
 	void _merge_log(String p_string) const;