TriangleSplitterFixedLeafSize.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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/TriangleSplitter/TriangleSplitterFixedLeafSize.h>
  6. #include <Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.h>
  7. JPH_NAMESPACE_BEGIN
  8. TriangleSplitterFixedLeafSize::TriangleSplitterFixedLeafSize(const VertexList &inVertices, const IndexedTriangleList &inTriangles, uint inLeafSize, uint inMinNumBins, uint inMaxNumBins, uint inNumTrianglesPerBin) :
  9. TriangleSplitter(inVertices, inTriangles),
  10. mLeafSize(inLeafSize),
  11. mMinNumBins(inMinNumBins),
  12. mMaxNumBins(inMaxNumBins),
  13. mNumTrianglesPerBin(inNumTrianglesPerBin)
  14. {
  15. // Group the triangles
  16. TriangleGrouperClosestCentroid grouper;
  17. grouper.Group(inVertices, inTriangles, mLeafSize, mSortedTriangleIdx);
  18. // Pad triangles so that we have a multiple of mLeafSize
  19. const uint num_triangles = (uint)inTriangles.size();
  20. const uint num_groups = (num_triangles + mLeafSize - 1) / mLeafSize;
  21. const uint last_triangle_idx = mSortedTriangleIdx.back();
  22. for (uint t = num_triangles, t_end = num_groups * mLeafSize; t < t_end; ++t)
  23. mSortedTriangleIdx.push_back(last_triangle_idx);
  24. }
  25. Vec3 TriangleSplitterFixedLeafSize::GetCentroidForGroup(uint inFirstTriangleInGroup)
  26. {
  27. JPH_ASSERT(inFirstTriangleInGroup % mLeafSize == 0);
  28. AABox box;
  29. for (uint g = 0; g < mLeafSize; ++g)
  30. box.Encapsulate(mVertices, GetTriangle(inFirstTriangleInGroup + g));
  31. return box.GetCenter();
  32. }
  33. bool TriangleSplitterFixedLeafSize::Split(const Range &inTriangles, Range &outLeft, Range &outRight)
  34. {
  35. // Cannot split anything smaller than leaf size
  36. JPH_ASSERT(inTriangles.Count() > mLeafSize);
  37. JPH_ASSERT(inTriangles.Count() % mLeafSize == 0);
  38. // Calculate bounds for this range
  39. AABox centroid_bounds;
  40. for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; t += mLeafSize)
  41. centroid_bounds.Encapsulate(GetCentroidForGroup(t));
  42. float best_cp = FLT_MAX;
  43. uint best_dim = 0xffffffff;
  44. float best_split = 0;
  45. // Bin in all dimensions
  46. uint num_bins = Clamp(inTriangles.Count() / mNumTrianglesPerBin, mMinNumBins, mMaxNumBins);
  47. Array<Bin> bins(num_bins);
  48. for (uint dim = 0; dim < 3; ++dim)
  49. {
  50. float bounds_min = centroid_bounds.mMin[dim];
  51. float bounds_size = centroid_bounds.mMax[dim] - bounds_min;
  52. // Skip axis if too small
  53. if (bounds_size < 1.0e-5f)
  54. continue;
  55. // Initialize bins
  56. for (uint b = 0; b < num_bins; ++b)
  57. {
  58. Bin &bin = bins[b];
  59. bin.mBounds.SetEmpty();
  60. bin.mMinCentroid = bounds_min + bounds_size * (b + 1) / num_bins;
  61. bin.mNumTriangles = 0;
  62. }
  63. // Bin all triangles
  64. for (uint t = inTriangles.mBegin; t < inTriangles.mEnd; t += mLeafSize)
  65. {
  66. // Calculate average centroid for group
  67. float centroid_pos = GetCentroidForGroup(t)[dim];
  68. // Select bin
  69. uint bin_no = min(uint((centroid_pos - bounds_min) / bounds_size * num_bins), num_bins - 1);
  70. Bin &bin = bins[bin_no];
  71. // Put all triangles of group in same bin
  72. for (uint g = 0; g < mLeafSize; ++g)
  73. bin.mBounds.Encapsulate(mVertices, GetTriangle(t + g));
  74. bin.mMinCentroid = min(bin.mMinCentroid, centroid_pos);
  75. bin.mNumTriangles += mLeafSize;
  76. }
  77. // Calculate totals left to right
  78. AABox prev_bounds;
  79. int prev_triangles = 0;
  80. for (uint b = 0; b < num_bins; ++b)
  81. {
  82. Bin &bin = bins[b];
  83. bin.mBoundsAccumulatedLeft = prev_bounds; // Don't include this node as we'll take a split on the left side of the bin
  84. bin.mNumTrianglesAccumulatedLeft = prev_triangles;
  85. prev_bounds.Encapsulate(bin.mBounds);
  86. prev_triangles += bin.mNumTriangles;
  87. }
  88. // Calculate totals right to left
  89. prev_bounds.SetEmpty();
  90. prev_triangles = 0;
  91. for (int b = num_bins - 1; b >= 0; --b)
  92. {
  93. Bin &bin = bins[b];
  94. prev_bounds.Encapsulate(bin.mBounds);
  95. prev_triangles += bin.mNumTriangles;
  96. bin.mBoundsAccumulatedRight = prev_bounds;
  97. bin.mNumTrianglesAccumulatedRight = prev_triangles;
  98. }
  99. // Get best splitting plane
  100. for (uint b = 1; b < num_bins; ++b) // Start at 1 since selecting bin 0 would result in everything ending up on the right side
  101. {
  102. // Calculate surface area heuristic and see if it is better than the current best
  103. const Bin &bin = bins[b];
  104. float cp = bin.mBoundsAccumulatedLeft.GetSurfaceArea() * bin.mNumTrianglesAccumulatedLeft + bin.mBoundsAccumulatedRight.GetSurfaceArea() * bin.mNumTrianglesAccumulatedRight;
  105. if (cp < best_cp)
  106. {
  107. best_cp = cp;
  108. best_dim = dim;
  109. best_split = bin.mMinCentroid;
  110. }
  111. }
  112. }
  113. // No split found?
  114. if (best_dim == 0xffffffff)
  115. return false;
  116. // Divide triangles
  117. uint start = inTriangles.mBegin, end = inTriangles.mEnd;
  118. while (start < end)
  119. {
  120. // Search for first element that is on the right hand side of the split plane
  121. while (start < end && GetCentroidForGroup(start)[best_dim] < best_split)
  122. start += mLeafSize;
  123. // Search for the first element that is on the left hand side of the split plane
  124. while (start < end && GetCentroidForGroup(end - mLeafSize)[best_dim] >= best_split)
  125. end -= mLeafSize;
  126. if (start < end)
  127. {
  128. // Swap the two elements
  129. for (uint g = 0; g < mLeafSize; ++g)
  130. swap(mSortedTriangleIdx[start + g], mSortedTriangleIdx[end - mLeafSize + g]);
  131. start += mLeafSize;
  132. end -= mLeafSize;
  133. }
  134. }
  135. JPH_ASSERT(start == end);
  136. // No suitable split found, doing random split in half
  137. if (start == inTriangles.mBegin || start == inTriangles.mEnd)
  138. start = inTriangles.mBegin + (inTriangles.Count() / mLeafSize + 1) / 2 * mLeafSize;
  139. outLeft = Range(inTriangles.mBegin, start);
  140. outRight = Range(start, inTriangles.mEnd);
  141. JPH_ASSERT(outLeft.mEnd > outLeft.mBegin && outRight.mEnd > outRight.mBegin);
  142. JPH_ASSERT(outLeft.Count() % mLeafSize == 0 && outRight.Count() % mLeafSize == 0);
  143. return true;
  144. }
  145. JPH_NAMESPACE_END