CompoundShape.h 14 KB

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