CompoundShape.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #include <Jolt/Physics/Collision/Shape/Shape.h>
  6. #include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>
  7. #include <Jolt/Physics/Collision/Shape/SubShapeID.h>
  8. JPH_NAMESPACE_BEGIN
  9. class CollideShapeSettings;
  10. class OrientedBox;
  11. /// Base class settings to construct a compound shape
  12. class JPH_EXPORT CompoundShapeSettings : public ShapeSettings
  13. {
  14. JPH_DECLARE_SERIALIZABLE_ABSTRACT(JPH_EXPORT, CompoundShapeSettings)
  15. public:
  16. /// Constructor. Use AddShape to add the parts.
  17. CompoundShapeSettings() = default;
  18. /// Add a shape to the compound.
  19. void AddShape(Vec3Arg inPosition, QuatArg inRotation, const ShapeSettings *inShape, uint32 inUserData = 0);
  20. /// Add a shape to the compound. Variant that uses a concrete shape, which means this object cannot be serialized.
  21. void AddShape(Vec3Arg inPosition, QuatArg inRotation, const Shape *inShape, uint32 inUserData = 0);
  22. struct SubShapeSettings
  23. {
  24. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SubShapeSettings)
  25. RefConst<ShapeSettings> mShape; ///< Sub shape (either this or mShapePtr needs to be filled up)
  26. RefConst<Shape> mShapePtr; ///< Sub shape (either this or mShape needs to be filled up)
  27. Vec3 mPosition; ///< Position of the sub shape
  28. Quat mRotation; ///< Rotation of the sub shape
  29. /// User data value (can be used by the application for any purpose).
  30. /// Note this value can be retrieved through GetSubShape(...).mUserData, not through GetSubShapeUserData(...) as that returns Shape::GetUserData() of the leaf shape.
  31. /// Use GetSubShapeIndexFromID get a shape index from a SubShapeID to pass to GetSubShape.
  32. uint32 mUserData = 0;
  33. };
  34. using SubShapes = Array<SubShapeSettings>;
  35. SubShapes mSubShapes;
  36. };
  37. /// Base class for a compound shape
  38. class JPH_EXPORT CompoundShape : public Shape
  39. {
  40. public:
  41. JPH_OVERRIDE_NEW_DELETE
  42. /// Constructor
  43. explicit CompoundShape(EShapeSubType inSubType) : Shape(EShapeType::Compound, inSubType) { }
  44. CompoundShape(EShapeSubType inSubType, const ShapeSettings &inSettings, ShapeResult &outResult) : Shape(EShapeType::Compound, inSubType, inSettings, outResult) { }
  45. // See Shape::GetCenterOfMass
  46. virtual Vec3 GetCenterOfMass() const override { return mCenterOfMass; }
  47. // See Shape::MustBeStatic
  48. virtual bool MustBeStatic() const override;
  49. // See Shape::GetLocalBounds
  50. virtual AABox GetLocalBounds() const override { return mLocalBounds; }
  51. // See Shape::GetSubShapeIDBitsRecursive
  52. virtual uint GetSubShapeIDBitsRecursive() const override;
  53. // See Shape::GetWorldSpaceBounds
  54. virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override;
  55. using Shape::GetWorldSpaceBounds;
  56. // See Shape::GetInnerRadius
  57. virtual float GetInnerRadius() const override { return mInnerRadius; }
  58. // See Shape::GetMassProperties
  59. virtual MassProperties GetMassProperties() const override;
  60. // See Shape::GetMaterial
  61. virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override;
  62. // See Shape::GetLeafShape
  63. virtual const Shape * GetLeafShape(const SubShapeID &inSubShapeID, SubShapeID &outRemainder) const override;
  64. // See Shape::GetSubShapeUserData
  65. virtual uint64 GetSubShapeUserData(const SubShapeID &inSubShapeID) const override;
  66. // See Shape::GetSubShapeTransformedShape
  67. virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const override;
  68. // See Shape::GetSurfaceNormal
  69. virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override;
  70. // See Shape::GetSupportingFace
  71. virtual void GetSupportingFace(const SubShapeID &inSubShapeID, Vec3Arg inDirection, Vec3Arg inScale, Mat44Arg inCenterOfMassTransform, SupportingFace &outVertices) const override;
  72. // See Shape::GetSubmergedVolume
  73. virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const override;
  74. #ifdef JPH_DEBUG_RENDERER
  75. // See Shape::Draw
  76. virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override;
  77. // See Shape::DrawGetSupportFunction
  78. virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const override;
  79. // See Shape::DrawGetSupportingFace
  80. virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale) const override;
  81. #endif // JPH_DEBUG_RENDERER
  82. // See: Shape::CollideSoftBodyVertices
  83. virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const CollideSoftBodyVertexIterator &inVertices, uint inNumVertices, int inCollidingShapeIndex) const override;
  84. // See Shape::TransformShape
  85. virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override;
  86. // See Shape::GetTrianglesStart
  87. virtual 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!"); }
  88. // See Shape::GetTrianglesNext
  89. virtual 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; }
  90. /// Get which sub shape's bounding boxes overlap with an axis aligned box
  91. /// @param inBox The axis aligned box to test against (relative to the center of mass of this shape)
  92. /// @param outSubShapeIndices Buffer where to place the indices of the sub shapes that intersect
  93. /// @param inMaxSubShapeIndices How many indices will fit in the buffer (normally you'd provide a buffer of GetNumSubShapes() indices)
  94. /// @return How many indices were placed in outSubShapeIndices
  95. virtual int GetIntersectingSubShapes(const AABox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const = 0;
  96. /// Get which sub shape's bounding boxes overlap with an axis aligned box
  97. /// @param inBox The axis aligned box to test against (relative to the center of mass of this shape)
  98. /// @param outSubShapeIndices Buffer where to place the indices of the sub shapes that intersect
  99. /// @param inMaxSubShapeIndices How many indices will fit in the buffer (normally you'd provide a buffer of GetNumSubShapes() indices)
  100. /// @return How many indices were placed in outSubShapeIndices
  101. virtual int GetIntersectingSubShapes(const OrientedBox &inBox, uint *outSubShapeIndices, int inMaxSubShapeIndices) const = 0;
  102. struct SubShape
  103. {
  104. /// Initialize sub shape from sub shape settings
  105. /// @param inSettings Settings object
  106. /// @param outResult Result object, only used in case of error
  107. /// @return True on success, false on failure
  108. bool FromSettings(const CompoundShapeSettings::SubShapeSettings &inSettings, ShapeResult &outResult)
  109. {
  110. if (inSettings.mShapePtr != nullptr)
  111. {
  112. // Use provided shape
  113. mShape = inSettings.mShapePtr;
  114. }
  115. else
  116. {
  117. // Create child shape
  118. ShapeResult child_result = inSettings.mShape->Create();
  119. if (!child_result.IsValid())
  120. {
  121. outResult = child_result;
  122. return false;
  123. }
  124. mShape = child_result.Get();
  125. }
  126. // Copy user data
  127. mUserData = inSettings.mUserData;
  128. SetTransform(inSettings.mPosition, inSettings.mRotation, Vec3::sZero() /* Center of mass not yet calculated */);
  129. return true;
  130. }
  131. /// Update the transform of this sub shape
  132. /// @param inPosition New position
  133. /// @param inRotation New orientation
  134. /// @param inCenterOfMass The center of mass of the compound shape
  135. JPH_INLINE void SetTransform(Vec3Arg inPosition, QuatArg inRotation, Vec3Arg inCenterOfMass)
  136. {
  137. SetPositionCOM(inPosition - inCenterOfMass + inRotation * mShape->GetCenterOfMass());
  138. mIsRotationIdentity = inRotation.IsClose(Quat::sIdentity()) || inRotation.IsClose(-Quat::sIdentity());
  139. SetRotation(mIsRotationIdentity? Quat::sIdentity() : inRotation);
  140. }
  141. /// Get the local transform for this shape given the scale of the child shape
  142. /// The total transform of the child shape will be GetLocalTransformNoScale(inScale) * Mat44::sScaling(TransformScale(inScale))
  143. /// @param inScale The scale of the child shape (in local space of this shape)
  144. JPH_INLINE Mat44 GetLocalTransformNoScale(Vec3Arg inScale) const
  145. {
  146. JPH_ASSERT(IsValidScale(inScale));
  147. return Mat44::sRotationTranslation(GetRotation(), inScale * GetPositionCOM());
  148. }
  149. /// Test if inScale is valid for this sub shape
  150. inline bool IsValidScale(Vec3Arg inScale) const
  151. {
  152. // We can always handle uniform scale or identity rotations
  153. if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale))
  154. return true;
  155. return ScaleHelpers::CanScaleBeRotated(GetRotation(), inScale);
  156. }
  157. /// Transform the scale to the local space of the child shape
  158. inline Vec3 TransformScale(Vec3Arg inScale) const
  159. {
  160. // We don't need to transform uniform scale or if the rotation is identity
  161. if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale))
  162. return inScale;
  163. return ScaleHelpers::RotateScale(GetRotation(), inScale);
  164. }
  165. /// Compress the center of mass position
  166. JPH_INLINE void SetPositionCOM(Vec3Arg inPositionCOM)
  167. {
  168. inPositionCOM.StoreFloat3(&mPositionCOM);
  169. }
  170. /// Uncompress the center of mass position
  171. JPH_INLINE Vec3 GetPositionCOM() const
  172. {
  173. return Vec3::sLoadFloat3Unsafe(mPositionCOM);
  174. }
  175. /// Compress the rotation
  176. JPH_INLINE void SetRotation(QuatArg inRotation)
  177. {
  178. inRotation.StoreFloat3(&mRotation);
  179. }
  180. /// Uncompress the rotation
  181. JPH_INLINE Quat GetRotation() const
  182. {
  183. return mIsRotationIdentity? Quat::sIdentity() : Quat::sLoadFloat3Unsafe(mRotation);
  184. }
  185. RefConst<Shape> mShape;
  186. Float3 mPositionCOM; ///< Note: Position of center of mass of sub shape!
  187. Float3 mRotation; ///< Note: X, Y, Z of rotation quaternion - note we read 4 bytes beyond this so make sure there's something there
  188. uint32 mUserData; ///< User data value (put here because it falls in padding bytes)
  189. bool mIsRotationIdentity; ///< If mRotation is close to identity (put here because it falls in padding bytes)
  190. // 3 padding bytes left
  191. };
  192. static_assert(sizeof(SubShape) == (sizeof(void *) == 8? 40 : 36), "Compiler added unexpected padding");
  193. using SubShapes = Array<SubShape>;
  194. /// Access to the sub shapes of this compound
  195. const SubShapes & GetSubShapes() const { return mSubShapes; }
  196. /// Get the total number of sub shapes
  197. uint GetNumSubShapes() const { return uint(mSubShapes.size()); }
  198. /// Access to a particular sub shape
  199. const SubShape & GetSubShape(uint inIdx) const { return mSubShapes[inIdx]; }
  200. /// Get the user data associated with a shape in this compound
  201. uint32 GetCompoundUserData(uint inIdx) const { return mSubShapes[inIdx].mUserData; }
  202. /// Set the user data associated with a shape in this compound
  203. void SetCompoundUserData(uint inIdx, uint32 inUserData) { mSubShapes[inIdx].mUserData = inUserData; }
  204. /// Check if a sub shape ID is still valid for this shape
  205. /// @param inSubShapeID Sub shape id that indicates the leaf shape relative to this shape
  206. /// @return True if the ID is valid, false if not
  207. inline bool IsSubShapeIDValid(SubShapeID inSubShapeID) const
  208. {
  209. SubShapeID remainder;
  210. return inSubShapeID.PopID(GetSubShapeIDBits(), remainder) < mSubShapes.size();
  211. }
  212. /// Convert SubShapeID to sub shape index
  213. /// @param inSubShapeID Sub shape id that indicates the leaf shape relative to this shape
  214. /// @param outRemainder This is the sub shape ID for the sub shape of the compound after popping off the index
  215. /// @return The index of the sub shape of this compound
  216. inline uint32 GetSubShapeIndexFromID(SubShapeID inSubShapeID, SubShapeID &outRemainder) const
  217. {
  218. uint32 idx = inSubShapeID.PopID(GetSubShapeIDBits(), outRemainder);
  219. JPH_ASSERT(idx < mSubShapes.size(), "Invalid SubShapeID");
  220. return idx;
  221. }
  222. /// @brief Convert a sub shape index to a sub shape ID
  223. /// @param inIdx Index of the sub shape of this compound
  224. /// @param inParentSubShapeID Parent SubShapeID (describing the path to the compound shape)
  225. /// @return A sub shape ID creator that contains the full path to the sub shape with index inIdx
  226. inline SubShapeIDCreator GetSubShapeIDFromIndex(int inIdx, const SubShapeIDCreator &inParentSubShapeID) const
  227. {
  228. return inParentSubShapeID.PushID(inIdx, GetSubShapeIDBits());
  229. }
  230. // See Shape
  231. virtual void SaveBinaryState(StreamOut &inStream) const override;
  232. virtual void SaveSubShapeState(ShapeList &outSubShapes) const override;
  233. virtual void RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) override;
  234. // See Shape::GetStatsRecursive
  235. virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const override;
  236. // See Shape::GetVolume
  237. virtual float GetVolume() const override;
  238. // See Shape::IsValidScale
  239. virtual bool IsValidScale(Vec3Arg inScale) const override;
  240. // See Shape::MakeScaleValid
  241. virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
  242. // Register shape functions with the registry
  243. static void sRegister();
  244. protected:
  245. // See: Shape::RestoreBinaryState
  246. virtual void RestoreBinaryState(StreamIn &inStream) override;
  247. // Visitors for collision detection
  248. struct CastRayVisitor;
  249. struct CastRayVisitorCollector;
  250. struct CollidePointVisitor;
  251. struct CastShapeVisitor;
  252. struct CollectTransformedShapesVisitor;
  253. struct CollideCompoundVsShapeVisitor;
  254. struct CollideShapeVsCompoundVisitor;
  255. template <class BoxType> struct GetIntersectingSubShapesVisitor;
  256. /// Determine amount of bits needed to encode sub shape id
  257. inline uint GetSubShapeIDBits() const
  258. {
  259. // Ensure we have enough bits to encode our shape [0, n - 1]
  260. uint32 n = uint32(mSubShapes.size()) - 1;
  261. return 32 - CountLeadingZeros(n);
  262. }
  263. /// Determine the inner radius of this shape
  264. inline void CalculateInnerRadius()
  265. {
  266. mInnerRadius = FLT_MAX;
  267. for (const SubShape &s : mSubShapes)
  268. mInnerRadius = min(mInnerRadius, s.mShape->GetInnerRadius());
  269. }
  270. Vec3 mCenterOfMass { Vec3::sZero() }; ///< Center of mass of the compound
  271. AABox mLocalBounds { Vec3::sZero(), Vec3::sZero() };
  272. SubShapes mSubShapes;
  273. float mInnerRadius = FLT_MAX; ///< Smallest radius of GetInnerRadius() of child shapes
  274. private:
  275. // Helper functions called by CollisionDispatch
  276. static 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);
  277. };
  278. JPH_NAMESPACE_END