Indexify.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Geometry/Indexify.h>
  6. #include <Jolt/Geometry/AABox.h>
  7. JPH_NAMESPACE_BEGIN
  8. static JPH_INLINE const Float3 &sIndexifyGetFloat3(const TriangleList &inTriangles, uint32 inVertexIndex)
  9. {
  10. return inTriangles[inVertexIndex / 3].mV[inVertexIndex % 3];
  11. }
  12. static JPH_INLINE Vec3 sIndexifyGetVec3(const TriangleList &inTriangles, uint32 inVertexIndex)
  13. {
  14. return Vec3::sLoadFloat3Unsafe(sIndexifyGetFloat3(inTriangles, inVertexIndex));
  15. }
  16. static void sIndexifyVerticesBruteForce(const TriangleList &inTriangles, const uint32 *inVertexIndices, const uint32 *inVertexIndicesEnd, Array<uint32> &ioWeldedVertices, float inVertexWeldDistance)
  17. {
  18. float weld_dist_sq = Square(inVertexWeldDistance);
  19. // Compare every vertex
  20. for (const uint32 *v1_idx = inVertexIndices; v1_idx < inVertexIndicesEnd; ++v1_idx)
  21. {
  22. Vec3 v1 = sIndexifyGetVec3(inTriangles, *v1_idx);
  23. // with every other vertex...
  24. for (const uint32 *v2_idx = v1_idx + 1; v2_idx < inVertexIndicesEnd; ++v2_idx)
  25. {
  26. Vec3 v2 = sIndexifyGetVec3(inTriangles, *v2_idx);
  27. // If they're weldable
  28. if ((v2 - v1).LengthSq() <= weld_dist_sq)
  29. {
  30. // Find the lowest indices both indices link to
  31. uint32 idx1 = *v1_idx;
  32. for (;;)
  33. {
  34. uint32 new_idx1 = ioWeldedVertices[idx1];
  35. if (new_idx1 >= idx1)
  36. break;
  37. idx1 = new_idx1;
  38. }
  39. uint32 idx2 = *v2_idx;
  40. for (;;)
  41. {
  42. uint32 new_idx2 = ioWeldedVertices[idx2];
  43. if (new_idx2 >= idx2)
  44. break;
  45. idx2 = new_idx2;
  46. }
  47. // Order the vertices
  48. uint32 lowest = min(idx1, idx2);
  49. uint32 highest = max(idx1, idx2);
  50. // Link highest to lowest
  51. ioWeldedVertices[highest] = lowest;
  52. // Also update the vertices we started from to avoid creating long chains
  53. ioWeldedVertices[*v1_idx] = lowest;
  54. ioWeldedVertices[*v2_idx] = lowest;
  55. break;
  56. }
  57. }
  58. }
  59. }
  60. static void sIndexifyVerticesRecursively(const TriangleList &inTriangles, uint32 *ioVertexIndices, uint inNumVertices, uint32 *ioScratch, Array<uint32> &ioWeldedVertices, float inVertexWeldDistance, uint inMaxRecursion)
  61. {
  62. // Check if we have few enough vertices to do a brute force search
  63. // Or if we've recursed too deep (this means we chipped off a few vertices each iteration because all points are very close)
  64. if (inNumVertices <= 8 || inMaxRecursion == 0)
  65. {
  66. sIndexifyVerticesBruteForce(inTriangles, ioVertexIndices, ioVertexIndices + inNumVertices, ioWeldedVertices, inVertexWeldDistance);
  67. return;
  68. }
  69. // Calculate bounds
  70. AABox bounds;
  71. for (const uint32 *v = ioVertexIndices, *v_end = ioVertexIndices + inNumVertices; v < v_end; ++v)
  72. bounds.Encapsulate(sIndexifyGetVec3(inTriangles, *v));
  73. // Determine split plane
  74. int split_axis = bounds.GetExtent().GetHighestComponentIndex();
  75. float split_value = bounds.GetCenter()[split_axis];
  76. // Partition vertices
  77. uint32 *v_read = ioVertexIndices, *v_write = ioVertexIndices, *v_end = ioVertexIndices + inNumVertices;
  78. uint32 *scratch = ioScratch;
  79. while (v_read < v_end)
  80. {
  81. // Calculate distance to plane
  82. float distance_to_split_plane = sIndexifyGetFloat3(inTriangles, *v_read)[split_axis] - split_value;
  83. if (distance_to_split_plane < -inVertexWeldDistance)
  84. {
  85. // Vertex is on the right side
  86. *v_write = *v_read;
  87. ++v_read;
  88. ++v_write;
  89. }
  90. else if (distance_to_split_plane > inVertexWeldDistance)
  91. {
  92. // Vertex is on the wrong side, swap with the last vertex
  93. --v_end;
  94. swap(*v_read, *v_end);
  95. }
  96. else
  97. {
  98. // Vertex is too close to the split plane, it goes on both sides
  99. *scratch++ = *v_read++;
  100. }
  101. }
  102. // Check if we made any progress
  103. uint num_vertices_on_both_sides = (uint)(scratch - ioScratch);
  104. if (num_vertices_on_both_sides == inNumVertices)
  105. {
  106. sIndexifyVerticesBruteForce(inTriangles, ioVertexIndices, ioVertexIndices + inNumVertices, ioWeldedVertices, inVertexWeldDistance);
  107. return;
  108. }
  109. // Calculate how we classified the vertices
  110. uint num_vertices_left = (uint)(v_write - ioVertexIndices);
  111. uint num_vertices_right = (uint)(ioVertexIndices + inNumVertices - v_end);
  112. JPH_ASSERT(num_vertices_left + num_vertices_right + num_vertices_on_both_sides == inNumVertices);
  113. memcpy(v_write, ioScratch, num_vertices_on_both_sides * sizeof(uint32));
  114. // Recurse
  115. uint max_recursion = inMaxRecursion - 1;
  116. sIndexifyVerticesRecursively(inTriangles, ioVertexIndices, num_vertices_left + num_vertices_on_both_sides, ioScratch, ioWeldedVertices, inVertexWeldDistance, max_recursion);
  117. sIndexifyVerticesRecursively(inTriangles, ioVertexIndices + num_vertices_left, num_vertices_right + num_vertices_on_both_sides, ioScratch, ioWeldedVertices, inVertexWeldDistance, max_recursion);
  118. }
  119. void Indexify(const TriangleList &inTriangles, VertexList &outVertices, IndexedTriangleList &outTriangles, float inVertexWeldDistance)
  120. {
  121. uint num_triangles = (uint)inTriangles.size();
  122. uint num_vertices = num_triangles * 3;
  123. // Create a list of all vertex indices
  124. Array<uint32> vertex_indices;
  125. vertex_indices.resize(num_vertices);
  126. for (uint i = 0; i < num_vertices; ++i)
  127. vertex_indices[i] = i;
  128. // Link each vertex to itself
  129. Array<uint32> welded_vertices;
  130. welded_vertices.resize(num_vertices);
  131. for (uint i = 0; i < num_vertices; ++i)
  132. welded_vertices[i] = i;
  133. // A scope to free memory used by the scratch array
  134. {
  135. // Some scratch memory, used for the vertices that fall in both partitions
  136. Array<uint32> scratch;
  137. scratch.resize(num_vertices);
  138. // Recursively split the vertices
  139. sIndexifyVerticesRecursively(inTriangles, vertex_indices.data(), num_vertices, scratch.data(), welded_vertices, inVertexWeldDistance, 32);
  140. }
  141. // Do a pass to complete the welding, linking each vertex to the vertex it is welded to
  142. // (and since we're going from 0 to N we can be sure that the vertex we're linking to is already linked to the lowest vertex)
  143. uint num_resulting_vertices = 0;
  144. for (uint i = 0; i < num_vertices; ++i)
  145. {
  146. JPH_ASSERT(welded_vertices[welded_vertices[i]] <= welded_vertices[i]);
  147. welded_vertices[i] = welded_vertices[welded_vertices[i]];
  148. if (welded_vertices[i] == i)
  149. ++num_resulting_vertices;
  150. }
  151. // Collect the vertices
  152. outVertices.clear();
  153. outVertices.reserve(num_resulting_vertices);
  154. for (uint i = 0; i < num_vertices; ++i)
  155. if (welded_vertices[i] == i)
  156. {
  157. // New vertex
  158. welded_vertices[i] = (uint32)outVertices.size();
  159. outVertices.push_back(sIndexifyGetFloat3(inTriangles, i));
  160. }
  161. else
  162. {
  163. // Reused vertex, remap index
  164. welded_vertices[i] = welded_vertices[welded_vertices[i]];
  165. }
  166. // Create indexed triangles
  167. outTriangles.clear();
  168. outTriangles.reserve(num_triangles);
  169. for (uint t = 0; t < num_triangles; ++t)
  170. {
  171. IndexedTriangle it;
  172. it.mMaterialIndex = inTriangles[t].mMaterialIndex;
  173. for (int v = 0; v < 3; ++v)
  174. it.mIdx[v] = welded_vertices[t * 3 + v];
  175. if (!it.IsDegenerate(outVertices))
  176. outTriangles.push_back(it);
  177. }
  178. }
  179. void Deindexify(const VertexList &inVertices, const IndexedTriangleList &inTriangles, TriangleList &outTriangles)
  180. {
  181. outTriangles.resize(inTriangles.size());
  182. for (size_t t = 0; t < inTriangles.size(); ++t)
  183. {
  184. outTriangles[t].mMaterialIndex = inTriangles[t].mMaterialIndex;
  185. for (int v = 0; v < 3; ++v)
  186. outTriangles[t].mV[v] = inVertices[inTriangles[t].mIdx[v]];
  187. }
  188. }
  189. JPH_NAMESPACE_END