summaryrefslogtreecommitdiff
path: root/Runtime/Misc/MeshWelding.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Misc/MeshWelding.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Misc/MeshWelding.cpp')
-rw-r--r--Runtime/Misc/MeshWelding.cpp249
1 files changed, 249 insertions, 0 deletions
diff --git a/Runtime/Misc/MeshWelding.cpp b/Runtime/Misc/MeshWelding.cpp
new file mode 100644
index 0000000..2fee400
--- /dev/null
+++ b/Runtime/Misc/MeshWelding.cpp
@@ -0,0 +1,249 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/MeshWelding.h"
+#include "Editor/Src/AssetPipeline/ImportMesh.h"
+
+
+inline UInt32 GetVector3HashValue (const Vector3f& value)
+{
+ const UInt32* h = (const UInt32*)(&value);
+ UInt32 f = (h[0]+h[1]*11-(h[2]*17))&0x7fffffff; // avoid problems with +-0
+ return (f>>22)^(f>>12)^(f);
+}
+
+/*
+ In 12 operations, this code computes the next highest power of 2 for a 32-bit integer. The result may be expressed by the formula 1U << (lg(v - 1) + 1).
+ It would be faster by 2 operations to use the formula and the log base 2 methed that uses a lookup table, but in some situations,
+ lookup tables are not suitable, so the above code may be best.
+ */
+
+inline int nextPowerOfTwo (UInt32 v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v + (v==0);
+}
+
+inline bool CompareBone(const BoneInfluence& lhs, const BoneInfluence& rhs)
+{
+ for (int i=0;i<4;i++)
+ {
+ if (!CompareApproximately(lhs.weight[0], rhs.weight[0]) || lhs.boneIndex[i] != rhs.boneIndex[i])
+ return false;
+ }
+ return true;
+}
+
+
+#if UNITY_EDITOR
+
+/*-----------------------------------------------------------------------//*
+ !
+ * \brief an array of vertex positions
+ * \param p Array of vertex positions (will be modified!)
+ * \param N Number of vertices in array
+ * \return Number of vertices after the welding operation
+ * \note The unique vertices are stored into the beginning of array
+ * 'p'.
+ * \note This welder is "bit-exact", i.e. only duplicate vertices are
+ * removed. For distance-based welding a somewhat more complicated
+ * algorithm needs to be used.
+
+ *-------------------------------------------------------------------------*/
+
+int weld (std::vector<Vector3f>& vertices, dynamic_array<BoneInfluence>& skin, std::vector<ImportBlendShape>& shapes, std::vector<int>& remap)
+{
+ const int NIL = -1; // linked list terminator symbol
+ int outputCount = 0; // # of output vertices
+ int hashSize = nextPowerOfTwo(vertices.size()); // size of the hash table
+ int* hashTable = new int[hashSize + vertices.size()]; // hash table + linked list
+ int* next = hashTable + hashSize; // use bottom part as linked list
+
+ remap.resize(vertices.size());
+
+ memset (hashTable, NIL, (hashSize) * sizeof(int)); // init hash table (NIL = 0xFFFFFFFF so memset works)
+
+ for (int i = 0; i < vertices.size(); i++)
+ {
+ const Vector3f& v = vertices[i];
+ UInt32 hashValue = GetVector3HashValue(v) & (hashSize-1);
+ int offset = hashTable[hashValue];
+ while (offset != NIL)
+ {
+ Assert (offset < i);
+ bool euqals = (vertices[offset] == v);
+
+ if (euqals && !skin.empty() && !CompareBone(skin[i], skin[offset]))
+ euqals = false;
+
+ if (euqals && !shapes.empty())
+ {
+ for (int j = 0; j < shapes.size(); ++j)
+ {
+ if (shapes[j].vertices[i] != shapes[j].vertices[offset])
+ {
+ euqals = false;
+ break;
+ }
+ }
+ }
+
+ if (euqals)
+ break;
+
+ offset = next[offset];
+ }
+
+ if (offset == NIL) // no match found - copy vertex & add to hash
+ {
+ remap[i] = outputCount;
+ vertices[outputCount] = v; // copy vertex
+ if (!skin.empty())
+ skin[outputCount] = skin[i];
+ for (int j = 0; j < shapes.size(); ++j)
+ shapes[j].vertices[outputCount] = shapes[j].vertices[i];
+
+ next[outputCount] = hashTable[hashValue]; // link to hash table
+ hashTable[hashValue] = outputCount++; // update hash heads and increase output counter
+ }
+ else
+ {
+ Assert (offset < i);
+ remap[i] = offset;
+ }
+ }
+
+ delete[] hashTable; // cleanup
+ if (outputCount < vertices.size())
+ {
+ vertices.resize(outputCount);
+ if (!skin.empty())
+ skin.resize_initialized(outputCount);
+ for (int j = 0; j < shapes.size(); ++j)
+ shapes[j].vertices.resize(outputCount);
+ return true;
+ }
+ else
+ return false;
+}
+
+void WeldVertices (ImportMesh& mesh)
+{
+ std::vector<int> remap;
+ if (weld (mesh.vertices, mesh.skin, mesh.shapes, remap))
+ {
+ UInt32* indices = &mesh.polygons[0];
+ for (int i=0;i<mesh.polygons.size();i++)
+ indices[i] = remap[indices[i]];
+ }
+}
+
+#endif
+
+bool WeldVertexArray(dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap)
+{
+ Mesh::BoneInfluenceContainer skin;
+ return WeldVertexArray(vertices, skin, triangles, remap);
+}
+
+bool WeldVertexArray(dynamic_array<Vector3f>& vertices, Mesh::BoneInfluenceContainer& skin, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap)
+{
+ const int NIL = -1; // linked list terminator symbol
+ int outputCount = 0; // # of output vertices
+ int hashSize = nextPowerOfTwo(vertices.size()); // size of the hash table
+ int* hashTable = new int[hashSize + vertices.size()]; // hash table + linked list
+ int* next = hashTable + hashSize; // use bottom part as linked list
+
+ remap.resize_uninitialized(vertices.size());
+
+ memset (hashTable, NIL, (hashSize) * sizeof(int)); // init hash table (NIL = 0xFFFFFFFF so memset works)
+
+ for (int i = 0; i < vertices.size(); i++)
+ {
+ const Vector3f& v = vertices[i];
+ UInt32 hashValue = GetVector3HashValue(v) & (hashSize-1);
+ int offset = hashTable[hashValue];
+ while (offset != NIL)
+ {
+ Assert (offset < i);
+ if (vertices[offset] == v)
+ {
+ if (skin.empty() || CompareBone(skin[i], skin[offset]))
+ break;
+ }
+ offset = next[offset];
+ }
+
+ if (offset == NIL) // no match found - copy vertex & add to hash
+ {
+ remap[i] = outputCount;
+ vertices[outputCount] = v; // copy vertex
+
+ if (!skin.empty())
+ skin[outputCount] = skin[i];
+
+ next[outputCount] = hashTable[hashValue]; // link to hash table
+ hashTable[hashValue] = outputCount++; // update hash heads and increase output counter
+ }
+ else
+ {
+ Assert (offset < i);
+ remap[i] = offset;
+ }
+ }
+
+ delete[] hashTable; // cleanup
+
+ if (outputCount < vertices.size())
+ {
+ vertices.resize_uninitialized(outputCount);
+ if (!skin.empty())
+ skin.resize_uninitialized(outputCount);
+ for (int i=0;i<triangles.size();i++)
+ triangles[i] = remap[triangles[i]];
+ return true;
+ }
+ return false;
+}
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+SUITE (VertexWeldingTests)
+{
+
+ TEST (TestVertexWelding)
+ {
+ Vector3f vertices[] = { Vector3f (0,0,0), Vector3f (1,0,0), Vector3f (1,0,0), Vector3f (0,0,0) };
+ dynamic_array<Vector3f> dVertices; dVertices.assign(vertices, vertices + ARRAY_SIZE(vertices));
+
+ UInt16 indices[] = { 0, 1, 2, 3 };
+ dynamic_array<UInt16> dIndices; dIndices.assign(indices, indices + ARRAY_SIZE(indices));
+
+ dynamic_array<UInt16> remap;
+
+ WeldVertexArray(dVertices, dIndices, remap);
+
+ CHECK_EQUAL(2, dVertices.size());
+ CHECK(Vector3f(0,0,0) == dVertices[0]);
+ CHECK(Vector3f(1,0,0) == dVertices[1]);
+
+ CHECK(0 == dIndices[0]);
+ CHECK(1 == dIndices[1]);
+ CHECK(1 == dIndices[2]);
+ CHECK(0 == dIndices[3]);
+
+ CHECK_EQUAL(4, remap.size());
+ CHECK_EQUAL(0, remap[0]);
+ CHECK_EQUAL(1, remap[1]);
+ CHECK_EQUAL(1, remap[2]);
+ CHECK_EQUAL(0, remap[3]);
+ }
+}
+#endif