Shape.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Physics/Body/MassProperties.h>
  5. #include <Physics/Collision/BackFaceMode.h>
  6. #include <Physics/Collision/CollisionCollector.h>
  7. #include <Geometry/AABox.h>
  8. #include <Core/Reference.h>
  9. #include <Core/Color.h>
  10. #include <Core/Result.h>
  11. #include <ObjectStream/SerializableObject.h>
  12. namespace JPH {
  13. struct RayCast;
  14. class RayCastSettings;
  15. struct ShapeCast;
  16. class ShapeCastSettings;
  17. class RayCastResult;
  18. class ShapeCastResult;
  19. class CollidePointResult;
  20. class CollideShapeResult;
  21. class ShapeFilter;
  22. class SubShapeIDCreator;
  23. class SubShapeID;
  24. class PhysicsMaterial;
  25. class TransformedShape;
  26. class Plane;
  27. class Shape;
  28. class StreamOut;
  29. class StreamIn;
  30. #ifdef JPH_DEBUG_RENDERER
  31. class DebugRenderer;
  32. #endif // JPH_DEBUG_RENDERER
  33. using CastRayCollector = CollisionCollector<RayCastResult, CollisionCollectorTraitsCastRay>;
  34. using CastShapeCollector = CollisionCollector<ShapeCastResult, CollisionCollectorTraitsCastShape>;
  35. using CollidePointCollector = CollisionCollector<CollidePointResult, CollisionCollectorTraitsCollidePoint>;
  36. using CollideShapeCollector = CollisionCollector<CollideShapeResult, CollisionCollectorTraitsCollideShape>;
  37. using TransformedShapeCollector = CollisionCollector<TransformedShape, CollisionCollectorTraitsCollideShape>;
  38. using ShapeRefC = RefConst<Shape>;
  39. using ShapeList = vector<ShapeRefC>;
  40. using PhysicsMaterialRefC = RefConst<PhysicsMaterial>;
  41. using PhysicsMaterialList = vector<PhysicsMaterialRefC>;
  42. /// Shapes are categorized in groups, each shape can return which group it belongs to through its Shape::GetType function.
  43. enum class EShapeType : uint8
  44. {
  45. Convex, ///< Used by ConvexShape, all shapes that use the generic convex vs convex collision detection system (box, sphere, capsule, tapered capsule, cylinder, triangle)
  46. Compound, ///< Used by CompoundShape
  47. Decorated, ///< Used by DecoratedShape
  48. Mesh, ///< Used by MeshShape
  49. HeightField, ///< Used by HeightFieldShape
  50. // User defined shapes
  51. User1,
  52. User2,
  53. User3,
  54. User4,
  55. };
  56. /// This enumerates all shape types, each shape can return its type through Shape::GetSubType
  57. enum class EShapeSubType : uint8
  58. {
  59. // Convex shapes
  60. Sphere,
  61. Box,
  62. Triangle,
  63. Capsule,
  64. TaperedCapsule,
  65. Cylinder,
  66. ConvexHull,
  67. // Compound shapes
  68. StaticCompound,
  69. MutableCompound,
  70. // Decorated shapes
  71. RotatedTranslated,
  72. Scaled,
  73. OffsetCenterOfMass,
  74. // Other shapes
  75. Mesh,
  76. HeightField,
  77. // User defined shapes
  78. User1,
  79. User2,
  80. User3,
  81. User4,
  82. User5,
  83. User6,
  84. User7,
  85. User8,
  86. };
  87. // Sets of shape sub types
  88. static constexpr EShapeSubType sAllSubShapeTypes[] = { EShapeSubType::Sphere, EShapeSubType::Box, EShapeSubType::Triangle, EShapeSubType::Capsule, EShapeSubType::TaperedCapsule, EShapeSubType::Cylinder, EShapeSubType::ConvexHull, EShapeSubType::StaticCompound, EShapeSubType::MutableCompound, EShapeSubType::RotatedTranslated, EShapeSubType::Scaled, EShapeSubType::OffsetCenterOfMass, EShapeSubType::Mesh, EShapeSubType::HeightField, EShapeSubType::User1, EShapeSubType::User2, EShapeSubType::User3, EShapeSubType::User4, EShapeSubType::User5, EShapeSubType::User6, EShapeSubType::User7, EShapeSubType::User8 };
  89. static constexpr EShapeSubType sConvexSubShapeTypes[] = { EShapeSubType::Sphere, EShapeSubType::Box, EShapeSubType::Triangle, EShapeSubType::Capsule, EShapeSubType::TaperedCapsule, EShapeSubType::Cylinder, EShapeSubType::ConvexHull };
  90. static constexpr EShapeSubType sCompoundSubShapeTypes[] = { EShapeSubType::StaticCompound, EShapeSubType::MutableCompound };
  91. static constexpr EShapeSubType sDecoratorSubShapeTypes[] = { EShapeSubType::RotatedTranslated, EShapeSubType::Scaled, EShapeSubType::OffsetCenterOfMass };
  92. /// How many shape types we support
  93. static constexpr uint NumSubShapeTypes = (uint)size(sAllSubShapeTypes);
  94. /// Names of sub shape types
  95. static constexpr const char *sSubShapeTypeNames[] = { "Sphere", "Box", "Triangle", "Capsule", "TaperedCapsule", "Cylinder", "ConvexHull", "StaticCompound", "MutableCompound", "RotatedTranslated", "Scaled", "OffsetCenterOfMass", "Mesh", "HeightField", "User1", "User2", "User3", "User4", "User5", "User6", "User7", "User8" };
  96. static_assert(size(sSubShapeTypeNames) == NumSubShapeTypes);
  97. /// Class that can construct shapes and that is serializable using the ObjectStream system.
  98. /// Can be used to store shape data in 'uncooked' form (i.e. in a form that is still human readable and authorable).
  99. /// Once the shape has been created using the Create() function, the data will be moved into the Shape class
  100. /// in a form that is optimized for collision detection. After this, the ShapeSettings object is no longer needed
  101. /// and can be destroyed. Each shape class has a derived class of the ShapeSettings object to store shape specific
  102. /// data.
  103. class ShapeSettings : public SerializableObject, public RefTarget<ShapeSettings>
  104. {
  105. public:
  106. JPH_DECLARE_SERIALIZABLE_ABSTRACT(ShapeSettings)
  107. using ShapeResult = Result<Ref<Shape>>;
  108. /// Create a shape according to the settings specified by this object.
  109. virtual ShapeResult Create() const = 0;
  110. /// User data (to be used freely by the application)
  111. uint32 mUserData = 0;
  112. protected:
  113. mutable ShapeResult mCachedResult;
  114. };
  115. /// Function table for functions on shapes
  116. class ShapeFunctions
  117. {
  118. public:
  119. /// Construct a shape
  120. Shape * (*mConstruct)() = nullptr;
  121. /// Color of the shape when drawing
  122. Color mColor = Color::sBlack;
  123. /// Get an entry in the registry for a particular sub type
  124. static inline ShapeFunctions & sGet(EShapeSubType inSubType) { return sRegistry[(int)inSubType]; }
  125. private:
  126. static ShapeFunctions sRegistry[NumSubShapeTypes];
  127. };
  128. /// Base class for all shapes (collision volume of a body). Defines a virtual interface for collision detection.
  129. class Shape : public RefTarget<Shape>
  130. {
  131. public:
  132. using ShapeResult = ShapeSettings::ShapeResult;
  133. /// Constructor
  134. Shape(EShapeType inType, EShapeSubType inSubType) : mShapeType(inType), mShapeSubType(inSubType) { }
  135. Shape(EShapeType inType, EShapeSubType inSubType, const ShapeSettings &inSettings, [[maybe_unused]] ShapeResult &outResult) : mUserData(inSettings.mUserData), mShapeType(inType), mShapeSubType(inSubType) { }
  136. /// Destructor
  137. virtual ~Shape() = default;
  138. /// Get type
  139. inline EShapeType GetType() const { return mShapeType; }
  140. inline EShapeSubType GetSubType() const { return mShapeSubType; }
  141. /// User data (to be used freely by the application)
  142. uint32 GetUserData() const { return mUserData; }
  143. void SetUserData(uint32 inUserData) { mUserData = inUserData; }
  144. /// Check if this shape can only be used to create a static body or if it can also be dynamic/kinematic
  145. virtual bool MustBeStatic() const { return false; }
  146. /// All shapes are centered around their center of mass. This function returns the center of mass position that needs to be applied to transform the shape to where it was created.
  147. virtual Vec3 GetCenterOfMass() const { return Vec3::sZero(); }
  148. /// Get local bounding box including convex radius, this box is centered around the center of mass rather than the world transform
  149. virtual AABox GetLocalBounds() const = 0;
  150. /// Get the max number of sub shape ID bits that are needed to be able to address any leaf shape in this shape. Used mainly for checking that it is smaller or equal than SubShapeID::MaxBits.
  151. virtual uint GetSubShapeIDBitsRecursive() const = 0;
  152. /// Get world space bounds including convex radius.
  153. /// This shape is scaled by inScale in local space first.
  154. /// This function can be overridden to return a closer fitting world space bounding box, by default it will just transform what GetLocalBounds() returns.
  155. virtual AABox GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const { return GetLocalBounds().Scaled(inScale).Transformed(inCenterOfMassTransform); }
  156. /// Returns the radius of the biggest sphere that fits entirely in the shape. In case this shape consists of multiple sub shapes, it returns the smallest sphere of the parts.
  157. /// This can be used as a measure of how far the shape can be moved without risking going through geometry.
  158. virtual float GetInnerRadius() const = 0;
  159. /// Calculate the mass and inertia of this shape
  160. virtual MassProperties GetMassProperties() const = 0;
  161. /// Get the material assigned to a particular sub shape ID
  162. virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const = 0;
  163. /// Get the surface normal of a particular sub shape ID and point on surface (all vectors are relative to center of mass for this shape)
  164. virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const = 0;
  165. /// Get the user data of a particular sub shape ID
  166. virtual uint32 GetSubShapeUserData(const SubShapeID &inSubShapeID) const { return mUserData; }
  167. /// Get the direct child sub shape and its transform for a sub shape ID.
  168. /// @param inSubShapeID Sub shape ID that indicates the path to the leaf shape
  169. /// @param inPositionCOM The position of the center of mass of this shape
  170. /// @param inRotation The orientation of this shape
  171. /// @param inScale Scale of this shape
  172. /// @param outRemainder The remainder of the sub shape ID after removing the sub shape
  173. /// @return Direct child sub shape and its transform, note that the body ID and sub shape ID will be invalid
  174. virtual TransformedShape GetSubShapeTransformedShape(const SubShapeID &inSubShapeID, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, SubShapeID &outRemainder) const;
  175. /// Gets the properties needed to do buoyancy calculations for a body using this shape
  176. /// @param inCenterOfMassTransform Transform that takes this shape (centered around center of mass) to world space
  177. /// @param inScale Scale in local space of the shape
  178. /// @param inSurface The surface plane of the liquid in world space
  179. /// @param outTotalVolume On return this contains the total volume of the shape
  180. /// @param outSubmergedVolume On return this contains the submerged volume of the shape
  181. /// @param outCenterOfBuoyancy On return this contains the world space center of mass of the submerged volume
  182. virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy) const = 0;
  183. #ifdef JPH_DEBUG_RENDERER
  184. /// Draw the shape at a particular location with a particular color (debugging purposes)
  185. virtual void Draw(DebugRenderer *inRenderer, Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const = 0;
  186. /// Draw the results of the GetSupportFunction with the convex radius added back on to show any errors introduced by this process (only relevant for convex shapes)
  187. virtual void DrawGetSupportFunction(DebugRenderer *inRenderer, Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const { }
  188. /// Draw the results of the GetSupportingFace function to show any errors introduced by this process (only relevant for convex shapes)
  189. virtual void DrawGetSupportingFace(DebugRenderer *inRenderer, Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const { }
  190. #endif // JPH_DEBUG_RENDERER
  191. /// Cast a ray against this shape, returns true if it finds a hit closer than ioHit.mFraction and updates that fraction. Otherwise ioHit is left untouched and the function returns false.
  192. /// Note that the ray should be relative to the center of mass of this shape (i.e. subtract Shape::GetCenterOfMass() from RayCast::mOrigin if you want to cast against the shape in the space it was created).
  193. /// If this is a solid shape and the ray starts inside the object, the returned fraction will be 0.
  194. virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const = 0;
  195. /// Cast a ray against this shape. Allows returning multiple hits through ioCollector.
  196. virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector) const = 0;
  197. /// Check if inPoint is inside this shape. For this tests all shapes are treated as if they were solid.
  198. /// Note that inPoint should be relative to the center of mass of this shape (i.e. subtract Shape::GetCenterOfMass() from inPoint if you want to test against the shape in the space it was created).
  199. /// For a mesh shape, this test will only provide sensible information if the mesh is a closed manifold.
  200. /// For each shape that collides, ioCollector will receive a hit.
  201. virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector) const = 0;
  202. /// Collect the leaf transformed shapes of all leaf shapes of this shape.
  203. /// inBox is the world space axis aligned box which leaf shapes should collide with.
  204. /// inPositionCOM/inRotation/inScale describes the transform of this shape.
  205. /// inSubShapeIDCeator represents the current sub shape ID of this shape.
  206. virtual void CollectTransformedShapes(const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector) const;
  207. /// Transforms this shape and all of its children with inTransform, resulting shape(s) are passed to ioCollector.
  208. /// Note that not all shapes support all transforms (especially true for scaling), the resulting shape will try to match the transform as accurately as possible.
  209. /// @param inCenterOfMassTransform The transform (rotation, translation, scale) that the center of mass of the shape should get
  210. /// @param ioCollector The transformed shapes will be passed to this collector
  211. virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const;
  212. /// Scale this shape. Note that not all shapes support all scales, this will return a shape that matches the scale as accurately as possible.
  213. /// @param inScale The scale to use for this shape (note: this scale is applied to the entire shape in the space it was created, most function apply the scale in the space of the leaf shapes and from the center of mass!)
  214. ShapeResult ScaleShape(Vec3Arg inScale) const;
  215. /// An opaque buffer that holds shape specific information during GetTrianglesStart/Next.
  216. struct alignas(16) GetTrianglesContext { uint8 mData[4288]; };
  217. /// This is the minimum amount of triangles that should be requested through GetTrianglesNext.
  218. static constexpr int cGetTrianglesMinTrianglesRequested = 32;
  219. /// To start iterating over triangles, call this function first.
  220. /// ioContext is a temporary buffer and should remain untouched until the last call to GetTrianglesNext.
  221. /// inBox is the world space bounding in which you want to get the triangles.
  222. /// inPositionCOM/inRotation/inScale describes the transform of this shape.
  223. /// To get the actual triangles call GetTrianglesNext.
  224. virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const = 0;
  225. /// Call this repeatedly to get all triangles in the box.
  226. /// outTriangleVertices should be large enough to hold 3 * inMaxTriangleRequested entries.
  227. /// outMaterials (if it is not null) should contain inMaxTrianglesRequested entries.
  228. /// The function returns the amount of triangles that it found (which will be <= inMaxTrianglesRequested), or 0 if there are no more triangles.
  229. /// Note that the function can return a value < inMaxTrianglesRequested and still have more triangles to process (triangles can be returned in blocks).
  230. /// Note that the function may return triangles outside of the requested box, only coarse culling is performed on the returned triangles.
  231. virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const = 0;
  232. ///@name Binary serialization of the shape. Note that this saves the 'cooked' shape in a format which will not be backwards compatible for newer library versions.
  233. /// In this case you need to recreate the shape from the ShapeSettings object and save it again. The user is expected to call SaveBinaryState followed by SaveMaterialState and SaveSubShapeState.
  234. /// The stream should be stored as is and the material and shape list should be saved using the applications own serialization system (e.g. by assigning an ID to each pointer).
  235. /// When restoring data, call sRestoreFromBinararyState to get the shape and then call RestoreMaterialState and RestoreSubShapeState to restore the pointers to the external objects.
  236. ///@{
  237. /// Saves the contents of the shape in binary form to inStream.
  238. virtual void SaveBinaryState(StreamOut &inStream) const;
  239. /// Creates a Shape of the correct type and restores its contents from the binary stream inStream.
  240. static ShapeResult sRestoreFromBinaryState(StreamIn &inStream);
  241. /// Outputs the material references that this shape has to outMaterials.
  242. virtual void SaveMaterialState(PhysicsMaterialList &outMaterials) const { }
  243. /// Restore the material references after calling sRestoreFromBinaryState. Note that the exact same materials need to be provided in the same order as returned by SaveMaterialState.
  244. virtual void RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials) { JPH_ASSERT(inNumMaterials == 0); }
  245. /// Outputs the shape references that this shape has to outSubShapes.
  246. virtual void SaveSubShapeState(ShapeList &outSubShapes) const { }
  247. /// Restore the shape references after calling sRestoreFromBinaryState. Note that the exact same shapes need to be provided in the same order as returned by SaveSubShapeState.
  248. virtual void RestoreSubShapeState(const ShapeRefC *inSubShapes, uint inNumShapes) { JPH_ASSERT(inNumShapes == 0); }
  249. using ShapeToIDMap = unordered_map<const Shape *, uint32>;
  250. using MaterialToIDMap = unordered_map<const PhysicsMaterial *, uint32>;
  251. using IDToShapeMap = vector<Ref<Shape>>;
  252. using IDToMaterialMap = vector<Ref<PhysicsMaterial>>;
  253. /// Save this shape, all its children and its materials. Pass in an empty map in ioShapeMap / ioMaterialMap or reuse the same map while saving multiple shapes to the same stream in order to avoid writing duplicates.
  254. void SaveWithChildren(StreamOut &inStream, ShapeToIDMap &ioShapeMap, MaterialToIDMap &ioMaterialMap) const;
  255. /// Restore a shape, all its children and materials. Pass in an empty map in ioShapeMap / ioMaterialMap or reuse the same map while reading multiple shapes from the same stream in order to restore duplicates.
  256. static ShapeResult sRestoreWithChildren(StreamIn &inStream, IDToShapeMap &ioShapeMap, IDToMaterialMap &ioMaterialMap);
  257. ///@}
  258. /// Class that holds information about the shape that can be used for logging / data collection purposes
  259. struct Stats
  260. {
  261. Stats(size_t inSizeBytes, uint inNumTriangles) : mSizeBytes(inSizeBytes), mNumTriangles(inNumTriangles) { }
  262. size_t mSizeBytes; ///< Amount of memory used by this shape (size in bytes)
  263. uint mNumTriangles; ///< Number of triangles in this shape (when applicable)
  264. };
  265. /// Get stats of this shape. Use for logging / data collection purposes only. Does not add values from child shapes, use GetStatsRecursive for this.
  266. virtual Stats GetStats() const = 0;
  267. using VisitedShapes = unordered_set<const Shape *>;
  268. /// Get the combined stats of this shape and its children.
  269. /// @param ioVisitedShapes is used to track which shapes have already been visited, to avoid calculating the wrong memory size.
  270. virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const;
  271. ///< Volume of this shape (m^3). Note that for compound shapes the volume may be incorrect since child shapes can overlap which is not accounted for.
  272. virtual float GetVolume() const = 0;
  273. /// Test if inScale is a valid scale for this shape. Some shapes can only be scaled uniformly, compound shapes cannot handle shapes
  274. /// being rotated and scaled (this would cause shearing). In this case this function will return false.
  275. virtual bool IsValidScale(Vec3Arg inScale) const { return !inScale.IsNearZero(); }
  276. #ifdef JPH_DEBUG_RENDERER
  277. /// Debug helper which draws the intersection between water and the shapes, the center of buoyancy and the submerged volume
  278. static bool sDrawSubmergedVolumes;
  279. #endif // JPH_DEBUG_RENDERER
  280. protected:
  281. /// This function should not be called directly, it is used by sRestoreFromBinaryState.
  282. virtual void RestoreBinaryState(StreamIn &inStream);
  283. private:
  284. uint32 mUserData = 0;
  285. EShapeType mShapeType;
  286. EShapeSubType mShapeSubType;
  287. };
  288. } // JPH