Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>7#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>8#include <Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h>9#include <Jolt/Core/Profiler.h>10#include <Jolt/Core/StreamIn.h>11#include <Jolt/Core/StreamOut.h>12#include <Jolt/Core/TempAllocator.h>13#include <Jolt/Core/ScopeExit.h>14#include <Jolt/ObjectStream/TypeDeclarations.h>1516JPH_NAMESPACE_BEGIN1718JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(StaticCompoundShapeSettings)19{20JPH_ADD_BASE_CLASS(StaticCompoundShapeSettings, CompoundShapeSettings)21}2223ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create(TempAllocator &inTempAllocator) const24{25if (mCachedResult.IsEmpty())26{27if (mSubShapes.size() == 0)28{29// It's an error to create a compound with no subshapes (the compound cannot encode this)30mCachedResult.SetError("Compound needs a sub shape!");31}32else if (mSubShapes.size() == 1)33{34// If there's only 1 part we don't need a StaticCompoundShape35const SubShapeSettings &s = mSubShapes[0];36if (s.mPosition == Vec3::sZero()37&& s.mRotation == Quat::sIdentity())38{39// No rotation or translation, we can use the shape directly40if (s.mShapePtr != nullptr)41mCachedResult.Set(const_cast<Shape *>(s.mShapePtr.GetPtr()));42else if (s.mShape != nullptr)43mCachedResult = s.mShape->Create();44else45mCachedResult.SetError("Sub shape is null!");46}47else48{49// We can use a RotatedTranslatedShape instead50RotatedTranslatedShapeSettings settings;51settings.mPosition = s.mPosition;52settings.mRotation = s.mRotation;53settings.mInnerShape = s.mShape;54settings.mInnerShapePtr = s.mShapePtr;55Ref<Shape> shape = new RotatedTranslatedShape(settings, mCachedResult);56}57}58else59{60// Build a regular compound shape61Ref<Shape> shape = new StaticCompoundShape(*this, inTempAllocator, mCachedResult);62}63}64return mCachedResult;65}6667ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create() const68{69TempAllocatorMalloc allocator;70return Create(allocator);71}7273void StaticCompoundShape::Node::SetChildInvalid(uint inIndex)74{75// Make this an invalid node76mNodeProperties[inIndex] = INVALID_NODE;7778// Make bounding box invalid79mBoundsMinX[inIndex] = HALF_FLT_MAX;80mBoundsMinY[inIndex] = HALF_FLT_MAX;81mBoundsMinZ[inIndex] = HALF_FLT_MAX;82mBoundsMaxX[inIndex] = HALF_FLT_MAX;83mBoundsMaxY[inIndex] = HALF_FLT_MAX;84mBoundsMaxZ[inIndex] = HALF_FLT_MAX;85}8687void StaticCompoundShape::Node::SetChildBounds(uint inIndex, const AABox &inBounds)88{89mBoundsMinX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetX());90mBoundsMinY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetY());91mBoundsMinZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetZ());92mBoundsMaxX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetX());93mBoundsMaxY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetY());94mBoundsMaxZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetZ());95}9697void StaticCompoundShape::sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint)98{99// Handle trivial case100if (inNumber <= 4)101{102outMidPoint = inNumber / 2;103return;104}105106// Calculate bounding box of box centers107Vec3 center_min = Vec3::sReplicate(FLT_MAX);108Vec3 center_max = Vec3::sReplicate(-FLT_MAX);109for (const AABox *b = ioBounds, *b_end = ioBounds + inNumber; b < b_end; ++b)110{111Vec3 center = b->GetCenter();112center_min = Vec3::sMin(center_min, center);113center_max = Vec3::sMax(center_max, center);114}115116// Calculate split plane117int dimension = (center_max - center_min).GetHighestComponentIndex();118float split = 0.5f * (center_min + center_max)[dimension];119120// Divide bodies121int start = 0, end = inNumber;122while (start < end)123{124// Search for first element that is on the right hand side of the split plane125while (start < end && ioBounds[start].GetCenter()[dimension] < split)126++start;127128// Search for the first element that is on the left hand side of the split plane129while (start < end && ioBounds[end - 1].GetCenter()[dimension] >= split)130--end;131132if (start < end)133{134// Swap the two elements135std::swap(ioBodyIdx[start], ioBodyIdx[end - 1]);136std::swap(ioBounds[start], ioBounds[end - 1]);137++start;138--end;139}140}141JPH_ASSERT(start == end);142143if (start > 0 && start < inNumber)144{145// Success!146outMidPoint = start;147}148else149{150// Failed to divide bodies151outMidPoint = inNumber / 2;152}153}154155void StaticCompoundShape::sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit)156{157uint *body_idx = ioBodyIdx + inBegin;158AABox *node_bounds = ioBounds + inBegin;159int number = inEnd - inBegin;160161// Partition entire range162sPartition(body_idx, node_bounds, number, outSplit[2]);163164// Partition lower half165sPartition(body_idx, node_bounds, outSplit[2], outSplit[1]);166167// Partition upper half168sPartition(body_idx + outSplit[2], node_bounds + outSplit[2], number - outSplit[2], outSplit[3]);169170// Convert to proper range171outSplit[0] = inBegin;172outSplit[1] += inBegin;173outSplit[2] += inBegin;174outSplit[3] += outSplit[2];175outSplit[4] = inEnd;176}177178StaticCompoundShape::StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult) :179CompoundShape(EShapeSubType::StaticCompound, inSettings, outResult)180{181// Check that there's at least 1 shape182uint num_subshapes = (uint)inSettings.mSubShapes.size();183if (num_subshapes < 2)184{185outResult.SetError("Compound needs at least 2 sub shapes, otherwise you should use a RotatedTranslatedShape!");186return;187}188189// Keep track of total mass to calculate center of mass190float mass = 0.0f;191192mSubShapes.resize(num_subshapes);193for (uint i = 0; i < num_subshapes; ++i)194{195const CompoundShapeSettings::SubShapeSettings &shape = inSettings.mSubShapes[i];196197// Start constructing the runtime sub shape198SubShape &out_shape = mSubShapes[i];199if (!out_shape.FromSettings(shape, outResult))200return;201202// Calculate mass properties of child203MassProperties child = out_shape.mShape->GetMassProperties();204205// Accumulate center of mass206mass += child.mMass;207mCenterOfMass += out_shape.GetPositionCOM() * child.mMass;208}209210if (mass > 0.0f)211mCenterOfMass /= mass;212213// Cache the inner radius as it can take a while to recursively iterate over all sub shapes214CalculateInnerRadius();215216// Temporary storage for the bounding boxes of all shapes217uint bounds_size = num_subshapes * sizeof(AABox);218AABox *bounds = (AABox *)inTempAllocator.Allocate(bounds_size);219JPH_SCOPE_EXIT([&inTempAllocator, bounds, bounds_size]{ inTempAllocator.Free(bounds, bounds_size); });220221// Temporary storage for body indexes (we're shuffling them)222uint body_idx_size = num_subshapes * sizeof(uint);223uint *body_idx = (uint *)inTempAllocator.Allocate(body_idx_size);224JPH_SCOPE_EXIT([&inTempAllocator, body_idx, body_idx_size]{ inTempAllocator.Free(body_idx, body_idx_size); });225226// Shift all shapes so that the center of mass is now at the origin and calculate bounds227for (uint i = 0; i < num_subshapes; ++i)228{229SubShape &shape = mSubShapes[i];230231// Shift the shape so it's centered around our center of mass232shape.SetPositionCOM(shape.GetPositionCOM() - mCenterOfMass);233234// Transform the shape's bounds into our local space235Mat44 transform = Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM());236AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sOne());237238// Store bounds and body index for tree construction239bounds[i] = shape_bounds;240body_idx[i] = i;241242// Update our local bounds243mLocalBounds.Encapsulate(shape_bounds);244}245246// The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here247struct StackEntry248{249uint32 mNodeIdx; // Node index of node that is generated250int mChildIdx; // Index of child that we're currently processing251int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions252AABox mBounds; // Bounding box of this node253};254uint stack_size = num_subshapes * sizeof(StackEntry);255StackEntry *stack = (StackEntry *)inTempAllocator.Allocate(stack_size);256JPH_SCOPE_EXIT([&inTempAllocator, stack, stack_size]{ inTempAllocator.Free(stack, stack_size); });257int top = 0;258259// Reserve enough space so that every sub shape gets its own leaf node260uint next_node_idx = 0;261mNodes.resize(num_subshapes + (num_subshapes + 2) / 3); // = Sum(num_subshapes * 4^-i) with i = [0, Inf].262263// Create root node264stack[0].mNodeIdx = next_node_idx++;265stack[0].mChildIdx = -1;266stack[0].mBounds = AABox();267sPartition4(body_idx, bounds, 0, num_subshapes, stack[0].mSplit);268269for (;;)270{271StackEntry &cur_stack = stack[top];272273// Next child274cur_stack.mChildIdx++;275276// Check if all children processed277if (cur_stack.mChildIdx >= 4)278{279// Terminate if there's nothing left to pop280if (top <= 0)281break;282283// Add our bounds to our parents bounds284StackEntry &prev_stack = stack[top - 1];285prev_stack.mBounds.Encapsulate(cur_stack.mBounds);286287// Store this node's properties in the parent node288Node &parent_node = mNodes[prev_stack.mNodeIdx];289parent_node.mNodeProperties[prev_stack.mChildIdx] = cur_stack.mNodeIdx;290parent_node.SetChildBounds(prev_stack.mChildIdx, cur_stack.mBounds);291292// Pop entry from stack293--top;294}295else296{297// Get low and high index to bodies to process298int low = cur_stack.mSplit[cur_stack.mChildIdx];299int high = cur_stack.mSplit[cur_stack.mChildIdx + 1];300int num_bodies = high - low;301302if (num_bodies == 0)303{304// Mark invalid305Node &node = mNodes[cur_stack.mNodeIdx];306node.SetChildInvalid(cur_stack.mChildIdx);307}308else if (num_bodies == 1)309{310// Get body info311uint child_node_idx = body_idx[low];312const AABox &child_bounds = bounds[low];313314// Update node315Node &node = mNodes[cur_stack.mNodeIdx];316node.mNodeProperties[cur_stack.mChildIdx] = child_node_idx | IS_SUBSHAPE;317node.SetChildBounds(cur_stack.mChildIdx, child_bounds);318319// Encapsulate bounding box in parent320cur_stack.mBounds.Encapsulate(child_bounds);321}322else323{324// Allocate new node325StackEntry &new_stack = stack[++top];326JPH_ASSERT(top < (int)num_subshapes);327new_stack.mNodeIdx = next_node_idx++;328new_stack.mChildIdx = -1;329new_stack.mBounds = AABox();330sPartition4(body_idx, bounds, low, high, new_stack.mSplit);331}332}333}334335// Resize nodes to actual size336JPH_ASSERT(next_node_idx <= mNodes.size());337mNodes.resize(next_node_idx);338mNodes.shrink_to_fit();339340// Check if we ran out of bits for addressing a node341if (next_node_idx > IS_SUBSHAPE)342{343outResult.SetError("Compound hierarchy has too many nodes");344return;345}346347// Check if we're not exceeding the amount of sub shape id bits348if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits)349{350outResult.SetError("Compound hierarchy is too deep and exceeds the amount of available sub shape ID bits");351return;352}353354outResult.Set(this);355}356357template <class Visitor>358inline void StaticCompoundShape::WalkTree(Visitor &ioVisitor) const359{360uint32 node_stack[cStackSize];361node_stack[0] = 0;362int top = 0;363do364{365// Test if the node is valid, the node should rarely be invalid but it is possible when testing366// a really large box against the tree that the invalid nodes will intersect with the box367uint32 node_properties = node_stack[top];368if (node_properties != INVALID_NODE)369{370// Test if node contains triangles371bool is_node = (node_properties & IS_SUBSHAPE) == 0;372if (is_node)373{374const Node &node = mNodes[node_properties];375376// Unpack bounds377UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMinX[0]));378Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy);379Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());380381UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMinZ[0]));382Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx);383Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());384385UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMaxY[0]));386Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz);387Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());388389// Load properties for 4 children390UVec4 properties = UVec4::sLoadInt4(&node.mNodeProperties[0]);391392// Check which sub nodes to visit393int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, top);394395// Push them onto the stack396JPH_ASSERT(top + 4 < cStackSize);397properties.StoreInt4(&node_stack[top]);398top += num_results;399}400else401{402// Points to a sub shape403uint32 sub_shape_idx = node_properties ^ IS_SUBSHAPE;404const SubShape &sub_shape = mSubShapes[sub_shape_idx];405406ioVisitor.VisitShape(sub_shape, sub_shape_idx);407}408409// Check if we're done410if (ioVisitor.ShouldAbort())411break;412}413414// Fetch next node until we find one that the visitor wants to see415do416--top;417while (top >= 0 && !ioVisitor.ShouldVisitNode(top));418}419while (top >= 0);420}421422bool StaticCompoundShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const423{424JPH_PROFILE_FUNCTION();425426struct Visitor : public CastRayVisitor427{428using CastRayVisitor::CastRayVisitor;429430JPH_INLINE bool ShouldVisitNode(int inStackTop) const431{432return mDistanceStack[inStackTop] < mHit.mFraction;433}434435JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)436{437// Test bounds of 4 children438Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);439440// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)441return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]);442}443444float mDistanceStack[cStackSize];445};446447Visitor visitor(inRay, this, inSubShapeIDCreator, ioHit);448WalkTree(visitor);449return visitor.mReturnValue;450}451452void StaticCompoundShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const453{454// Test shape filter455if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))456return;457458JPH_PROFILE_FUNCTION();459460struct Visitor : public CastRayVisitorCollector461{462using CastRayVisitorCollector::CastRayVisitorCollector;463464JPH_INLINE bool ShouldVisitNode(int inStackTop) const465{466return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();467}468469JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)470{471// Test bounds of 4 children472Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);473474// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)475return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);476}477478float mDistanceStack[cStackSize];479};480481Visitor visitor(inRay, inRayCastSettings, this, inSubShapeIDCreator, ioCollector, inShapeFilter);482WalkTree(visitor);483}484485void StaticCompoundShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const486{487JPH_PROFILE_FUNCTION();488489struct Visitor : public CollidePointVisitor490{491using CollidePointVisitor::CollidePointVisitor;492493JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const494{495return true;496}497498JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const499{500// Test if point overlaps with box501UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);502return CountAndSortTrues(collides, ioProperties);503}504};505506Visitor visitor(inPoint, this, inSubShapeIDCreator, ioCollector, inShapeFilter);507WalkTree(visitor);508}509510void 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)511{512JPH_PROFILE_FUNCTION();513514struct Visitor : public CastShapeVisitor515{516using CastShapeVisitor::CastShapeVisitor;517518JPH_INLINE bool ShouldVisitNode(int inStackTop) const519{520return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();521}522523JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)524{525// Test bounds of 4 children526Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);527528// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)529return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);530}531532float mDistanceStack[cStackSize];533};534535JPH_ASSERT(inShape->GetSubType() == EShapeSubType::StaticCompound);536const StaticCompoundShape *shape = static_cast<const StaticCompoundShape *>(inShape);537538Visitor visitor(inShapeCast, inShapeCastSettings, shape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector);539shape->WalkTree(visitor);540}541542void StaticCompoundShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const543{544JPH_PROFILE_FUNCTION();545546// Test shape filter547if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))548return;549550struct Visitor : public CollectTransformedShapesVisitor551{552using CollectTransformedShapesVisitor::CollectTransformedShapesVisitor;553554JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const555{556return true;557}558559JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const560{561// Test which nodes collide562UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);563return CountAndSortTrues(collides, ioProperties);564}565};566567Visitor visitor(inBox, this, inPositionCOM, inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter);568WalkTree(visitor);569}570571int StaticCompoundShape::GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const572{573JPH_PROFILE_FUNCTION();574575GetIntersectingSubShapesVisitorSC<AABox> visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices);576WalkTree(visitor);577return visitor.GetNumResults();578}579580int StaticCompoundShape::GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const581{582JPH_PROFILE_FUNCTION();583584GetIntersectingSubShapesVisitorSC<OrientedBox> visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices);585WalkTree(visitor);586return visitor.GetNumResults();587}588589void 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)590{591JPH_PROFILE_FUNCTION();592593JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::StaticCompound);594const StaticCompoundShape *shape1 = static_cast<const StaticCompoundShape *>(inShape1);595596struct Visitor : public CollideCompoundVsShapeVisitor597{598using CollideCompoundVsShapeVisitor::CollideCompoundVsShapeVisitor;599600JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const601{602return true;603}604605JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const606{607// Test which nodes collide608UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);609return CountAndSortTrues(collides, ioProperties);610}611};612613Visitor visitor(shape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter);614shape1->WalkTree(visitor);615}616617void 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)618{619JPH_PROFILE_FUNCTION();620621struct Visitor : public CollideShapeVsCompoundVisitor622{623using CollideShapeVsCompoundVisitor::CollideShapeVsCompoundVisitor;624625JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const626{627return true;628}629630JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const631{632// Test which nodes collide633UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);634return CountAndSortTrues(collides, ioProperties);635}636};637638JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::StaticCompound);639const StaticCompoundShape *shape2 = static_cast<const StaticCompoundShape *>(inShape2);640641Visitor visitor(inShape1, shape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter);642shape2->WalkTree(visitor);643}644645void StaticCompoundShape::SaveBinaryState(StreamOut &inStream) const646{647CompoundShape::SaveBinaryState(inStream);648649inStream.Write(mNodes);650}651652void StaticCompoundShape::RestoreBinaryState(StreamIn &inStream)653{654CompoundShape::RestoreBinaryState(inStream);655656inStream.Read(mNodes);657}658659void StaticCompoundShape::sRegister()660{661ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::StaticCompound);662f.mConstruct = []() -> Shape * { return new StaticCompoundShape; };663f.mColor = Color::sOrange;664665for (EShapeSubType s : sAllSubShapeTypes)666{667CollisionDispatch::sRegisterCollideShape(EShapeSubType::StaticCompound, s, sCollideCompoundVsShape);668CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::StaticCompound, sCollideShapeVsCompound);669CollisionDispatch::sRegisterCastShape(s, EShapeSubType::StaticCompound, sCastShapeVsCompound);670}671}672673JPH_NAMESPACE_END674675676