Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/CompoundShape.h
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/Physics/Collision/Shape/Shape.h>7#include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>8#include <Jolt/Physics/Collision/Shape/SubShapeID.h>910JPH_NAMESPACE_BEGIN1112class CollideShapeSettings;13class OrientedBox;1415/// Base class settings to construct a compound shape16class JPH_EXPORT CompoundShapeSettings : public ShapeSettings17{18JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, CompoundShapeSettings)1920public:21/// Constructor. Use AddShape to add the parts.22CompoundShapeSettings() = default;2324/// Add a shape to the compound.25void AddShape(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape, uint32 inUserData = 0);2627/// Add a shape to the compound. Variant that uses a concrete shape, which means this object cannot be serialized.28void AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData = 0);2930struct SubShapeSettings31{32JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SubShapeSettings)3334RefConst<ShapeSettings> mShape; ///< Sub shape (either this or mShapePtr needs to be filled up)35RefConst<Shape> mShapePtr; ///< Sub shape (either this or mShape needs to be filled up)36Vec3 mPosition; ///< Position of the sub shape37Quat mRotation; ///< Rotation of the sub shape3839/// User data value (can be used by the application for any purpose).40/// Note this value can be retrieved through GetSubShape(...).mUserData, not through GetSubShapeUserData(...) as that returns Shape::GetUserData() of the leaf shape.41/// Use GetSubShapeIndexFromID get a shape index from a SubShapeID to pass to GetSubShape.42uint32 mUserData = 0;43};4445using SubShapes = Array<SubShapeSettings>;4647SubShapes mSubShapes;48};4950/// Base class for a compound shape51class JPH_EXPORT CompoundShape : public Shape52{53public:54JPH_OVERRIDE_NEW_DELETE5556/// Constructor57explicit CompoundShape(EShapeSubType inSubType) : Shape(EShapeType::Compound, inSubType) { }58CompoundShape(EShapeSubType inSubType, const ShapeSettings &inSettings, ShapeResult &outResult) : Shape(EShapeType::Compound, inSubType, inSettings, outResult) { }5960// See Shape::GetCenterOfMass61virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; }6263// See Shape::MustBeStatic64virtual bool MustBeStatic() const override;6566// See Shape::GetLocalBounds67virtual AABox GetLocalBounds() const override { return mLocalBounds; }6869// See Shape::GetSubShapeIDBitsRecursive70virtual uint GetSubShapeIDBitsRecursive() const override;7172// See Shape::GetWorldSpaceBounds73virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override;74using Shape::GetWorldSpaceBounds;7576// See Shape::GetInnerRadius77virtual float GetInnerRadius() const override { return mInnerRadius; }7879// See Shape::GetMassProperties80virtual MassProperties GetMassProperties() const override;8182// See Shape::GetMaterial83virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override;8485// See Shape::GetLeafShape86virtual const Shape * GetLeafShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const override;8788// See Shape::GetSubShapeUserData89virtual uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const override;9091// See Shape::GetSubShapeTransformedShape92virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override;9394// See Shape::GetSurfaceNormal95virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override;9697// See Shape::GetSupportingFace98virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override;99100// See Shape::GetSubmergedVolume101virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override;102103#ifdef JPH_DEBUG_RENDERER104// See Shape::Draw105virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override;106107// See Shape::DrawGetSupportFunction108virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override;109110// See Shape::DrawGetSupportingFace111virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override;112#endif // JPH_DEBUG_RENDERER113114// See: Shape::CollideSoftBodyVertices115virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const override;116117// See Shape::TransformShape118virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override;119120// See Shape::GetTrianglesStart121virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); }122123// See Shape::GetTrianglesNext124virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override { JPH_ASSERT(false, "Cannot call on non-leaf shapes, use CollectTransformedShapes to collect the leaves first!"); return 0; }125126/// Get which sub shape's bounding boxes overlap with an axis aligned box127/// @param inBox The axis aligned box to test against (relative to the center of mass of this shape)128/// @param outSubShapeIndices Buffer where to place the indices of the sub shapes that intersect129/// @param inMaxSubShapeIndices How many indices will fit in the buffer (normally you'd provide a buffer of GetNumSubShapes() indices)130/// @return How many indices were placed in outSubShapeIndices131virtual int GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const = 0;132133/// Get which sub shape's bounding boxes overlap with an axis aligned box134/// @param inBox The axis aligned box to test against (relative to the center of mass of this shape)135/// @param outSubShapeIndices Buffer where to place the indices of the sub shapes that intersect136/// @param inMaxSubShapeIndices How many indices will fit in the buffer (normally you'd provide a buffer of GetNumSubShapes() indices)137/// @return How many indices were placed in outSubShapeIndices138virtual int GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const = 0;139140struct SubShape141{142/// Initialize sub shape from sub shape settings143/// @param inSettings Settings object144/// @param outResult Result object, only used in case of error145/// @return True on success, false on failure146bool FromSettings(const CompoundShapeSettings::SubShapeSettings &inSettings, ShapeResult &outResult)147{148if (inSettings.mShapePtr != nullptr)149{150// Use provided shape151mShape = inSettings.mShapePtr;152}153else154{155// Create child shape156ShapeResult child_result = inSettings.mShape->Create();157if (!child_result.IsValid())158{159outResult = child_result;160return false;161}162mShape = child_result.Get();163}164165// Copy user data166mUserData = inSettings.mUserData;167168SetTransform(inSettings.mPosition, inSettings.mRotation, Vec3::sZero() /* Center of mass not yet calculated */);169return true;170}171172/// Update the transform of this sub shape173/// @param inPosition New position174/// @param inRotation New orientation175/// @param inCenterOfMass The center of mass of the compound shape176JPH_INLINE void SetTransform(Vec3Arg inPosition, QuatArg inRotation, Vec3Arg inCenterOfMass)177{178SetPositionCOM(inPosition - inCenterOfMass + inRotation * mShape->GetCenterOfMass());179180mIsRotationIdentity = inRotation.IsClose(Quat::sIdentity()) || inRotation.IsClose(-Quat::sIdentity());181SetRotation(mIsRotationIdentity? Quat::sIdentity() : inRotation);182}183184/// Get the local transform for this shape given the scale of the child shape185/// The total transform of the child shape will be GetLocalTransformNoScale(inScale) * Mat44::sScaling(TransformScale(inScale))186/// @param inScale The scale of the child shape (in local space of this shape)187JPH_INLINE Mat44 GetLocalTransformNoScale(Vec3Arg inScale) const188{189JPH_ASSERT(IsValidScale(inScale));190return Mat44::sRotationTranslation(GetRotation(), inScale * GetPositionCOM());191}192193/// Test if inScale is valid for this sub shape194inline bool IsValidScale(Vec3Arg inScale) const195{196// We can always handle uniform scale or identity rotations197if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale))198return true;199200return ScaleHelpers::CanScaleBeRotated(GetRotation(), inScale);201}202203/// Transform the scale to the local space of the child shape204inline Vec3 TransformScale(Vec3Arg inScale) const205{206// We don't need to transform uniform scale or if the rotation is identity207if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale))208return inScale;209210return ScaleHelpers::RotateScale(GetRotation(), inScale);211}212213/// Compress the center of mass position214JPH_INLINE void SetPositionCOM(Vec3Arg inPositionCOM)215{216inPositionCOM.StoreFloat3(&mPositionCOM);217}218219/// Uncompress the center of mass position220JPH_INLINE Vec3 GetPositionCOM() const221{222return Vec3::sLoadFloat3Unsafe(mPositionCOM);223}224225/// Compress the rotation226JPH_INLINE void SetRotation(QuatArg inRotation)227{228inRotation.StoreFloat3(&mRotation);229}230231/// Uncompress the rotation232JPH_INLINE Quat GetRotation() const233{234return mIsRotationIdentity? Quat::sIdentity() : Quat::sLoadFloat3Unsafe(mRotation);235}236237RefConst<Shape> mShape;238Float3 mPositionCOM; ///< Note: Position of center of mass of sub shape!239Float3 mRotation; ///< Note: X, Y, Z of rotation quaternion - note we read 4 bytes beyond this so make sure there's something there240uint32 mUserData; ///< User data value (put here because it falls in padding bytes)241bool mIsRotationIdentity; ///< If mRotation is close to identity (put here because it falls in padding bytes)242// 3 padding bytes left243};244245static_assert(sizeof(SubShape) == (JPH_CPU_ADDRESS_BITS == 64? 40 : 36), "Compiler added unexpected padding");246247using SubShapes = Array<SubShape>;248249/// Access to the sub shapes of this compound250const SubShapes & GetSubShapes() const { return mSubShapes; }251252/// Get the total number of sub shapes253uint GetNumSubShapes() const { return uint(mSubShapes.size()); }254255/// Access to a particular sub shape256const SubShape & GetSubShape(uint inIdx) const { return mSubShapes[inIdx]; }257258/// Get the user data associated with a shape in this compound259uint32 GetCompoundUserData(uint inIdx) const { return mSubShapes[inIdx].mUserData; }260261/// Set the user data associated with a shape in this compound262void SetCompoundUserData(uint inIdx, uint32 inUserData) { mSubShapes[inIdx].mUserData = inUserData; }263264/// Check if a sub shape ID is still valid for this shape265/// @param inSubShapeID Sub shape id that indicates the leaf shape relative to this shape266/// @return True if the ID is valid, false if not267inline bool IsSubShapeIDValid(SubShapeID inSubShapeID) const268{269SubShapeID remainder;270return inSubShapeID.PopID(GetSubShapeIDBits(), remainder) < mSubShapes.size();271}272273/// Convert SubShapeID to sub shape index274/// @param inSubShapeID Sub shape id that indicates the leaf shape relative to this shape275/// @param outRemainder This is the sub shape ID for the sub shape of the compound after popping off the index276/// @return The index of the sub shape of this compound277inline uint32 GetSubShapeIndexFromID(SubShapeID inSubShapeID, SubShapeID &outRemainder) const278{279uint32 idx = inSubShapeID.PopID(GetSubShapeIDBits(), outRemainder);280JPH_ASSERT(idx < mSubShapes.size(), "Invalid SubShapeID");281return idx;282}283284/// @brief Convert a sub shape index to a sub shape ID285/// @param inIdx Index of the sub shape of this compound286/// @param inParentSubShapeID Parent SubShapeID (describing the path to the compound shape)287/// @return A sub shape ID creator that contains the full path to the sub shape with index inIdx288inline SubShapeIDCreator GetSubShapeIDFromIndex(int inIdx, const SubShapeIDCreator &inParentSubShapeID) const289{290return inParentSubShapeID.PushID(inIdx, GetSubShapeIDBits());291}292293// See Shape294virtual void SaveBinaryState(StreamOut &inStream) const override;295virtual void SaveSubShapeState(ShapeList &outSubShapes) const override;296virtual void RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) override;297298// See Shape::GetStatsRecursive299virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const override;300301// See Shape::GetVolume302virtual float GetVolume() const override;303304// See Shape::IsValidScale305virtual bool IsValidScale(Vec3Arg inScale) const override;306307// See Shape::MakeScaleValid308virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;309310// Register shape functions with the registry311static void sRegister();312313protected:314// See: Shape::RestoreBinaryState315virtual void RestoreBinaryState(StreamIn &inStream) override;316317// Visitors for collision detection318struct CastRayVisitor;319struct CastRayVisitorCollector;320struct CollidePointVisitor;321struct CastShapeVisitor;322struct CollectTransformedShapesVisitor;323struct CollideCompoundVsShapeVisitor;324struct CollideShapeVsCompoundVisitor;325template <class BoxType> struct GetIntersectingSubShapesVisitor;326327/// Determine amount of bits needed to encode sub shape id328inline uint GetSubShapeIDBits() const329{330// Ensure we have enough bits to encode our shape [0, n - 1]331uint32 n = uint32(mSubShapes.size()) - 1;332return 32 - CountLeadingZeros(n);333}334335/// Determine the inner radius of this shape336inline void CalculateInnerRadius()337{338mInnerRadius = FLT_MAX;339for (const SubShape &s : mSubShapes)340mInnerRadius = min(mInnerRadius, s.mShape->GetInnerRadius());341}342343Vec3 mCenterOfMass { Vec3::sZero() }; ///< Center of mass of the compound344AABox mLocalBounds { Vec3::sZero(), Vec3::sZero() };345SubShapes mSubShapes;346float mInnerRadius = FLT_MAX; ///< Smallest radius of GetInnerRadius() of child shapes347348private:349// Helper functions called by CollisionDispatch350static void 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);351};352353JPH_NAMESPACE_END354355356