434 lines
14 KiB
C++
434 lines
14 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/CompoundShape.h>
|
|
#include <Jolt/Physics/Collision/CollisionDispatch.h>
|
|
#include <Jolt/Physics/Collision/ShapeCast.h>
|
|
#include <Jolt/Physics/Collision/CastResult.h>
|
|
#include <Jolt/Physics/Collision/TransformedShape.h>
|
|
#include <Jolt/Core/Profiler.h>
|
|
#include <Jolt/Core/StreamIn.h>
|
|
#include <Jolt/Core/StreamOut.h>
|
|
#include <Jolt/ObjectStream/TypeDeclarations.h>
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
#include <Jolt/Renderer/DebugRenderer.h>
|
|
#endif // JPH_DEBUG_RENDERER
|
|
|
|
JPH_NAMESPACE_BEGIN
|
|
|
|
JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(CompoundShapeSettings)
|
|
{
|
|
JPH_ADD_BASE_CLASS(CompoundShapeSettings, ShapeSettings)
|
|
|
|
JPH_ADD_ATTRIBUTE(CompoundShapeSettings, mSubShapes)
|
|
}
|
|
|
|
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(CompoundShapeSettings::SubShapeSettings)
|
|
{
|
|
JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mShape)
|
|
JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mPosition)
|
|
JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mRotation)
|
|
JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mUserData)
|
|
}
|
|
|
|
void CompoundShapeSettings::AddShape(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape, uint32 inUserData)
|
|
{
|
|
// Add shape
|
|
SubShapeSettings shape;
|
|
shape.mPosition = inPosition;
|
|
shape.mRotation = inRotation;
|
|
shape.mShape = inShape;
|
|
shape.mUserData = inUserData;
|
|
mSubShapes.push_back(shape);
|
|
}
|
|
|
|
void CompoundShapeSettings::AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData)
|
|
{
|
|
// Add shape
|
|
SubShapeSettings shape;
|
|
shape.mPosition = inPosition;
|
|
shape.mRotation = inRotation;
|
|
shape.mShapePtr = inShape;
|
|
shape.mUserData = inUserData;
|
|
mSubShapes.push_back(shape);
|
|
}
|
|
|
|
bool CompoundShape::MustBeStatic() const
|
|
{
|
|
for (const SubShape &shape : mSubShapes)
|
|
if (shape.mShape->MustBeStatic())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
MassProperties CompoundShape::GetMassProperties() const
|
|
{
|
|
MassProperties p;
|
|
|
|
// Calculate mass and inertia
|
|
p.mMass = 0.0f;
|
|
p.mInertia = Mat44::sZero();
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
// Rotate and translate inertia of child into place
|
|
MassProperties child = shape.mShape->GetMassProperties();
|
|
child.Rotate(Mat44::sRotation(shape.GetRotation()));
|
|
child.Translate(shape.GetPositionCOM());
|
|
|
|
// Accumulate mass and inertia
|
|
p.mMass += child.mMass;
|
|
p.mInertia += child.mInertia;
|
|
}
|
|
|
|
// Ensure that inertia is a 3x3 matrix, adding inertias causes the bottom right element to change
|
|
p.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1));
|
|
|
|
return p;
|
|
}
|
|
|
|
AABox CompoundShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const
|
|
{
|
|
if (mSubShapes.empty())
|
|
{
|
|
// If there are no sub-shapes, we must return an empty box to avoid overflows in the broadphase
|
|
return AABox(inCenterOfMassTransform.GetTranslation(), inCenterOfMassTransform.GetTranslation());
|
|
}
|
|
else if (mSubShapes.size() <= 10)
|
|
{
|
|
AABox bounds;
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
Mat44 transform = inCenterOfMassTransform * shape.GetLocalTransformNoScale(inScale);
|
|
bounds.Encapsulate(shape.mShape->GetWorldSpaceBounds(transform, shape.TransformScale(inScale)));
|
|
}
|
|
return bounds;
|
|
}
|
|
else
|
|
{
|
|
// If there are too many shapes, use the base class function (this will result in a slightly wider bounding box)
|
|
return Shape::GetWorldSpaceBounds(inCenterOfMassTransform, inScale);
|
|
}
|
|
}
|
|
|
|
uint CompoundShape::GetSubShapeIDBitsRecursive() const
|
|
{
|
|
// Add max of child bits to our bits
|
|
uint child_bits = 0;
|
|
for (const SubShape &shape : mSubShapes)
|
|
child_bits = max(child_bits, shape.mShape->GetSubShapeIDBitsRecursive());
|
|
return child_bits + GetSubShapeIDBits();
|
|
}
|
|
|
|
const PhysicsMaterial *CompoundShape::GetMaterial(const SubShapeID &inSubShapeID) const
|
|
{
|
|
// Decode sub shape index
|
|
SubShapeID remainder;
|
|
uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);
|
|
|
|
// Pass call on
|
|
return mSubShapes[index].mShape->GetMaterial(remainder);
|
|
}
|
|
|
|
const Shape *CompoundShape::GetLeafShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const
|
|
{
|
|
// Decode sub shape index
|
|
SubShapeID remainder;
|
|
uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);
|
|
if (index >= mSubShapes.size())
|
|
{
|
|
// No longer valid index
|
|
outRemainder = SubShapeID();
|
|
return nullptr;
|
|
}
|
|
|
|
// Pass call on
|
|
return mSubShapes[index].mShape->GetLeafShape(remainder, outRemainder);
|
|
}
|
|
|
|
uint64 CompoundShape::GetSubShapeUserData(const SubShapeID &inSubShapeID) const
|
|
{
|
|
// Decode sub shape index
|
|
SubShapeID remainder;
|
|
uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);
|
|
if (index >= mSubShapes.size())
|
|
return 0; // No longer valid index
|
|
|
|
// Pass call on
|
|
return mSubShapes[index].mShape->GetSubShapeUserData(remainder);
|
|
}
|
|
|
|
TransformedShape CompoundShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const
|
|
{
|
|
// Get the sub shape
|
|
const SubShape &sub_shape = mSubShapes[GetSubShapeIndexFromID(inSubShapeID, outRemainder)];
|
|
|
|
// Calculate transform for sub shape
|
|
Vec3 position = inPositionCOM + inRotation * (inScale * sub_shape.GetPositionCOM());
|
|
Quat rotation = inRotation * sub_shape.GetRotation();
|
|
Vec3 scale = sub_shape.TransformScale(inScale);
|
|
|
|
// Return transformed shape
|
|
TransformedShape ts(RVec3(position), rotation, sub_shape.mShape, BodyID());
|
|
ts.SetShapeScale(scale);
|
|
return ts;
|
|
}
|
|
|
|
Vec3 CompoundShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const
|
|
{
|
|
// Decode sub shape index
|
|
SubShapeID remainder;
|
|
uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);
|
|
|
|
// Transform surface position to local space and pass call on
|
|
const SubShape &shape = mSubShapes[index];
|
|
Mat44 transform = Mat44::sInverseRotationTranslation(shape.GetRotation(), shape.GetPositionCOM());
|
|
Vec3 normal = shape.mShape->GetSurfaceNormal(remainder, transform * inLocalSurfacePosition);
|
|
|
|
// Transform normal to this shape's space
|
|
return transform.Multiply3x3Transposed(normal);
|
|
}
|
|
|
|
void CompoundShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const
|
|
{
|
|
// Decode sub shape index
|
|
SubShapeID remainder;
|
|
uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);
|
|
|
|
// Apply transform and pass on to sub shape
|
|
const SubShape &shape = mSubShapes[index];
|
|
Mat44 transform = shape.GetLocalTransformNoScale(inScale);
|
|
shape.mShape->GetSupportingFace(remainder, transform.Multiply3x3Transposed(inDirection), shape.TransformScale(inScale), inCenterOfMassTransform * transform, outVertices);
|
|
}
|
|
|
|
void CompoundShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const
|
|
{
|
|
outTotalVolume = 0.0f;
|
|
outSubmergedVolume = 0.0f;
|
|
outCenterOfBuoyancy = Vec3::sZero();
|
|
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
// Get center of mass transform of child
|
|
Mat44 transform = inCenterOfMassTransform * shape.GetLocalTransformNoScale(inScale);
|
|
|
|
// Recurse to child
|
|
float total_volume, submerged_volume;
|
|
Vec3 center_of_buoyancy;
|
|
shape.mShape->GetSubmergedVolume(transform, shape.TransformScale(inScale), inSurface, total_volume, submerged_volume, center_of_buoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset));
|
|
|
|
// Accumulate volumes
|
|
outTotalVolume += total_volume;
|
|
outSubmergedVolume += submerged_volume;
|
|
|
|
// The center of buoyancy is the weighted average of the center of buoyancy of our child shapes
|
|
outCenterOfBuoyancy += submerged_volume * center_of_buoyancy;
|
|
}
|
|
|
|
if (outSubmergedVolume > 0.0f)
|
|
outCenterOfBuoyancy /= outSubmergedVolume;
|
|
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
// Draw center of buoyancy
|
|
if (sDrawSubmergedVolumes)
|
|
DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1);
|
|
#endif // JPH_DEBUG_RENDERER
|
|
}
|
|
|
|
#ifdef JPH_DEBUG_RENDERER
|
|
void CompoundShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const
|
|
{
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
Mat44 transform = shape.GetLocalTransformNoScale(inScale);
|
|
shape.mShape->Draw(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale), inColor, inUseMaterialColors, inDrawWireframe);
|
|
}
|
|
}
|
|
|
|
void CompoundShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const
|
|
{
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
Mat44 transform = shape.GetLocalTransformNoScale(inScale);
|
|
shape.mShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale), inColor, inDrawSupportDirection);
|
|
}
|
|
}
|
|
|
|
void CompoundShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const
|
|
{
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
Mat44 transform = shape.GetLocalTransformNoScale(inScale);
|
|
shape.mShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale));
|
|
}
|
|
}
|
|
#endif // JPH_DEBUG_RENDERER
|
|
|
|
void CompoundShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const
|
|
{
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
Mat44 transform = shape.GetLocalTransformNoScale(inScale);
|
|
shape.mShape->CollideSoftBodyVertices(inCenterOfMassTransform * transform, shape.TransformScale(inScale), inVertices, inNumVertices, inCollidingShapeIndex);
|
|
}
|
|
}
|
|
|
|
void CompoundShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const
|
|
{
|
|
for (const SubShape &shape : mSubShapes)
|
|
shape.mShape->TransformShape(inCenterOfMassTransform * Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM()), ioCollector);
|
|
}
|
|
|
|
void CompoundShape::sCastCompoundVsShape(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();
|
|
|
|
// Fetch compound shape from cast shape
|
|
JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Compound);
|
|
const CompoundShape *compound = static_cast<const CompoundShape *>(inShapeCast.mShape);
|
|
|
|
// Number of sub shapes
|
|
int n = (int)compound->mSubShapes.size();
|
|
|
|
// Determine amount of bits for sub shape
|
|
uint sub_shape_bits = compound->GetSubShapeIDBits();
|
|
|
|
// Recurse to sub shapes
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
const SubShape &shape = compound->mSubShapes[i];
|
|
|
|
// Create ID for sub shape
|
|
SubShapeIDCreator shape1_sub_shape_id = inSubShapeIDCreator1.PushID(i, sub_shape_bits);
|
|
|
|
// Transform the shape cast and update the shape
|
|
Mat44 transform = inShapeCast.mCenterOfMassStart * shape.GetLocalTransformNoScale(inShapeCast.mScale);
|
|
Vec3 scale = shape.TransformScale(inShapeCast.mScale);
|
|
ShapeCast shape_cast(shape.mShape, scale, transform, inShapeCast.mDirection);
|
|
|
|
CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, shape1_sub_shape_id, inSubShapeIDCreator2, ioCollector);
|
|
|
|
if (ioCollector.ShouldEarlyOut())
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CompoundShape::SaveBinaryState(StreamOut &inStream) const
|
|
{
|
|
Shape::SaveBinaryState(inStream);
|
|
|
|
inStream.Write(mCenterOfMass);
|
|
inStream.Write(mLocalBounds.mMin);
|
|
inStream.Write(mLocalBounds.mMax);
|
|
inStream.Write(mInnerRadius);
|
|
|
|
// Write sub shapes
|
|
inStream.Write(mSubShapes, [](const SubShape &inElement, StreamOut &inS) {
|
|
inS.Write(inElement.mUserData);
|
|
inS.Write(inElement.mPositionCOM);
|
|
inS.Write(inElement.mRotation);
|
|
});
|
|
}
|
|
|
|
void CompoundShape::RestoreBinaryState(StreamIn &inStream)
|
|
{
|
|
Shape::RestoreBinaryState(inStream);
|
|
|
|
inStream.Read(mCenterOfMass);
|
|
inStream.Read(mLocalBounds.mMin);
|
|
inStream.Read(mLocalBounds.mMax);
|
|
inStream.Read(mInnerRadius);
|
|
|
|
// Read sub shapes
|
|
inStream.Read(mSubShapes, [](StreamIn &inS, SubShape &outElement) {
|
|
inS.Read(outElement.mUserData);
|
|
inS.Read(outElement.mPositionCOM);
|
|
inS.Read(outElement.mRotation);
|
|
outElement.mIsRotationIdentity = outElement.mRotation == Float3(0, 0, 0);
|
|
});
|
|
}
|
|
|
|
void CompoundShape::SaveSubShapeState(ShapeList &outSubShapes) const
|
|
{
|
|
outSubShapes.clear();
|
|
outSubShapes.reserve(mSubShapes.size());
|
|
for (const SubShape &shape : mSubShapes)
|
|
outSubShapes.push_back(shape.mShape);
|
|
}
|
|
|
|
void CompoundShape::RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes)
|
|
{
|
|
JPH_ASSERT(mSubShapes.size() == inNumShapes);
|
|
for (uint i = 0; i < inNumShapes; ++i)
|
|
mSubShapes[i].mShape = inSubShapes[i];
|
|
}
|
|
|
|
Shape::Stats CompoundShape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const
|
|
{
|
|
// Get own stats
|
|
Stats stats = Shape::GetStatsRecursive(ioVisitedShapes);
|
|
|
|
// Add child stats
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
Stats child_stats = shape.mShape->GetStatsRecursive(ioVisitedShapes);
|
|
stats.mSizeBytes += child_stats.mSizeBytes;
|
|
stats.mNumTriangles += child_stats.mNumTriangles;
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
|
|
float CompoundShape::GetVolume() const
|
|
{
|
|
float volume = 0.0f;
|
|
for (const SubShape &shape : mSubShapes)
|
|
volume += shape.mShape->GetVolume();
|
|
return volume;
|
|
}
|
|
|
|
bool CompoundShape::IsValidScale(Vec3Arg inScale) const
|
|
{
|
|
if (!Shape::IsValidScale(inScale))
|
|
return false;
|
|
|
|
for (const SubShape &shape : mSubShapes)
|
|
{
|
|
// Test if the scale is non-uniform and the shape is rotated
|
|
if (!shape.IsValidScale(inScale))
|
|
return false;
|
|
|
|
// Test the child shape
|
|
if (!shape.mShape->IsValidScale(shape.TransformScale(inScale)))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Vec3 CompoundShape::MakeScaleValid(Vec3Arg inScale) const
|
|
{
|
|
Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);
|
|
if (CompoundShape::IsValidScale(scale))
|
|
return scale;
|
|
|
|
Vec3 abs_uniform_scale = ScaleHelpers::MakeUniformScale(scale.Abs());
|
|
Vec3 uniform_scale = scale.GetSign() * abs_uniform_scale;
|
|
if (CompoundShape::IsValidScale(uniform_scale))
|
|
return uniform_scale;
|
|
|
|
return Sign(scale.GetX()) * abs_uniform_scale;
|
|
}
|
|
|
|
void CompoundShape::sRegister()
|
|
{
|
|
for (EShapeSubType s1 : sCompoundSubShapeTypes)
|
|
for (EShapeSubType s2 : sAllSubShapeTypes)
|
|
CollisionDispatch::sRegisterCastShape(s1, s2, sCastCompoundVsShape);
|
|
}
|
|
|
|
JPH_NAMESPACE_END
|