Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/CompoundShape.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/CompoundShape.h>7#include <Jolt/Physics/Collision/CollisionDispatch.h>8#include <Jolt/Physics/Collision/ShapeCast.h>9#include <Jolt/Physics/Collision/CastResult.h>10#include <Jolt/Physics/Collision/TransformedShape.h>11#include <Jolt/Core/Profiler.h>12#include <Jolt/Core/StreamIn.h>13#include <Jolt/Core/StreamOut.h>14#include <Jolt/ObjectStream/TypeDeclarations.h>15#ifdef JPH_DEBUG_RENDERER16#include <Jolt/Renderer/DebugRenderer.h>17#endif // JPH_DEBUG_RENDERER1819JPH_NAMESPACE_BEGIN2021JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(CompoundShapeSettings)22{23JPH_ADD_BASE_CLASS(CompoundShapeSettings, ShapeSettings)2425JPH_ADD_ATTRIBUTE(CompoundShapeSettings, mSubShapes)26}2728JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(CompoundShapeSettings::SubShapeSettings)29{30JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mShape)31JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mPosition)32JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mRotation)33JPH_ADD_ATTRIBUTE(CompoundShapeSettings::SubShapeSettings, mUserData)34}3536void CompoundShapeSettings::AddShape(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape, uint32 inUserData)37{38// Add shape39SubShapeSettings shape;40shape.mPosition = inPosition;41shape.mRotation = inRotation;42shape.mShape = inShape;43shape.mUserData = inUserData;44mSubShapes.push_back(shape);45}4647void CompoundShapeSettings::AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData)48{49// Add shape50SubShapeSettings shape;51shape.mPosition = inPosition;52shape.mRotation = inRotation;53shape.mShapePtr = inShape;54shape.mUserData = inUserData;55mSubShapes.push_back(shape);56}5758bool CompoundShape::MustBeStatic() const59{60for (const SubShape &shape : mSubShapes)61if (shape.mShape->MustBeStatic())62return true;6364return false;65}6667MassProperties CompoundShape::GetMassProperties() const68{69MassProperties p;7071// Calculate mass and inertia72p.mMass = 0.0f;73p.mInertia = Mat44::sZero();74for (const SubShape &shape : mSubShapes)75{76// Rotate and translate inertia of child into place77MassProperties child = shape.mShape->GetMassProperties();78child.Rotate(Mat44::sRotation(shape.GetRotation()));79child.Translate(shape.GetPositionCOM());8081// Accumulate mass and inertia82p.mMass += child.mMass;83p.mInertia += child.mInertia;84}8586// Ensure that inertia is a 3x3 matrix, adding inertias causes the bottom right element to change87p.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1));8889return p;90}9192AABox CompoundShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const93{94if (mSubShapes.empty())95{96// If there are no sub-shapes, we must return an empty box to avoid overflows in the broadphase97return AABox(inCenterOfMassTransform.GetTranslation(), inCenterOfMassTransform.GetTranslation());98}99else if (mSubShapes.size() <= 10)100{101AABox bounds;102for (const SubShape &shape : mSubShapes)103{104Mat44 transform = inCenterOfMassTransform * shape.GetLocalTransformNoScale(inScale);105bounds.Encapsulate(shape.mShape->GetWorldSpaceBounds(transform, shape.TransformScale(inScale)));106}107return bounds;108}109else110{111// If there are too many shapes, use the base class function (this will result in a slightly wider bounding box)112return Shape::GetWorldSpaceBounds(inCenterOfMassTransform, inScale);113}114}115116uint CompoundShape::GetSubShapeIDBitsRecursive() const117{118// Add max of child bits to our bits119uint child_bits = 0;120for (const SubShape &shape : mSubShapes)121child_bits = max(child_bits, shape.mShape->GetSubShapeIDBitsRecursive());122return child_bits + GetSubShapeIDBits();123}124125const PhysicsMaterial *CompoundShape::GetMaterial(const SubShapeID &inSubShapeID) const126{127// Decode sub shape index128SubShapeID remainder;129uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);130131// Pass call on132return mSubShapes[index].mShape->GetMaterial(remainder);133}134135const Shape *CompoundShape::GetLeafShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const136{137// Decode sub shape index138SubShapeID remainder;139uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);140if (index >= mSubShapes.size())141{142// No longer valid index143outRemainder = SubShapeID();144return nullptr;145}146147// Pass call on148return mSubShapes[index].mShape->GetLeafShape(remainder, outRemainder);149}150151uint64 CompoundShape::GetSubShapeUserData(const SubShapeID &inSubShapeID) const152{153// Decode sub shape index154SubShapeID remainder;155uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);156if (index >= mSubShapes.size())157return 0; // No longer valid index158159// Pass call on160return mSubShapes[index].mShape->GetSubShapeUserData(remainder);161}162163TransformedShape CompoundShape::GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const164{165// Get the sub shape166const SubShape &sub_shape = mSubShapes[GetSubShapeIndexFromID(inSubShapeID, outRemainder)];167168// Calculate transform for sub shape169Vec3 position = inPositionCOM + inRotation * (inScale * sub_shape.GetPositionCOM());170Quat rotation = inRotation * sub_shape.GetRotation();171Vec3 scale = sub_shape.TransformScale(inScale);172173// Return transformed shape174TransformedShape ts(RVec3(position), rotation, sub_shape.mShape, BodyID());175ts.SetShapeScale(scale);176return ts;177}178179Vec3 CompoundShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const180{181// Decode sub shape index182SubShapeID remainder;183uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);184185// Transform surface position to local space and pass call on186const SubShape &shape = mSubShapes[index];187Mat44 transform = Mat44::sInverseRotationTranslation(shape.GetRotation(), shape.GetPositionCOM());188Vec3 normal = shape.mShape->GetSurfaceNormal(remainder, transform * inLocalSurfacePosition);189190// Transform normal to this shape's space191return transform.Multiply3x3Transposed(normal);192}193194void CompoundShape::GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const195{196// Decode sub shape index197SubShapeID remainder;198uint32 index = GetSubShapeIndexFromID(inSubShapeID, remainder);199200// Apply transform and pass on to sub shape201const SubShape &shape = mSubShapes[index];202Mat44 transform = shape.GetLocalTransformNoScale(inScale);203shape.mShape->GetSupportingFace(remainder, transform.Multiply3x3Transposed(inDirection), shape.TransformScale(inScale), inCenterOfMassTransform * transform, outVertices);204}205206void CompoundShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const207{208outTotalVolume = 0.0f;209outSubmergedVolume = 0.0f;210outCenterOfBuoyancy = Vec3::sZero();211212for (const SubShape &shape : mSubShapes)213{214// Get center of mass transform of child215Mat44 transform = inCenterOfMassTransform * shape.GetLocalTransformNoScale(inScale);216217// Recurse to child218float total_volume, submerged_volume;219Vec3 center_of_buoyancy;220shape.mShape->GetSubmergedVolume(transform, shape.TransformScale(inScale), inSurface, total_volume, submerged_volume, center_of_buoyancy JPH_IF_DEBUG_RENDERER(, inBaseOffset));221222// Accumulate volumes223outTotalVolume += total_volume;224outSubmergedVolume += submerged_volume;225226// The center of buoyancy is the weighted average of the center of buoyancy of our child shapes227outCenterOfBuoyancy += submerged_volume * center_of_buoyancy;228}229230if (outSubmergedVolume > 0.0f)231outCenterOfBuoyancy /= outSubmergedVolume;232233#ifdef JPH_DEBUG_RENDERER234// Draw center of buoyancy235if (sDrawSubmergedVolumes)236DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1);237#endif // JPH_DEBUG_RENDERER238}239240#ifdef JPH_DEBUG_RENDERER241void CompoundShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const242{243for (const SubShape &shape : mSubShapes)244{245Mat44 transform = shape.GetLocalTransformNoScale(inScale);246shape.mShape->Draw(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale), inColor, inUseMaterialColors, inDrawWireframe);247}248}249250void CompoundShape::DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const251{252for (const SubShape &shape : mSubShapes)253{254Mat44 transform = shape.GetLocalTransformNoScale(inScale);255shape.mShape->DrawGetSupportFunction(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale), inColor, inDrawSupportDirection);256}257}258259void CompoundShape::DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const260{261for (const SubShape &shape : mSubShapes)262{263Mat44 transform = shape.GetLocalTransformNoScale(inScale);264shape.mShape->DrawGetSupportingFace(inRenderer, inCenterOfMassTransform * transform, shape.TransformScale(inScale));265}266}267#endif // JPH_DEBUG_RENDERER268269void CompoundShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const270{271for (const SubShape &shape : mSubShapes)272{273Mat44 transform = shape.GetLocalTransformNoScale(inScale);274shape.mShape->CollideSoftBodyVertices(inCenterOfMassTransform * transform, shape.TransformScale(inScale), inVertices, inNumVertices, inCollidingShapeIndex);275}276}277278void CompoundShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const279{280for (const SubShape &shape : mSubShapes)281shape.mShape->TransformShape(inCenterOfMassTransform * Mat44::sRotationTranslation(shape.GetRotation(), shape.GetPositionCOM()), ioCollector);282}283284void 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)285{286JPH_PROFILE_FUNCTION();287288// Fetch compound shape from cast shape289JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Compound);290const CompoundShape *compound = static_cast<const CompoundShape *>(inShapeCast.mShape);291292// Number of sub shapes293int n = (int)compound->mSubShapes.size();294295// Determine amount of bits for sub shape296uint sub_shape_bits = compound->GetSubShapeIDBits();297298// Recurse to sub shapes299for (int i = 0; i < n; ++i)300{301const SubShape &shape = compound->mSubShapes[i];302303// Create ID for sub shape304SubShapeIDCreator shape1_sub_shape_id = inSubShapeIDCreator1.PushID(i, sub_shape_bits);305306// Transform the shape cast and update the shape307Mat44 transform = inShapeCast.mCenterOfMassStart * shape.GetLocalTransformNoScale(inShapeCast.mScale);308Vec3 scale = shape.TransformScale(inShapeCast.mScale);309ShapeCast shape_cast(shape.mShape, scale, transform, inShapeCast.mDirection);310311CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, inShapeCastSettings, inShape, inScale, inShapeFilter, inCenterOfMassTransform2, shape1_sub_shape_id, inSubShapeIDCreator2, ioCollector);312313if (ioCollector.ShouldEarlyOut())314break;315}316}317318void CompoundShape::SaveBinaryState(StreamOut &inStream) const319{320Shape::SaveBinaryState(inStream);321322inStream.Write(mCenterOfMass);323inStream.Write(mLocalBounds.mMin);324inStream.Write(mLocalBounds.mMax);325inStream.Write(mInnerRadius);326327// Write sub shapes328inStream.Write(mSubShapes, [](const SubShape &inElement, StreamOut &inS) {329inS.Write(inElement.mUserData);330inS.Write(inElement.mPositionCOM);331inS.Write(inElement.mRotation);332});333}334335void CompoundShape::RestoreBinaryState(StreamIn &inStream)336{337Shape::RestoreBinaryState(inStream);338339inStream.Read(mCenterOfMass);340inStream.Read(mLocalBounds.mMin);341inStream.Read(mLocalBounds.mMax);342inStream.Read(mInnerRadius);343344// Read sub shapes345inStream.Read(mSubShapes, [](StreamIn &inS, SubShape &outElement) {346inS.Read(outElement.mUserData);347inS.Read(outElement.mPositionCOM);348inS.Read(outElement.mRotation);349outElement.mIsRotationIdentity = outElement.mRotation == Float3(0, 0, 0);350});351}352353void CompoundShape::SaveSubShapeState(ShapeList &outSubShapes) const354{355outSubShapes.clear();356outSubShapes.reserve(mSubShapes.size());357for (const SubShape &shape : mSubShapes)358outSubShapes.push_back(shape.mShape);359}360361void CompoundShape::RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes)362{363JPH_ASSERT(mSubShapes.size() == inNumShapes);364for (uint i = 0; i < inNumShapes; ++i)365mSubShapes[i].mShape = inSubShapes[i];366}367368Shape::Stats CompoundShape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const369{370// Get own stats371Stats stats = Shape::GetStatsRecursive(ioVisitedShapes);372373// Add child stats374for (const SubShape &shape : mSubShapes)375{376Stats child_stats = shape.mShape->GetStatsRecursive(ioVisitedShapes);377stats.mSizeBytes += child_stats.mSizeBytes;378stats.mNumTriangles += child_stats.mNumTriangles;379}380381return stats;382}383384float CompoundShape::GetVolume() const385{386float volume = 0.0f;387for (const SubShape &shape : mSubShapes)388volume += shape.mShape->GetVolume();389return volume;390}391392bool CompoundShape::IsValidScale(Vec3Arg inScale) const393{394if (!Shape::IsValidScale(inScale))395return false;396397for (const SubShape &shape : mSubShapes)398{399// Test if the scale is non-uniform and the shape is rotated400if (!shape.IsValidScale(inScale))401return false;402403// Test the child shape404if (!shape.mShape->IsValidScale(shape.TransformScale(inScale)))405return false;406}407408return true;409}410411Vec3 CompoundShape::MakeScaleValid(Vec3Arg inScale) const412{413Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);414if (CompoundShape::IsValidScale(scale))415return scale;416417Vec3 abs_uniform_scale = ScaleHelpers::MakeUniformScale(scale.Abs());418Vec3 uniform_scale = scale.GetSign() * abs_uniform_scale;419if (CompoundShape::IsValidScale(uniform_scale))420return uniform_scale;421422return Sign(scale.GetX()) * abs_uniform_scale;423}424425void CompoundShape::sRegister()426{427for (EShapeSubType s1 : sCompoundSubShapeTypes)428for (EShapeSubType s2 : sAllSubShapeTypes)429CollisionDispatch::sRegisterCastShape(s1, s2, sCastCompoundVsShape);430}431432JPH_NAMESPACE_END433434435