StaticCompoundShape.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  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/Core/ScopeExit.h>
  13. #include <Jolt/ObjectStream/TypeDeclarations.h>
  14. JPH_NAMESPACE_BEGIN
  15. JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(StaticCompoundShapeSettings)
  16. {
  17. JPH_ADD_BASE_CLASS(StaticCompoundShapeSettings, CompoundShapeSettings)
  18. }
  19. ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create(TempAllocator &inTempAllocator) const
  20. {
  21. if (mCachedResult.IsEmpty())
  22. {
  23. if (mSubShapes.size() == 0)
  24. {
  25. // It's an error to create a compound with no subshapes (the compound cannot encode this)
  26. mCachedResult.SetError("Compound needs a sub shape!");
  27. }
  28. else if (mSubShapes.size() == 1)
  29. {
  30. // If there's only 1 part we don't need a StaticCompoundShape
  31. const SubShapeSettings &s = mSubShapes[0];
  32. if (s.mPosition == Vec3::sZero()
  33. && s.mRotation == Quat::sIdentity())
  34. {
  35. // No rotation or translation, we can use the shape directly
  36. if (s.mShapePtr != nullptr)
  37. mCachedResult.Set(const_cast<Shape *>(s.mShapePtr.GetPtr()));
  38. else if (s.mShape != nullptr)
  39. mCachedResult = s.mShape->Create();
  40. else
  41. mCachedResult.SetError("Sub shape is null!");
  42. }
  43. else
  44. {
  45. // We can use a RotatedTranslatedShape instead
  46. RotatedTranslatedShapeSettings settings;
  47. settings.mPosition = s.mPosition;
  48. settings.mRotation = s.mRotation;
  49. settings.mInnerShape = s.mShape;
  50. settings.mInnerShapePtr = s.mShapePtr;
  51. Ref<Shape> shape = new RotatedTranslatedShape(settings, mCachedResult);
  52. }
  53. }
  54. else
  55. {
  56. // Build a regular compound shape
  57. Ref<Shape> shape = new StaticCompoundShape(*this, inTempAllocator, mCachedResult);
  58. }
  59. }
  60. return mCachedResult;
  61. }
  62. ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create() const
  63. {
  64. TempAllocatorMalloc allocator;
  65. return Create(allocator);
  66. }
  67. void StaticCompoundShape::Node::SetChildInvalid(uint inIndex)
  68. {
  69. // Make this an invalid node
  70. mNodeProperties[inIndex] = INVALID_NODE;
  71. // Make bounding box invalid
  72. mBoundsMinX[inIndex] = HALF_FLT_MAX;
  73. mBoundsMinY[inIndex] = HALF_FLT_MAX;
  74. mBoundsMinZ[inIndex] = HALF_FLT_MAX;
  75. mBoundsMaxX[inIndex] = HALF_FLT_MAX;
  76. mBoundsMaxY[inIndex] = HALF_FLT_MAX;
  77. mBoundsMaxZ[inIndex] = HALF_FLT_MAX;
  78. }
  79. void StaticCompoundShape::Node::SetChildBounds(uint inIndex, const AABox &inBounds)
  80. {
  81. mBoundsMinX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetX());
  82. mBoundsMinY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetY());
  83. mBoundsMinZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetZ());
  84. mBoundsMaxX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetX());
  85. mBoundsMaxY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetY());
  86. mBoundsMaxZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetZ());
  87. }
  88. void StaticCompoundShape::sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint)
  89. {
  90. // Handle trivial case
  91. if (inNumber <= 4)
  92. {
  93. outMidPoint = inNumber / 2;
  94. return;
  95. }
  96. // Calculate bounding box of box centers
  97. Vec3 center_min = Vec3::sReplicate(FLT_MAX);
  98. Vec3 center_max = Vec3::sReplicate(-FLT_MAX);
  99. for (const AABox *b = ioBounds, *b_end = ioBounds + inNumber; b < b_end; ++b)
  100. {
  101. Vec3 center = b->GetCenter();
  102. center_min = Vec3::sMin(center_min, center);
  103. center_max = Vec3::sMax(center_max, center);
  104. }
  105. // Calculate split plane
  106. int dimension = (center_max - center_min).GetHighestComponentIndex();
  107. float split = 0.5f * (center_min + center_max)[dimension];
  108. // Divide bodies
  109. int start = 0, end = inNumber;
  110. while (start < end)
  111. {
  112. // Search for first element that is on the right hand side of the split plane
  113. while (start < end && ioBounds[start].GetCenter()[dimension] < split)
  114. ++start;
  115. // Search for the first element that is on the left hand side of the split plane
  116. while (start < end && ioBounds[end - 1].GetCenter()[dimension] >= split)
  117. --end;
  118. if (start < end)
  119. {
  120. // Swap the two elements
  121. std::swap(ioBodyIdx[start], ioBodyIdx[end - 1]);
  122. std::swap(ioBounds[start], ioBounds[end - 1]);
  123. ++start;
  124. --end;
  125. }
  126. }
  127. JPH_ASSERT(start == end);
  128. if (start > 0 && start < inNumber)
  129. {
  130. // Success!
  131. outMidPoint = start;
  132. }
  133. else
  134. {
  135. // Failed to divide bodies
  136. outMidPoint = inNumber / 2;
  137. }
  138. }
  139. void StaticCompoundShape::sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit)
  140. {
  141. uint *body_idx = ioBodyIdx + inBegin;
  142. AABox *node_bounds = ioBounds + inBegin;
  143. int number = inEnd - inBegin;
  144. // Partition entire range
  145. sPartition(body_idx, node_bounds, number, outSplit[2]);
  146. // Partition lower half
  147. sPartition(body_idx, node_bounds, outSplit[2], outSplit[1]);
  148. // Partition upper half
  149. sPartition(body_idx + outSplit[2], node_bounds + outSplit[2], number - outSplit[2], outSplit[3]);
  150. // Convert to proper range
  151. outSplit[0] = inBegin;
  152. outSplit[1] += inBegin;
  153. outSplit[2] += inBegin;
  154. outSplit[3] += outSplit[2];
  155. outSplit[4] = inEnd;
  156. }
  157. StaticCompoundShape::StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult) :
  158. CompoundShape(EShapeSubType::StaticCompound, inSettings, outResult)
  159. {
  160. // Check that there's at least 1 shape
  161. uint num_subshapes = (uint)inSettings.mSubShapes.size();
  162. if (num_subshapes < 2)
  163. {
  164. outResult.SetError("Compound needs at least 2 sub shapes, otherwise you should use a RotatedTranslatedShape!");
  165. return;
  166. }
  167. // Keep track of total mass to calculate center of mass
  168. float mass = 0.0f;
  169. mSubShapes.resize(num_subshapes);
  170. for (uint i = 0; i < num_subshapes; ++i)
  171. {
  172. const CompoundShapeSettings::SubShapeSettings &shape = inSettings.mSubShapes[i];
  173. // Start constructing the runtime sub shape
  174. SubShape &out_shape = mSubShapes[i];
  175. if (!out_shape.FromSettings(shape, outResult))
  176. return;
  177. // Calculate mass properties of child
  178. MassProperties child = out_shape.mShape->GetMassProperties();
  179. // Accumulate center of mass
  180. mass += child.mMass;
  181. mCenterOfMass += out_shape.GetPositionCOM() * child.mMass;
  182. }
  183. if (mass > 0.0f)
  184. mCenterOfMass /= mass;
  185. // Cache the inner radius as it can take a while to recursively iterate over all sub shapes
  186. CalculateInnerRadius();
  187. // Temporary storage for the bounding boxes of all shapes
  188. uint bounds_size = num_subshapes * sizeof(AABox);
  189. AABox *bounds = (AABox *)inTempAllocator.Allocate(bounds_size);
  190. JPH_SCOPE_EXIT([&inTempAllocator, bounds, bounds_size]{ inTempAllocator.Free(bounds, bounds_size); });
  191. // Temporary storage for body indexes (we're shuffling them)
  192. uint body_idx_size = num_subshapes * sizeof(uint);
  193. uint *body_idx = (uint *)inTempAllocator.Allocate(body_idx_size);
  194. JPH_SCOPE_EXIT([&inTempAllocator, body_idx, body_idx_size]{ inTempAllocator.Free(body_idx, body_idx_size); });
  195. // Shift all shapes so that the center of mass is now at the origin and calculate bounds
  196. for (uint i = 0; i < num_subshapes; ++i)
  197. {
  198. SubShape &shape = mSubShapes[i];
  199. // Shift the shape so it's centered around our center of mass
  200. shape.SetPositionCOM(shape.GetPositionCOM() - mCenterOfMass);
  201. // Transform the shape's bounds into our local space
  202. Mat44 transform = Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM());
  203. AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f));
  204. // Store bounds and body index for tree construction
  205. bounds[i] = shape_bounds;
  206. body_idx[i] = i;
  207. // Update our local bounds
  208. mLocalBounds.Encapsulate(shape_bounds);
  209. }
  210. // The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here
  211. struct StackEntry
  212. {
  213. uint32 mNodeIdx; // Node index of node that is generated
  214. int mChildIdx; // Index of child that we're currently processing
  215. int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions
  216. AABox mBounds; // Bounding box of this node
  217. };
  218. uint stack_size = num_subshapes * sizeof(StackEntry);
  219. StackEntry *stack = (StackEntry *)inTempAllocator.Allocate(stack_size);
  220. JPH_SCOPE_EXIT([&inTempAllocator, stack, stack_size]{ inTempAllocator.Free(stack, stack_size); });
  221. int top = 0;
  222. // Reserve enough space so that every sub shape gets its own leaf node
  223. uint next_node_idx = 0;
  224. mNodes.resize(num_subshapes + (num_subshapes + 2) / 3); // = Sum(num_subshapes * 4^-i) with i = [0, Inf].
  225. // Create root node
  226. stack[0].mNodeIdx = next_node_idx++;
  227. stack[0].mChildIdx = -1;
  228. stack[0].mBounds = AABox();
  229. sPartition4(body_idx, bounds, 0, num_subshapes, stack[0].mSplit);
  230. for (;;)
  231. {
  232. StackEntry &cur_stack = stack[top];
  233. // Next child
  234. cur_stack.mChildIdx++;
  235. // Check if all children processed
  236. if (cur_stack.mChildIdx >= 4)
  237. {
  238. // Terminate if there's nothing left to pop
  239. if (top <= 0)
  240. break;
  241. // Add our bounds to our parents bounds
  242. StackEntry &prev_stack = stack[top - 1];
  243. prev_stack.mBounds.Encapsulate(cur_stack.mBounds);
  244. // Store this node's properties in the parent node
  245. Node &parent_node = mNodes[prev_stack.mNodeIdx];
  246. parent_node.mNodeProperties[prev_stack.mChildIdx] = cur_stack.mNodeIdx;
  247. parent_node.SetChildBounds(prev_stack.mChildIdx, cur_stack.mBounds);
  248. // Pop entry from stack
  249. --top;
  250. }
  251. else
  252. {
  253. // Get low and high index to bodies to process
  254. int low = cur_stack.mSplit[cur_stack.mChildIdx];
  255. int high = cur_stack.mSplit[cur_stack.mChildIdx + 1];
  256. int num_bodies = high - low;
  257. if (num_bodies == 0)
  258. {
  259. // Mark invalid
  260. Node &node = mNodes[cur_stack.mNodeIdx];
  261. node.SetChildInvalid(cur_stack.mChildIdx);
  262. }
  263. else if (num_bodies == 1)
  264. {
  265. // Get body info
  266. uint child_node_idx = body_idx[low];
  267. const AABox &child_bounds = bounds[low];
  268. // Update node
  269. Node &node = mNodes[cur_stack.mNodeIdx];
  270. node.mNodeProperties[cur_stack.mChildIdx] = child_node_idx | IS_SUBSHAPE;
  271. node.SetChildBounds(cur_stack.mChildIdx, child_bounds);
  272. // Encapsulate bounding box in parent
  273. cur_stack.mBounds.Encapsulate(child_bounds);
  274. }
  275. else
  276. {
  277. // Allocate new node
  278. StackEntry &new_stack = stack[++top];
  279. JPH_ASSERT(top < (int)num_subshapes);
  280. new_stack.mNodeIdx = next_node_idx++;
  281. new_stack.mChildIdx = -1;
  282. new_stack.mBounds = AABox();
  283. sPartition4(body_idx, bounds, low, high, new_stack.mSplit);
  284. }
  285. }
  286. }
  287. // Resize nodes to actual size
  288. JPH_ASSERT(next_node_idx <= mNodes.size());
  289. mNodes.resize(next_node_idx);
  290. mNodes.shrink_to_fit();
  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