StaticCompoundShape.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
  5. #include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
  6. #include <Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h>
  7. #include <Jolt/Core/Profiler.h>
  8. #include <Jolt/Core/StreamIn.h>
  9. #include <Jolt/Core/StreamOut.h>
  10. #include <Jolt/Core/TempAllocator.h>
  11. #include <Jolt/ObjectStream/TypeDeclarations.h>
  12. JPH_NAMESPACE_BEGIN
  13. JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(StaticCompoundShapeSettings)
  14. {
  15. JPH_ADD_BASE_CLASS(StaticCompoundShapeSettings, CompoundShapeSettings)
  16. }
  17. ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create(TempAllocator &inTempAllocator) const
  18. {
  19. if (mCachedResult.IsEmpty())
  20. {
  21. if (mSubShapes.size() == 0)
  22. {
  23. // It's an error to create a compound with no subshapes (the compound cannot encode this)
  24. mCachedResult.SetError("Compound needs a sub shape!");
  25. }
  26. else if (mSubShapes.size() == 1)
  27. {
  28. // If there's only 1 part, we can use a RotatedTranslatedShape instead
  29. RotatedTranslatedShapeSettings settings;
  30. const SubShapeSettings &s = mSubShapes[0];
  31. settings.mPosition = s.mPosition;
  32. settings.mRotation = s.mRotation;
  33. settings.mInnerShape = s.mShape;
  34. settings.mInnerShapePtr = s.mShapePtr;
  35. Ref<Shape> shape = new RotatedTranslatedShape(settings, mCachedResult);
  36. }
  37. else
  38. {
  39. // Build a regular compound shape
  40. Ref<Shape> shape = new StaticCompoundShape(*this, inTempAllocator, mCachedResult);
  41. }
  42. }
  43. return mCachedResult;
  44. }
  45. ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create() const
  46. {
  47. TempAllocatorMalloc allocator;
  48. return Create(allocator);
  49. }
  50. void StaticCompoundShape::Node::SetChildInvalid(uint inIndex)
  51. {
  52. // Make this an invalid node
  53. mNodeProperties[inIndex] = INVALID_NODE;
  54. // Make bounding box invalid
  55. mBoundsMinX[inIndex] = HALF_FLT_MAX;
  56. mBoundsMinY[inIndex] = HALF_FLT_MAX;
  57. mBoundsMinZ[inIndex] = HALF_FLT_MAX;
  58. mBoundsMaxX[inIndex] = HALF_FLT_MAX;
  59. mBoundsMaxY[inIndex] = HALF_FLT_MAX;
  60. mBoundsMaxZ[inIndex] = HALF_FLT_MAX;
  61. }
  62. void StaticCompoundShape::Node::SetChildBounds(uint inIndex, const AABox &inBounds)
  63. {
  64. mBoundsMinX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetX());
  65. mBoundsMinY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetY());
  66. mBoundsMinZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetZ());
  67. mBoundsMaxX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetX());
  68. mBoundsMaxY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetY());
  69. mBoundsMaxZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetZ());
  70. }
  71. void StaticCompoundShape::sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint)
  72. {
  73. // Handle trivial case
  74. if (inNumber <= 4)
  75. {
  76. outMidPoint = inNumber / 2;
  77. return;
  78. }
  79. // Calculate bounding box of box centers
  80. Vec3 center_min = Vec3::sReplicate(FLT_MAX);
  81. Vec3 center_max = Vec3::sReplicate(-FLT_MAX);
  82. for (const AABox *b = ioBounds, *b_end = ioBounds + inNumber; b < b_end; ++b)
  83. {
  84. Vec3 center = b->GetCenter();
  85. center_min = Vec3::sMin(center_min, center);
  86. center_max = Vec3::sMax(center_max, center);
  87. }
  88. // Calculate split plane
  89. int dimension = (center_max - center_min).GetHighestComponentIndex();
  90. float split = 0.5f * (center_min + center_max)[dimension];
  91. // Divide bodies
  92. int start = 0, end = inNumber;
  93. while (start < end)
  94. {
  95. // Search for first element that is on the right hand side of the split plane
  96. while (start < end && ioBounds[start].GetCenter()[dimension] < split)
  97. ++start;
  98. // Search for the first element that is on the left hand side of the split plane
  99. while (start < end && ioBounds[end - 1].GetCenter()[dimension] >= split)
  100. --end;
  101. if (start < end)
  102. {
  103. // Swap the two elements
  104. swap(ioBodyIdx[start], ioBodyIdx[end - 1]);
  105. swap(ioBounds[start], ioBounds[end - 1]);
  106. ++start;
  107. --end;
  108. }
  109. }
  110. JPH_ASSERT(start == end);
  111. if (start > 0 && start < inNumber)
  112. {
  113. // Success!
  114. outMidPoint = start;
  115. }
  116. else
  117. {
  118. // Failed to divide bodies
  119. outMidPoint = inNumber / 2;
  120. }
  121. }
  122. void StaticCompoundShape::sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit)
  123. {
  124. uint *body_idx = ioBodyIdx + inBegin;
  125. AABox *node_bounds = ioBounds + inBegin;
  126. int number = inEnd - inBegin;
  127. // Partition entire range
  128. sPartition(body_idx, node_bounds, number, outSplit[2]);
  129. // Partition lower half
  130. sPartition(body_idx, node_bounds, outSplit[2], outSplit[1]);
  131. // Partition upper half
  132. sPartition(body_idx + outSplit[2], node_bounds + outSplit[2], number - outSplit[2], outSplit[3]);
  133. // Convert to proper range
  134. outSplit[0] = inBegin;
  135. outSplit[1] += inBegin;
  136. outSplit[2] += inBegin;
  137. outSplit[3] += outSplit[2];
  138. outSplit[4] = inEnd;
  139. }
  140. StaticCompoundShape::StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult) :
  141. CompoundShape(EShapeSubType::StaticCompound, inSettings, outResult)
  142. {
  143. // Check that there's at least 1 shape
  144. uint num_subshapes = (uint)inSettings.mSubShapes.size();
  145. if (num_subshapes < 2)
  146. {
  147. outResult.SetError("Compound needs at least 2 sub shapes, otherwise you should use a RotatedTranslatedShape!");
  148. return;
  149. }
  150. // Keep track of total mass to calculate center of mass
  151. float mass = 0.0f;
  152. mSubShapes.resize(num_subshapes);
  153. for (uint i = 0; i < num_subshapes; ++i)
  154. {
  155. const CompoundShapeSettings::SubShapeSettings &shape = inSettings.mSubShapes[i];
  156. // Start constructing the runtime sub shape
  157. SubShape &out_shape = mSubShapes[i];
  158. if (!out_shape.FromSettings(shape, outResult))
  159. return;
  160. // Calculate mass properties of child
  161. MassProperties child = out_shape.mShape->GetMassProperties();
  162. // Accumulate center of mass
  163. mass += child.mMass;
  164. mCenterOfMass += out_shape.GetPositionCOM() * child.mMass;
  165. }
  166. if (mass > 0.0f)
  167. mCenterOfMass /= mass;
  168. // Cache the inner radius as it can take a while to recursively iterate over all sub shapes
  169. CalculateInnerRadius();
  170. // Temporary storage for the bounding boxes of all shapes
  171. uint bounds_size = num_subshapes * sizeof(AABox);
  172. AABox *bounds = (AABox *)inTempAllocator.Allocate(bounds_size);
  173. // Temporary storage for body indexes (we're shuffling them)
  174. uint body_idx_size = num_subshapes * sizeof(uint);
  175. uint *body_idx = (uint *)inTempAllocator.Allocate(body_idx_size);
  176. // Shift all shapes so that the center of mass is now at the origin and calculate bounds
  177. for (uint i = 0; i < num_subshapes; ++i)
  178. {
  179. SubShape &shape = mSubShapes[i];
  180. // Shift the shape so it's centered around our center of mass
  181. shape.SetPositionCOM(shape.GetPositionCOM() - mCenterOfMass);
  182. // Transform the shape's bounds into our local space
  183. Mat44 transform = Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM());
  184. AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f));
  185. // Store bounds and body index for tree construction
  186. bounds[i] = shape_bounds;
  187. body_idx[i] = i;
  188. // Update our local bounds
  189. mLocalBounds.Encapsulate(shape_bounds);
  190. }
  191. // The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here
  192. struct StackEntry
  193. {
  194. uint32 mNodeIdx; // Node index of node that is generated
  195. int mChildIdx; // Index of child that we're currently processing
  196. int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions
  197. AABox mBounds; // Bounding box of this node
  198. };
  199. uint stack_size = num_subshapes * sizeof(StackEntry);
  200. StackEntry *stack = (StackEntry *)inTempAllocator.Allocate(stack_size);
  201. int top = 0;
  202. // Reserve enough space so that every sub shape gets its own leaf node
  203. uint next_node_idx = 0;
  204. mNodes.resize(num_subshapes + (num_subshapes + 2) / 3); // = Sum(num_subshapes * 4^-i) with i = [0, Inf].
  205. // Create root node
  206. stack[0].mNodeIdx = next_node_idx++;
  207. stack[0].mChildIdx = -1;
  208. stack[0].mBounds = AABox();
  209. sPartition4(body_idx, bounds, 0, num_subshapes, stack[0].mSplit);
  210. for (;;)
  211. {
  212. StackEntry &cur_stack = stack[top];
  213. // Next child
  214. cur_stack.mChildIdx++;
  215. // Check if all children processed
  216. if (cur_stack.mChildIdx >= 4)
  217. {
  218. // Terminate if there's nothing left to pop
  219. if (top <= 0)
  220. break;
  221. // Add our bounds to our parents bounds
  222. StackEntry &prev_stack = stack[top - 1];
  223. prev_stack.mBounds.Encapsulate(cur_stack.mBounds);
  224. // Store this node's properties in the parent node
  225. Node &parent_node = mNodes[prev_stack.mNodeIdx];
  226. parent_node.mNodeProperties[prev_stack.mChildIdx] = cur_stack.mNodeIdx;
  227. parent_node.SetChildBounds(prev_stack.mChildIdx, cur_stack.mBounds);
  228. // Pop entry from stack
  229. --top;
  230. }
  231. else
  232. {
  233. // Get low and high index to bodies to process
  234. int low = cur_stack.mSplit[cur_stack.mChildIdx];
  235. int high = cur_stack.mSplit[cur_stack.mChildIdx + 1];
  236. int num_bodies = high - low;
  237. if (num_bodies == 0)
  238. {
  239. // Mark invalid
  240. Node &node = mNodes[cur_stack.mNodeIdx];
  241. node.SetChildInvalid(cur_stack.mChildIdx);
  242. }
  243. else if (num_bodies == 1)
  244. {
  245. // Get body info
  246. uint child_node_idx = body_idx[low];
  247. const AABox &child_bounds = bounds[low];
  248. // Update node
  249. Node &node = mNodes[cur_stack.mNodeIdx];
  250. node.mNodeProperties[cur_stack.mChildIdx] = child_node_idx | IS_SUBSHAPE;
  251. node.SetChildBounds(cur_stack.mChildIdx, child_bounds);
  252. // Encapsulate bounding box in parent
  253. cur_stack.mBounds.Encapsulate(child_bounds);
  254. }
  255. else
  256. {
  257. // Allocate new node
  258. StackEntry &new_stack = stack[++top];
  259. JPH_ASSERT(top < (int)num_subshapes);
  260. new_stack.mNodeIdx = next_node_idx++;
  261. new_stack.mChildIdx = -1;
  262. new_stack.mBounds = AABox();
  263. sPartition4(body_idx, bounds, low, high, new_stack.mSplit);
  264. }
  265. }
  266. }
  267. // Resize nodes to actual size
  268. JPH_ASSERT(next_node_idx <= mNodes.size());
  269. mNodes.resize(next_node_idx);
  270. mNodes.shrink_to_fit();
  271. // Free temporary memory
  272. inTempAllocator.Free(stack, stack_size);
  273. inTempAllocator.Free(body_idx, body_idx_size);
  274. inTempAllocator.Free(bounds, bounds_size);
  275. // Check if we ran out of bits for addressing a node
  276. if (next_node_idx > IS_SUBSHAPE)
  277. {
  278. outResult.SetError("Compound hierarchy has too many nodes");
  279. return;
  280. }
  281. // Check if we're not exceeding the amount of sub shape id bits
  282. if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits)
  283. {
  284. outResult.SetError("Compound hierarchy is too deep and exceeds the amount of available sub shape ID bits");
  285. return;
  286. }
  287. outResult.Set(this);
  288. }
  289. template <class Visitor>
  290. inline void StaticCompoundShape::WalkTree(Visitor &ioVisitor) const
  291. {
  292. uint32 node_stack[cStackSize];
  293. node_stack[0] = 0;
  294. int top = 0;
  295. do
  296. {
  297. // Test if the node is valid, the node should rarely be invalid but it is possible when testing
  298. // a really large box against the tree that the invalid nodes will intersect with the box
  299. uint32 node_properties = node_stack[top];
  300. if (node_properties != INVALID_NODE)
  301. {
  302. // Test if node contains triangles
  303. bool is_node = (node_properties & IS_SUBSHAPE) == 0;
  304. if (is_node)
  305. {
  306. const Node &node = mNodes[node_properties];
  307. // Unpack bounds
  308. UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMinX[0]));
  309. Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy);
  310. Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
  311. UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMinZ[0]));
  312. Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx);
  313. Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
  314. UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMaxY[0]));
  315. Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz);
  316. Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
  317. // Load properties for 4 children
  318. UVec4 properties = UVec4::sLoadInt4(&node.mNodeProperties[0]);
  319. // Check which sub nodes to visit
  320. int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, top);
  321. // Push them onto the stack
  322. JPH_ASSERT(top + 4 < cStackSize);
  323. properties.StoreInt4(&node_stack[top]);
  324. top += num_results;
  325. }
  326. else
  327. {
  328. // Points to a sub shape
  329. uint32 sub_shape_idx = node_properties ^ IS_SUBSHAPE;
  330. const SubShape &sub_shape = mSubShapes[sub_shape_idx];
  331. ioVisitor.VisitShape(sub_shape, sub_shape_idx);
  332. }
  333. // Check if we're done
  334. if (ioVisitor.ShouldAbort())
  335. break;
  336. }
  337. // Fetch next node until we find one that the visitor wants to see
  338. do
  339. --top;
  340. while (top >= 0 && !ioVisitor.ShouldVisitNode(top));
  341. }
  342. while (top >= 0);
  343. }
  344. bool StaticCompoundShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const
  345. {
  346. JPH_PROFILE_FUNCTION();
  347. struct Visitor : public CastRayVisitor
  348. {
  349. using CastRayVisitor::CastRayVisitor;
  350. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  351. {
  352. return mDistanceStack[inStackTop] < mHit.mFraction;
  353. }
  354. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
  355. {
  356. // Test bounds of 4 children
  357. Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  358. // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
  359. return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]);
  360. }
  361. float mDistanceStack[cStackSize];
  362. };
  363. Visitor visitor(inRay, this, inSubShapeIDCreator, ioHit);
  364. WalkTree(visitor);
  365. return visitor.mReturnValue;
  366. }
  367. void StaticCompoundShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const
  368. {
  369. // Test shape filter
  370. if (!inShapeFilter.ShouldCollide(inSubShapeIDCreator.GetID()))
  371. return;
  372. JPH_PROFILE_FUNCTION();
  373. struct Visitor : public CastRayVisitorCollector
  374. {
  375. using CastRayVisitorCollector::CastRayVisitorCollector;
  376. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  377. {
  378. return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();
  379. }
  380. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
  381. {
  382. // Test bounds of 4 children
  383. Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  384. // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
  385. return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
  386. }
  387. float mDistanceStack[cStackSize];
  388. };
  389. Visitor visitor(inRay, inRayCastSettings, this, inSubShapeIDCreator, ioCollector, inShapeFilter);
  390. WalkTree(visitor);
  391. }
  392. void StaticCompoundShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const
  393. {
  394. JPH_PROFILE_FUNCTION();
  395. struct Visitor : public CollidePointVisitor
  396. {
  397. using CollidePointVisitor::CollidePointVisitor;
  398. JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
  399. {
  400. return true;
  401. }
  402. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
  403. {
  404. // Test if point overlaps with box
  405. UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  406. return CountAndSortTrues(collides, ioProperties);
  407. }
  408. };
  409. Visitor visitor(inPoint, this, inSubShapeIDCreator, ioCollector, inShapeFilter);
  410. WalkTree(visitor);
  411. }
  412. void StaticCompoundShape::sCastShapeVsCompound(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector)
  413. {
  414. JPH_PROFILE_FUNCTION();
  415. struct Visitor : public CastShapeVisitor
  416. {
  417. using CastShapeVisitor::CastShapeVisitor;
  418. JPH_INLINE bool ShouldVisitNode(int inStackTop) const
  419. {
  420. return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();
  421. }
  422. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
  423. {
  424. // Test bounds of 4 children
  425. Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  426. // Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
  427. return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
  428. }
  429. float mDistanceStack[cStackSize];
  430. };
  431. JPH_ASSERT(inShape->GetSubType() == EShapeSubType::StaticCompound);
  432. const StaticCompoundShape *shape = static_cast<const StaticCompoundShape *>(inShape);
  433. Visitor visitor(inShapeCast, inShapeCastSettings, shape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector);
  434. shape->WalkTree(visitor);
  435. }
  436. void StaticCompoundShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const
  437. {
  438. JPH_PROFILE_FUNCTION();
  439. // Test shape filter
  440. if (!inShapeFilter.ShouldCollide(inSubShapeIDCreator.GetID()))
  441. return;
  442. struct Visitor : public CollectTransformedShapesVisitor
  443. {
  444. using CollectTransformedShapesVisitor::CollectTransformedShapesVisitor;
  445. JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
  446. {
  447. return true;
  448. }
  449. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
  450. {
  451. // Test which nodes collide
  452. UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  453. return CountAndSortTrues(collides, ioProperties);
  454. }
  455. };
  456. Visitor visitor(inBox, this, inPositionCOM, inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter);
  457. WalkTree(visitor);
  458. }
  459. int StaticCompoundShape::GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const
  460. {
  461. JPH_PROFILE_FUNCTION();
  462. GetIntersectingSubShapesVisitorSC<AABox> visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices);
  463. WalkTree(visitor);
  464. return visitor.GetNumResults();
  465. }
  466. int StaticCompoundShape::GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const
  467. {
  468. JPH_PROFILE_FUNCTION();
  469. GetIntersectingSubShapesVisitorSC<OrientedBox> visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices);
  470. WalkTree(visitor);
  471. return visitor.GetNumResults();
  472. }
  473. void StaticCompoundShape::sCollideCompoundVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
  474. {
  475. JPH_PROFILE_FUNCTION();
  476. JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::StaticCompound);
  477. const StaticCompoundShape *shape1 = static_cast<const StaticCompoundShape *>(inShape1);
  478. struct Visitor : public CollideCompoundVsShapeVisitor
  479. {
  480. using CollideCompoundVsShapeVisitor::CollideCompoundVsShapeVisitor;
  481. JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
  482. {
  483. return true;
  484. }
  485. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
  486. {
  487. // Test which nodes collide
  488. UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  489. return CountAndSortTrues(collides, ioProperties);
  490. }
  491. };
  492. Visitor visitor(shape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter);
  493. shape1->WalkTree(visitor);
  494. }
  495. void StaticCompoundShape::sCollideShapeVsCompound(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
  496. {
  497. JPH_PROFILE_FUNCTION();
  498. struct Visitor : public CollideShapeVsCompoundVisitor
  499. {
  500. using CollideShapeVsCompoundVisitor::CollideShapeVsCompoundVisitor;
  501. JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
  502. {
  503. return true;
  504. }
  505. JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
  506. {
  507. // Test which nodes collide
  508. UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
  509. return CountAndSortTrues(collides, ioProperties);
  510. }
  511. };
  512. JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::StaticCompound);
  513. const StaticCompoundShape *shape2 = static_cast<const StaticCompoundShape *>(inShape2);
  514. Visitor visitor(inShape1, shape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter);
  515. shape2->WalkTree(visitor);
  516. }
  517. void StaticCompoundShape::SaveBinaryState(StreamOut &inStream) const
  518. {
  519. CompoundShape::SaveBinaryState(inStream);
  520. inStream.Write(mNodes);
  521. }
  522. void StaticCompoundShape::RestoreBinaryState(StreamIn &inStream)
  523. {
  524. CompoundShape::RestoreBinaryState(inStream);
  525. inStream.Read(mNodes);
  526. }
  527. void StaticCompoundShape::sRegister()
  528. {
  529. ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::StaticCompound);
  530. f.mConstruct = []() -> Shape * { return new StaticCompoundShape; };
  531. f.mColor = Color::sOrange;
  532. for (EShapeSubType s : sAllSubShapeTypes)
  533. {
  534. CollisionDispatch::sRegisterCollideShape(EShapeSubType::StaticCompound, s, sCollideCompoundVsShape);
  535. CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::StaticCompound, sCollideShapeVsCompound);
  536. CollisionDispatch::sRegisterCastShape(s, EShapeSubType::StaticCompound, sCastShapeVsCompound);
  537. }
  538. }
  539. JPH_NAMESPACE_END