StaticCompoundShape.cpp 23 KB

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