godot-module-template/engine/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp

675 lines
24 KiB
C++

// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
#include <Jolt/Physics/Collision/Shape/CompoundShapeVisitors.h>
#include <Jolt/Core/Profiler.h>
#include <Jolt/Core/StreamIn.h>
#include <Jolt/Core/StreamOut.h>
#include <Jolt/Core/TempAllocator.h>
#include <Jolt/Core/ScopeExit.h>
#include <Jolt/ObjectStream/TypeDeclarations.h>
JPH_NAMESPACE_BEGIN
JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(StaticCompoundShapeSettings)
{
JPH_ADD_BASE_CLASS(StaticCompoundShapeSettings, CompoundShapeSettings)
}
ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create(TempAllocator &inTempAllocator) const
{
if (mCachedResult.IsEmpty())
{
if (mSubShapes.size() == 0)
{
// It's an error to create a compound with no subshapes (the compound cannot encode this)
mCachedResult.SetError("Compound needs a sub shape!");
}
else if (mSubShapes.size() == 1)
{
// If there's only 1 part we don't need a StaticCompoundShape
const SubShapeSettings &s = mSubShapes[0];
if (s.mPosition == Vec3::sZero()
&& s.mRotation == Quat::sIdentity())
{
// No rotation or translation, we can use the shape directly
if (s.mShapePtr != nullptr)
mCachedResult.Set(const_cast<Shape *>(s.mShapePtr.GetPtr()));
else if (s.mShape != nullptr)
mCachedResult = s.mShape->Create();
else
mCachedResult.SetError("Sub shape is null!");
}
else
{
// We can use a RotatedTranslatedShape instead
RotatedTranslatedShapeSettings settings;
settings.mPosition = s.mPosition;
settings.mRotation = s.mRotation;
settings.mInnerShape = s.mShape;
settings.mInnerShapePtr = s.mShapePtr;
Ref<Shape> shape = new RotatedTranslatedShape(settings, mCachedResult);
}
}
else
{
// Build a regular compound shape
Ref<Shape> shape = new StaticCompoundShape(*this, inTempAllocator, mCachedResult);
}
}
return mCachedResult;
}
ShapeSettings::ShapeResult StaticCompoundShapeSettings::Create() const
{
TempAllocatorMalloc allocator;
return Create(allocator);
}
void StaticCompoundShape::Node::SetChildInvalid(uint inIndex)
{
// Make this an invalid node
mNodeProperties[inIndex] = INVALID_NODE;
// Make bounding box invalid
mBoundsMinX[inIndex] = HALF_FLT_MAX;
mBoundsMinY[inIndex] = HALF_FLT_MAX;
mBoundsMinZ[inIndex] = HALF_FLT_MAX;
mBoundsMaxX[inIndex] = HALF_FLT_MAX;
mBoundsMaxY[inIndex] = HALF_FLT_MAX;
mBoundsMaxZ[inIndex] = HALF_FLT_MAX;
}
void StaticCompoundShape::Node::SetChildBounds(uint inIndex, const AABox &inBounds)
{
mBoundsMinX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetX());
mBoundsMinY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetY());
mBoundsMinZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(inBounds.mMin.GetZ());
mBoundsMaxX[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetX());
mBoundsMaxY[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetY());
mBoundsMaxZ[inIndex] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(inBounds.mMax.GetZ());
}
void StaticCompoundShape::sPartition(uint *ioBodyIdx, AABox *ioBounds, int inNumber, int &outMidPoint)
{
// Handle trivial case
if (inNumber <= 4)
{
outMidPoint = inNumber / 2;
return;
}
// Calculate bounding box of box centers
Vec3 center_min = Vec3::sReplicate(FLT_MAX);
Vec3 center_max = Vec3::sReplicate(-FLT_MAX);
for (const AABox *b = ioBounds, *b_end = ioBounds + inNumber; b < b_end; ++b)
{
Vec3 center = b->GetCenter();
center_min = Vec3::sMin(center_min, center);
center_max = Vec3::sMax(center_max, center);
}
// Calculate split plane
int dimension = (center_max - center_min).GetHighestComponentIndex();
float split = 0.5f * (center_min + center_max)[dimension];
// Divide bodies
int start = 0, end = inNumber;
while (start < end)
{
// Search for first element that is on the right hand side of the split plane
while (start < end && ioBounds[start].GetCenter()[dimension] < split)
++start;
// Search for the first element that is on the left hand side of the split plane
while (start < end && ioBounds[end - 1].GetCenter()[dimension] >= split)
--end;
if (start < end)
{
// Swap the two elements
std::swap(ioBodyIdx[start], ioBodyIdx[end - 1]);
std::swap(ioBounds[start], ioBounds[end - 1]);
++start;
--end;
}
}
JPH_ASSERT(start == end);
if (start > 0 && start < inNumber)
{
// Success!
outMidPoint = start;
}
else
{
// Failed to divide bodies
outMidPoint = inNumber / 2;
}
}
void StaticCompoundShape::sPartition4(uint *ioBodyIdx, AABox *ioBounds, int inBegin, int inEnd, int *outSplit)
{
uint *body_idx = ioBodyIdx + inBegin;
AABox *node_bounds = ioBounds + inBegin;
int number = inEnd - inBegin;
// Partition entire range
sPartition(body_idx, node_bounds, number, outSplit[2]);
// Partition lower half
sPartition(body_idx, node_bounds, outSplit[2], outSplit[1]);
// Partition upper half
sPartition(body_idx + outSplit[2], node_bounds + outSplit[2], number - outSplit[2], outSplit[3]);
// Convert to proper range
outSplit[0] = inBegin;
outSplit[1] += inBegin;
outSplit[2] += inBegin;
outSplit[3] += outSplit[2];
outSplit[4] = inEnd;
}
StaticCompoundShape::StaticCompoundShape(const StaticCompoundShapeSettings &inSettings, TempAllocator &inTempAllocator, ShapeResult &outResult) :
CompoundShape(EShapeSubType::StaticCompound, inSettings, outResult)
{
// Check that there's at least 1 shape
uint num_subshapes = (uint)inSettings.mSubShapes.size();
if (num_subshapes < 2)
{
outResult.SetError("Compound needs at least 2 sub shapes, otherwise you should use a RotatedTranslatedShape!");
return;
}
// Keep track of total mass to calculate center of mass
float mass = 0.0f;
mSubShapes.resize(num_subshapes);
for (uint i = 0; i < num_subshapes; ++i)
{
const CompoundShapeSettings::SubShapeSettings &shape = inSettings.mSubShapes[i];
// Start constructing the runtime sub shape
SubShape &out_shape = mSubShapes[i];
if (!out_shape.FromSettings(shape, outResult))
return;
// Calculate mass properties of child
MassProperties child = out_shape.mShape->GetMassProperties();
// Accumulate center of mass
mass += child.mMass;
mCenterOfMass += out_shape.GetPositionCOM() * child.mMass;
}
if (mass > 0.0f)
mCenterOfMass /= mass;
// Cache the inner radius as it can take a while to recursively iterate over all sub shapes
CalculateInnerRadius();
// Temporary storage for the bounding boxes of all shapes
uint bounds_size = num_subshapes * sizeof(AABox);
AABox *bounds = (AABox *)inTempAllocator.Allocate(bounds_size);
JPH_SCOPE_EXIT([&inTempAllocator, bounds, bounds_size]{ inTempAllocator.Free(bounds, bounds_size); });
// Temporary storage for body indexes (we're shuffling them)
uint body_idx_size = num_subshapes * sizeof(uint);
uint *body_idx = (uint *)inTempAllocator.Allocate(body_idx_size);
JPH_SCOPE_EXIT([&inTempAllocator, body_idx, body_idx_size]{ inTempAllocator.Free(body_idx, body_idx_size); });
// Shift all shapes so that the center of mass is now at the origin and calculate bounds
for (uint i = 0; i < num_subshapes; ++i)
{
SubShape &shape = mSubShapes[i];
// Shift the shape so it's centered around our center of mass
shape.SetPositionCOM(shape.GetPositionCOM() - mCenterOfMass);
// Transform the shape's bounds into our local space
Mat44 transform = Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM());
AABox shape_bounds = shape.mShape->GetWorldSpaceBounds(transform, Vec3::sReplicate(1.0f));
// Store bounds and body index for tree construction
bounds[i] = shape_bounds;
body_idx[i] = i;
// Update our local bounds
mLocalBounds.Encapsulate(shape_bounds);
}
// The algorithm is a recursive tree build, but to avoid the call overhead we keep track of a stack here
struct StackEntry
{
uint32 mNodeIdx; // Node index of node that is generated
int mChildIdx; // Index of child that we're currently processing
int mSplit[5]; // Indices where the node ID's have been split to form 4 partitions
AABox mBounds; // Bounding box of this node
};
uint stack_size = num_subshapes * sizeof(StackEntry);
StackEntry *stack = (StackEntry *)inTempAllocator.Allocate(stack_size);
JPH_SCOPE_EXIT([&inTempAllocator, stack, stack_size]{ inTempAllocator.Free(stack, stack_size); });
int top = 0;
// Reserve enough space so that every sub shape gets its own leaf node
uint next_node_idx = 0;
mNodes.resize(num_subshapes + (num_subshapes + 2) / 3); // = Sum(num_subshapes * 4^-i) with i = [0, Inf].
// Create root node
stack[0].mNodeIdx = next_node_idx++;
stack[0].mChildIdx = -1;
stack[0].mBounds = AABox();
sPartition4(body_idx, bounds, 0, num_subshapes, stack[0].mSplit);
for (;;)
{
StackEntry &cur_stack = stack[top];
// Next child
cur_stack.mChildIdx++;
// Check if all children processed
if (cur_stack.mChildIdx >= 4)
{
// Terminate if there's nothing left to pop
if (top <= 0)
break;
// Add our bounds to our parents bounds
StackEntry &prev_stack = stack[top - 1];
prev_stack.mBounds.Encapsulate(cur_stack.mBounds);
// Store this node's properties in the parent node
Node &parent_node = mNodes[prev_stack.mNodeIdx];
parent_node.mNodeProperties[prev_stack.mChildIdx] = cur_stack.mNodeIdx;
parent_node.SetChildBounds(prev_stack.mChildIdx, cur_stack.mBounds);
// Pop entry from stack
--top;
}
else
{
// Get low and high index to bodies to process
int low = cur_stack.mSplit[cur_stack.mChildIdx];
int high = cur_stack.mSplit[cur_stack.mChildIdx + 1];
int num_bodies = high - low;
if (num_bodies == 0)
{
// Mark invalid
Node &node = mNodes[cur_stack.mNodeIdx];
node.SetChildInvalid(cur_stack.mChildIdx);
}
else if (num_bodies == 1)
{
// Get body info
uint child_node_idx = body_idx[low];
const AABox &child_bounds = bounds[low];
// Update node
Node &node = mNodes[cur_stack.mNodeIdx];
node.mNodeProperties[cur_stack.mChildIdx] = child_node_idx | IS_SUBSHAPE;
node.SetChildBounds(cur_stack.mChildIdx, child_bounds);
// Encapsulate bounding box in parent
cur_stack.mBounds.Encapsulate(child_bounds);
}
else
{
// Allocate new node
StackEntry &new_stack = stack[++top];
JPH_ASSERT(top < (int)num_subshapes);
new_stack.mNodeIdx = next_node_idx++;
new_stack.mChildIdx = -1;
new_stack.mBounds = AABox();
sPartition4(body_idx, bounds, low, high, new_stack.mSplit);
}
}
}
// Resize nodes to actual size
JPH_ASSERT(next_node_idx <= mNodes.size());
mNodes.resize(next_node_idx);
mNodes.shrink_to_fit();
// Check if we ran out of bits for addressing a node
if (next_node_idx > IS_SUBSHAPE)
{
outResult.SetError("Compound hierarchy has too many nodes");
return;
}
// Check if we're not exceeding the amount of sub shape id bits
if (GetSubShapeIDBitsRecursive() > SubShapeID::MaxBits)
{
outResult.SetError("Compound hierarchy is too deep and exceeds the amount of available sub shape ID bits");
return;
}
outResult.Set(this);
}
template <class Visitor>
inline void StaticCompoundShape::WalkTree(Visitor &ioVisitor) const
{
uint32 node_stack[cStackSize];
node_stack[0] = 0;
int top = 0;
do
{
// Test if the node is valid, the node should rarely be invalid but it is possible when testing
// a really large box against the tree that the invalid nodes will intersect with the box
uint32 node_properties = node_stack[top];
if (node_properties != INVALID_NODE)
{
// Test if node contains triangles
bool is_node = (node_properties & IS_SUBSHAPE) == 0;
if (is_node)
{
const Node &node = mNodes[node_properties];
// Unpack bounds
UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMinX[0]));
Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy);
Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMinZ[0]));
Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx);
Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node.mBoundsMaxY[0]));
Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz);
Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
// Load properties for 4 children
UVec4 properties = UVec4::sLoadInt4(&node.mNodeProperties[0]);
// Check which sub nodes to visit
int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, top);
// Push them onto the stack
JPH_ASSERT(top + 4 < cStackSize);
properties.StoreInt4(&node_stack[top]);
top += num_results;
}
else
{
// Points to a sub shape
uint32 sub_shape_idx = node_properties ^ IS_SUBSHAPE;
const SubShape &sub_shape = mSubShapes[sub_shape_idx];
ioVisitor.VisitShape(sub_shape, sub_shape_idx);
}
// Check if we're done
if (ioVisitor.ShouldAbort())
break;
}
// Fetch next node until we find one that the visitor wants to see
do
--top;
while (top >= 0 && !ioVisitor.ShouldVisitNode(top));
}
while (top >= 0);
}
bool StaticCompoundShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const
{
JPH_PROFILE_FUNCTION();
struct Visitor : public CastRayVisitor
{
using CastRayVisitor::CastRayVisitor;
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
{
return mDistanceStack[inStackTop] < mHit.mFraction;
}
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
{
// Test bounds of 4 children
Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
return SortReverseAndStore(distance, mHit.mFraction, ioProperties, &mDistanceStack[inStackTop]);
}
float mDistanceStack[cStackSize];
};
Visitor visitor(inRay, this, inSubShapeIDCreator, ioHit);
WalkTree(visitor);
return visitor.mReturnValue;
}
void StaticCompoundShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const
{
// Test shape filter
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
return;
JPH_PROFILE_FUNCTION();
struct Visitor : public CastRayVisitorCollector
{
using CastRayVisitorCollector::CastRayVisitorCollector;
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
{
return mDistanceStack[inStackTop] < mCollector.GetEarlyOutFraction();
}
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
{
// Test bounds of 4 children
Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
return SortReverseAndStore(distance, mCollector.GetEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
}
float mDistanceStack[cStackSize];
};
Visitor visitor(inRay, inRayCastSettings, this, inSubShapeIDCreator, ioCollector, inShapeFilter);
WalkTree(visitor);
}
void StaticCompoundShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const
{
JPH_PROFILE_FUNCTION();
struct Visitor : public CollidePointVisitor
{
using CollidePointVisitor::CollidePointVisitor;
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
{
return true;
}
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
{
// Test if point overlaps with box
UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
return CountAndSortTrues(collides, ioProperties);
}
};
Visitor visitor(inPoint, this, inSubShapeIDCreator, ioCollector, inShapeFilter);
WalkTree(visitor);
}
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)
{
JPH_PROFILE_FUNCTION();
struct Visitor : public CastShapeVisitor
{
using CastShapeVisitor::CastShapeVisitor;
JPH_INLINE bool ShouldVisitNode(int inStackTop) const
{
return mDistanceStack[inStackTop] < mCollector.GetPositiveEarlyOutFraction();
}
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, int inStackTop)
{
// Test bounds of 4 children
Vec4 distance = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
// Sort so that highest values are first (we want to first process closer hits and we process stack top to bottom)
return SortReverseAndStore(distance, mCollector.GetPositiveEarlyOutFraction(), ioProperties, &mDistanceStack[inStackTop]);
}
float mDistanceStack[cStackSize];
};
JPH_ASSERT(inShape->GetSubType() == EShapeSubType::StaticCompound);
const StaticCompoundShape *shape = static_cast<const StaticCompoundShape *>(inShape);
Visitor visitor(inShapeCast, inShapeCastSettings, shape, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector);
shape->WalkTree(visitor);
}
void StaticCompoundShape::CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) const
{
JPH_PROFILE_FUNCTION();
// Test shape filter
if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
return;
struct Visitor : public CollectTransformedShapesVisitor
{
using CollectTransformedShapesVisitor::CollectTransformedShapesVisitor;
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
{
return true;
}
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
{
// Test which nodes collide
UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
return CountAndSortTrues(collides, ioProperties);
}
};
Visitor visitor(inBox, this, inPositionCOM, inRotation, inScale, inSubShapeIDCreator, ioCollector, inShapeFilter);
WalkTree(visitor);
}
int StaticCompoundShape::GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const
{
JPH_PROFILE_FUNCTION();
GetIntersectingSubShapesVisitorSC<AABox> visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices);
WalkTree(visitor);
return visitor.GetNumResults();
}
int StaticCompoundShape::GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const
{
JPH_PROFILE_FUNCTION();
GetIntersectingSubShapesVisitorSC<OrientedBox> visitor(inBox, outSubShapeIndices, inMaxSubShapeIndices);
WalkTree(visitor);
return visitor.GetNumResults();
}
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)
{
JPH_PROFILE_FUNCTION();
JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::StaticCompound);
const StaticCompoundShape *shape1 = static_cast<const StaticCompoundShape *>(inShape1);
struct Visitor : public CollideCompoundVsShapeVisitor
{
using CollideCompoundVsShapeVisitor::CollideCompoundVsShapeVisitor;
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
{
return true;
}
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
{
// Test which nodes collide
UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
return CountAndSortTrues(collides, ioProperties);
}
};
Visitor visitor(shape1, inShape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter);
shape1->WalkTree(visitor);
}
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)
{
JPH_PROFILE_FUNCTION();
struct Visitor : public CollideShapeVsCompoundVisitor
{
using CollideShapeVsCompoundVisitor::CollideShapeVsCompoundVisitor;
JPH_INLINE bool ShouldVisitNode([[maybe_unused]] int inStackTop) const
{
return true;
}
JPH_INLINE int VisitNodes(Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ, UVec4 &ioProperties, [[maybe_unused]] int inStackTop) const
{
// Test which nodes collide
UVec4 collides = TestBounds(inBoundsMinX, inBoundsMinY, inBoundsMinZ, inBoundsMaxX, inBoundsMaxY, inBoundsMaxZ);
return CountAndSortTrues(collides, ioProperties);
}
};
JPH_ASSERT(inShape2->GetSubType() == EShapeSubType::StaticCompound);
const StaticCompoundShape *shape2 = static_cast<const StaticCompoundShape *>(inShape2);
Visitor visitor(inShape1, shape2, inScale1, inScale2, inCenterOfMassTransform1, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, inCollideShapeSettings, ioCollector, inShapeFilter);
shape2->WalkTree(visitor);
}
void StaticCompoundShape::SaveBinaryState(StreamOut &inStream) const
{
CompoundShape::SaveBinaryState(inStream);
inStream.Write(mNodes);
}
void StaticCompoundShape::RestoreBinaryState(StreamIn &inStream)
{
CompoundShape::RestoreBinaryState(inStream);
inStream.Read(mNodes);
}
void StaticCompoundShape::sRegister()
{
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::StaticCompound);
f.mConstruct = []() -> Shape * { return new StaticCompoundShape; };
f.mColor = Color::sOrange;
for (EShapeSubType s : sAllSubShapeTypes)
{
CollisionDispatch::sRegisterCollideShape(EShapeSubType::StaticCompound, s, sCollideCompoundVsShape);
CollisionDispatch::sRegisterCollideShape(s, EShapeSubType::StaticCompound, sCollideShapeVsCompound);
CollisionDispatch::sRegisterCastShape(s, EShapeSubType::StaticCompound, sCastShapeVsCompound);
}
}
JPH_NAMESPACE_END