StaticCompoundShape.cpp 24 KB

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