TriangleSplitterFixedLeafSize.cpp 5.6 KB

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