HeightFieldShape.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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/PhysicsMaterial.h>
  6. #ifdef JPH_DEBUG_RENDERER
  7. #include <Renderer/DebugRenderer.h>
  8. #endif // JPH_DEBUG_RENDERER
  9. namespace JPH {
  10. class ConvexShape;
  11. class CollideShapeSettings;
  12. /// Constants for HeightFieldShape, this was moved out of the HeightFieldShape because of a linker bug
  13. namespace HeightFieldShapeConstants
  14. {
  15. /// Value used to create gaps in the height field
  16. constexpr float cNoCollisionValue = FLT_MAX;
  17. /// Stack size to use during WalkHeightField
  18. constexpr int cStackSize = 128;
  19. /// A position in the hierarchical grid is defined by a level (which grid), x and y position. We encode this in a single uint32 as: level << 28 | y << 14 | x
  20. constexpr uint cNumBitsXY = 14;
  21. constexpr uint cMaskBitsXY = (1 << cNumBitsXY) - 1;
  22. constexpr uint cLevelShift = 2 * cNumBitsXY;
  23. /// When height samples are converted to 16 bit:
  24. constexpr uint16 cNoCollisionValue16 = 0xffff; ///< This is the magic value for 'no collision'
  25. constexpr uint16 cMaxHeightValue16 = 0xfffe; ///< This is the maximum allowed height value
  26. };
  27. /// Class that constructs a HeightFieldShape
  28. class HeightFieldShapeSettings final : public ShapeSettings
  29. {
  30. public:
  31. JPH_DECLARE_SERIALIZABLE_VIRTUAL(HeightFieldShapeSettings)
  32. /// Default constructor for deserialization
  33. HeightFieldShapeSettings() = default;
  34. /// Create a height field shape of inSampleCount * inSampleCount vertices.
  35. /// The height field is a surface defined by: inOffset + inScale * (x, inSamples[y * inSampleCount + x], y).
  36. /// where x and y are integers in the range x and y e [0, inSampleCount - 1].
  37. /// inSampleCount: inSampleCount / mBlockSize must be a power of 2 and minimally 2.
  38. /// inSamples: inSampleCount^2 vertices.
  39. /// inMaterialIndices: (inSampleCount - 1)^2 indices that index into inMaterialList.
  40. HeightFieldShapeSettings(const float *inSamples, Vec3Arg inOffset, Vec3Arg inScale, uint32 inSampleCount, const uint8 *inMaterialIndices = nullptr, const PhysicsMaterialList &inMaterialList = PhysicsMaterialList());
  41. // See: ShapeSettings
  42. virtual ShapeResult Create() const override;
  43. /// Determine the minimal and maximal value of mHeightSamples (will ignore cNoCollisionValue)
  44. /// @param outMinValue The minimal value fo mHeightSamples or FLT_MAX if no samples have collision
  45. /// @param outMaxValue The maximal value fo mHeightSamples or -FLT_MAX if no samples have collision
  46. /// @param outQuantizationScale (value - outMinValue) * outQuantizationScale quantizes a height sample to 16 bits
  47. void DetermineMinAndMaxSample(float &outMinValue, float &outMaxValue, float &outQuantizationScale) const;
  48. /// Given mBlockSize, mSampleCount and mHeightSamples, calculate the amount of bits needed to stay below absolute error inMaxError
  49. /// @param inMaxError Maximum allowed error in mHeightSamples after compression (note that this does not take mScale.Y into account)
  50. /// @return Needed bits per sample in the range [1, 8].
  51. uint32 CalculateBitsPerSampleForError(float inMaxError) const;
  52. /// The height field is a surface defined by: mOffset + mScale * (x, mHeightSamples[y * mSampleCount + x], y).
  53. /// where x and y are integers in the range x and y e [0, mSampleCount - 1].
  54. Vec3 mOffset = Vec3::sZero();
  55. Vec3 mScale = Vec3::sReplicate(1.0f);
  56. uint32 mSampleCount = 0;
  57. /// The heightfield is divided in blocks of mBlockSize * mBlockSize * 2 triangles and the acceleration structure culls blocks only,
  58. /// bigger block sizes reduce memory consumption but also reduce query performance. Sensible values are [1, 4], does not need to be
  59. /// a power of 2.
  60. uint32 mBlockSize = 2;
  61. /// How many bits per sample to use to compress the height field. Can be in the range [1, 8].
  62. /// Note that each sample is compressed relative to the min/max value of its block of mBlockSize * mBlockSize pixels so the effective precision is higher.
  63. /// Also note that increasing mBlockSize saves more memory than reducing the amount of bits per sample.
  64. uint32 mBitsPerSample = 8;
  65. vector<float> mHeightSamples;
  66. vector<uint8> mMaterialIndices;
  67. /// The materials of square at (x, y) is: mMaterials[mMaterialIndices[x + y * (mSampleCount - 1)]]
  68. PhysicsMaterialList mMaterials;
  69. };
  70. /// A height field shape. Cannot be used as a dynamic object.
  71. class HeightFieldShape final : public Shape
  72. {
  73. public:
  74. JPH_DECLARE_RTTI_VIRTUAL(HeightFieldShape)
  75. /// Constructor
  76. HeightFieldShape() = default;
  77. HeightFieldShape(const HeightFieldShapeSettings &inSettings, ShapeResult &outResult);
  78. /// Get type
  79. virtual EShapeType GetType() const override { return EShapeType::HeightField; }
  80. // See Shape::MustBeStatic
  81. virtual bool MustBeStatic() const override { return true; }
  82. // See Shape::GetLocalBounds
  83. virtual AABox GetLocalBounds() const override;
  84. // See Shape::GetSubShapeIDBitsRecursive
  85. virtual uint GetSubShapeIDBitsRecursive() const override { return GetSubShapeIDBits(); }
  86. // See Shape::GetInnerRadius
  87. virtual float GetInnerRadius() const override { return 0.0f; }
  88. // See Shape::GetMassProperties
  89. virtual MassProperties GetMassProperties() const override;
  90. // See Shape::GetMaterial
  91. virtual const PhysicsMaterial * GetMaterial(const SubShapeID &inSubShapeID) const override;
  92. /// Overload to get the material at a particular location
  93. const PhysicsMaterial * GetMaterial(uint inX, uint inY) const;
  94. // See Shape::GetSurfaceNormal
  95. virtual Vec3 GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const override;
  96. // See Shape::GetSubmergedVolume
  97. virtual void GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy) const override { JPH_ASSERT(false, "Not supported"); }
  98. #ifdef JPH_DEBUG_RENDERER
  99. // See Shape::Draw
  100. virtual void Draw(DebugRenderer *inRenderer, Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override;
  101. #endif // JPH_DEBUG_RENDERER
  102. // See Shape::CastRay
  103. virtual bool CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const override;
  104. virtual void CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector) const override;
  105. // See: Shape::CollidePoint
  106. virtual void CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector) const override;
  107. // See Shape::CastShape
  108. virtual void CastShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) const override;
  109. // See Shape::GetTrianglesStart
  110. virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override;
  111. // See Shape::GetTrianglesNext
  112. virtual int GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials = nullptr) const override;
  113. /// Collide 2 shapes and pass any collisions on to ioCollector
  114. static void sCollideConvexVsHeightField(const ConvexShape *inShape1, const HeightFieldShape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector);
  115. /// Get height field position at sampled location (inX, inY).
  116. /// where inX and inY are integers in the range inX e [0, mSampleCount - 1] and inY e [0, mSampleCount - 1].
  117. Vec3 GetPosition(uint inX, uint inY) const;
  118. /// Check if height field at sampled location (inX, inY) has collision (has a hole or not)
  119. bool IsNoCollision(uint inX, uint inY) const;
  120. /// Projects inLocalPosition (a point in the space of the shape) along the Y axis onto the surface and returns it in outSurfacePosition.
  121. /// When there is no surface position (because of a hole or because the point is outside the heightfield) the function will return false.
  122. bool ProjectOntoSurface(Vec3Arg inLocalPosition, Vec3 &outSurfacePosition, SubShapeID &outSubShapeID) const;
  123. // See Shape
  124. virtual void SaveBinaryState(StreamOut &inStream) const override;
  125. virtual void SaveMaterialState(PhysicsMaterialList &outMaterials) const override;
  126. virtual void RestoreMaterialState(const PhysicsMaterialList &inMaterials) override;
  127. // See Shape::GetStats
  128. virtual Stats GetStats() const override;
  129. // See Shape::GetVolume
  130. virtual float GetVolume() const override { return 0; }
  131. #ifdef JPH_DEBUG_RENDERER
  132. // Settings
  133. static bool sDrawTriangleOutlines;
  134. #endif // JPH_DEBUG_RENDERER
  135. protected:
  136. // See: Shape::RestoreBinaryState
  137. virtual void RestoreBinaryState(StreamIn &inStream) override;
  138. private:
  139. class DecodingContext; ///< Context class for walking through all nodes of a heightfield
  140. struct HSGetTrianglesContext; ///< Context class for GetTrianglesStart/Next
  141. /// Calculate commonly used values and store them in the shape
  142. void CacheValues();
  143. /// Calculate bit mask for all active edges in the heightfield
  144. void CalculateActiveEdges();
  145. /// Store material indices in the least amount of bits per index possible
  146. void StoreMaterialIndices(const vector<uint8> &inMaterialIndices);
  147. /// For location (inX, inY) get the block that contains this position and get the offset and scale needed to decode a uint8 height sample to a uint16
  148. void GetBlockOffsetAndScale(uint inX, uint inY, float &outBlockOffset, float &outBlockScale) const;
  149. /// Get the height sample at position (inX, inY)
  150. inline uint8 GetHeightSample(uint inX, uint inY) const;
  151. /// Faster version of GetPosition when block offset and scale are already known
  152. Vec3 GetPosition(uint inX, uint inY, float inBlockOffset, float inBlockScale) const;
  153. /// Determine amount of bits needed to encode sub shape id
  154. uint GetSubShapeIDBits() const;
  155. /// En/decode a sub shape ID. inX and inY specify the coordinate of the triangle. inTriangle == 0 is the lower triangle, inTriangle == 1 is the upper triangle.
  156. inline SubShapeID EncodeSubShapeID(const SubShapeIDCreator &inCreator, uint inX, uint inY, uint inTriangle) const;
  157. inline void DecodeSubShapeID(const SubShapeID &inSubShapeID, uint &outX, uint &outY, uint &outTriangle) const;
  158. /// Get the edge flags for a triangle
  159. inline uint8 GetEdgeFlags(uint inX, uint inY, uint inTriangle) const;
  160. /// Visit the entire height field using a visitor pattern
  161. template <class Visitor>
  162. JPH_INLINE void WalkHeightField(Visitor &ioVisitor) const;
  163. /// A block of 2x2 ranges used to form a hierarchical grid, ordered left top, right top, left bottom, right bottom
  164. struct alignas(16) RangeBlock
  165. {
  166. uint16 mMin[4];
  167. uint16 mMax[4];
  168. };
  169. /// Offset of first RangedBlock in grid per level
  170. static const uint sGridOffsets[];
  171. /// The height field is a surface defined by: mOffset + mScale * (x, mHeightSamples[y * mSampleCount + x], y).
  172. /// where x and y are integers in the range x and y e [0, mSampleCount - 1].
  173. Vec3 mOffset = Vec3::sZero();
  174. Vec3 mScale = Vec3::sReplicate(1.0f);
  175. /// Height data
  176. uint32 mSampleCount = 0; ///< See HeightFieldShapeSettings::mSampleCount
  177. uint32 mBlockSize = 2; ///< See HeightFieldShapeSettings::mBlockSize
  178. uint8 mBitsPerSample = 8; ///< See HeightFieldShapeSettings::mBitsPerSample
  179. uint8 mSampleMask = 0xff; ///< All bits set for a sample: (1 << mBitsPerSample) - 1, used to indicate that there's no collision
  180. uint16 mMinSample = HeightFieldShapeConstants::cNoCollisionValue16; ///< Min and max value in mHeightSamples quantized to 16 bit, for calculating bounding box
  181. uint16 mMaxSample = HeightFieldShapeConstants::cNoCollisionValue16;
  182. vector<RangeBlock> mRangeBlocks; ///< Hierarchical grid of range data describing the height variations within 1 block. The grid for level <level> starts at offset sGridOffsets[<level>]
  183. vector<uint8> mHeightSamples; ///< mBitsPerSample-bit height samples. Value [0, mMaxHeightValue] maps to highest detail grid in mRangeBlocks [mMin, mMax]. mNoCollisionValue is reserved to indicate no collision.
  184. vector<uint8> mActiveEdges; ///< (mSampleCount - 1)^2 * 3-bit active edge flags.
  185. /// Materials
  186. PhysicsMaterialList mMaterials; ///< The materials of square at (x, y) is: mMaterials[mMaterialIndices[x + y * (mSampleCount - 1)]]
  187. vector<uint8> mMaterialIndices; ///< Compressed to the minimum amount of bits per material index (mSampleCount - 1) * (mSampleCount - 1) * mNumBitsPerMaterialIndex bits of data
  188. uint32 mNumBitsPerMaterialIndex = 0; ///< Number of bits per material index
  189. #ifdef JPH_DEBUG_RENDERER
  190. /// Temporary rendering data
  191. mutable vector<DebugRenderer::GeometryRef> mGeometry;
  192. mutable bool mCachedUseMaterialColors = false; ///< This is used to regenerate the triangle batch if the drawing settings change
  193. #endif // JPH_DEBUG_RENDERER
  194. };
  195. } // JPH