DebugRenderer.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #ifndef JPH_DEBUG_RENDERER
  6. #error This file should only be included when JPH_DEBUG_RENDERER is defined
  7. #endif // !JPH_DEBUG_RENDERER
  8. #ifndef JPH_DEBUG_RENDERER_EXPORT
  9. // By default export the debug renderer
  10. #define JPH_DEBUG_RENDERER_EXPORT JPH_EXPORT
  11. #endif // !JPH_DEBUG_RENDERER_EXPORT
  12. #include <Jolt/Core/Color.h>
  13. #include <Jolt/Core/Reference.h>
  14. #include <Jolt/Core/HashCombine.h>
  15. #include <Jolt/Core/UnorderedMap.h>
  16. #include <Jolt/Core/NonCopyable.h>
  17. #include <Jolt/Math/Float2.h>
  18. #include <Jolt/Geometry/IndexedTriangle.h>
  19. #include <Jolt/Geometry/AABox.h>
  20. JPH_NAMESPACE_BEGIN
  21. class OrientedBox;
  22. /// Simple triangle renderer for debugging purposes.
  23. ///
  24. /// Inherit from this class to provide your own implementation.
  25. ///
  26. /// Implement the following virtual functions:
  27. /// - DrawLine
  28. /// - DrawTriangle
  29. /// - DrawText3D
  30. /// - CreateTriangleBatch
  31. /// - DrawGeometry
  32. ///
  33. /// Make sure you call Initialize() from the constructor of your implementation.
  34. ///
  35. /// The CreateTriangleBatch is used to prepare a batch of triangles to be drawn by a single DrawGeometry call,
  36. /// which means that Jolt can render a complex scene much more efficiently than when each triangle in that scene would have been drawn through DrawTriangle.
  37. ///
  38. /// Note that an implementation that implements CreateTriangleBatch and DrawGeometry is provided by DebugRendererSimple which can be used to start quickly.
  39. class JPH_DEBUG_RENDERER_EXPORT DebugRenderer : public NonCopyable
  40. {
  41. public:
  42. JPH_OVERRIDE_NEW_DELETE
  43. /// Constructor
  44. DebugRenderer();
  45. virtual ~DebugRenderer();
  46. /// Call once after frame is complete. Releases unused dynamically generated geometry assets.
  47. void NextFrame();
  48. /// Draw line
  49. virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) = 0;
  50. /// Draw wireframe box
  51. void DrawWireBox(const AABox &inBox, ColorArg inColor);
  52. void DrawWireBox(const OrientedBox &inBox, ColorArg inColor);
  53. void DrawWireBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor);
  54. /// Draw a marker on a position
  55. void DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize);
  56. /// Draw an arrow
  57. void DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize);
  58. /// Draw coordinate system (3 arrows, x = red, y = green, z = blue)
  59. void DrawCoordinateSystem(RMat44Arg inTransform, float inSize = 1.0f);
  60. /// Draw a plane through inPoint with normal inNormal
  61. void DrawPlane(RVec3Arg inPoint, Vec3Arg inNormal, ColorArg inColor, float inSize);
  62. /// Draw wireframe triangle
  63. void DrawWireTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor);
  64. /// Draw a wireframe polygon
  65. template <class VERTEX_ARRAY>
  66. void DrawWirePolygon(RMat44Arg inTransform, const VERTEX_ARRAY &inVertices, ColorArg inColor, float inArrowSize = 0.0f) { for (typename VERTEX_ARRAY::size_type i = 0; i < inVertices.size(); ++i) DrawArrow(inTransform * inVertices[i], inTransform * inVertices[(i + 1) % inVertices.size()], inColor, inArrowSize); }
  67. /// Draw wireframe sphere
  68. void DrawWireSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, int inLevel = 3);
  69. void DrawWireUnitSphere(RMat44Arg inMatrix, ColorArg inColor, int inLevel = 3);
  70. /// Enum that determines if a shadow should be cast or not
  71. enum class ECastShadow
  72. {
  73. On, ///< This shape should cast a shadow
  74. Off ///< This shape should not cast a shadow
  75. };
  76. /// Determines how triangles are drawn
  77. enum class EDrawMode
  78. {
  79. Solid, ///< Draw as a solid shape
  80. Wireframe, ///< Draw as wireframe
  81. };
  82. /// Draw a single back face culled triangle
  83. virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::Off) = 0;
  84. /// Draw a box
  85. void DrawBox(const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  86. void DrawBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  87. /// Draw a sphere
  88. void DrawSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  89. void DrawUnitSphere(RMat44Arg inMatrix, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  90. /// Draw a capsule with one half sphere at (0, -inHalfHeightOfCylinder, 0) and the other half sphere at (0, inHalfHeightOfCylinder, 0) and radius inRadius.
  91. /// The capsule will be transformed by inMatrix.
  92. void DrawCapsule(RMat44Arg inMatrix, float inHalfHeightOfCylinder, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  93. /// Draw a cylinder with top (0, inHalfHeight, 0) and bottom (0, -inHalfHeight, 0) and radius inRadius.
  94. /// The cylinder will be transformed by inMatrix
  95. void DrawCylinder(RMat44Arg inMatrix, float inHalfHeight, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  96. /// Draw a bottomless cone.
  97. /// @param inTop Top of cone, center of base is at inTop + inAxis.
  98. /// @param inAxis Height and direction of cone
  99. /// @param inPerpendicular Perpendicular vector to inAxis.
  100. /// @param inHalfAngle Specifies the cone angle in radians (angle measured between inAxis and cone surface).
  101. /// @param inLength The length of the cone.
  102. /// @param inColor Color to use for drawing the cone.
  103. /// @param inCastShadow determines if this geometry should cast a shadow or not.
  104. /// @param inDrawMode determines if we draw the geometry solid or in wireframe.
  105. void DrawOpenCone(RVec3Arg inTop, Vec3Arg inAxis, Vec3Arg inPerpendicular, float inHalfAngle, float inLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  106. /// Draws cone rotation limits as used by the SwingTwistConstraintPart.
  107. /// @param inMatrix Matrix that transforms from constraint space to world space
  108. /// @param inSwingYHalfAngle See SwingTwistConstraintPart
  109. /// @param inSwingZHalfAngle See SwingTwistConstraintPart
  110. /// @param inEdgeLength Size of the edge of the cone shape
  111. /// @param inColor Color to use for drawing the cone.
  112. /// @param inCastShadow determines if this geometry should cast a shadow or not.
  113. /// @param inDrawMode determines if we draw the geometry solid or in wireframe.
  114. void DrawSwingConeLimits(RMat44Arg inMatrix, float inSwingYHalfAngle, float inSwingZHalfAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  115. /// Draws rotation limits as used by the SwingTwistConstraintPart.
  116. /// @param inMatrix Matrix that transforms from constraint space to world space
  117. /// @param inMinSwingYAngle See SwingTwistConstraintPart
  118. /// @param inMaxSwingYAngle See SwingTwistConstraintPart
  119. /// @param inMinSwingZAngle See SwingTwistConstraintPart
  120. /// @param inMaxSwingZAngle See SwingTwistConstraintPart
  121. /// @param inEdgeLength Size of the edge of the cone shape
  122. /// @param inColor Color to use for drawing the cone.
  123. /// @param inCastShadow determines if this geometry should cast a shadow or not.
  124. /// @param inDrawMode determines if we draw the geometry solid or in wireframe.
  125. void DrawSwingPyramidLimits(RMat44Arg inMatrix, float inMinSwingYAngle, float inMaxSwingYAngle, float inMinSwingZAngle, float inMaxSwingZAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  126. /// Draw a pie (part of a circle).
  127. /// @param inCenter The center of the circle.
  128. /// @param inRadius Radius of the circle.
  129. /// @param inNormal The plane normal in which the pie resides.
  130. /// @param inAxis The axis that defines an angle of 0 radians.
  131. /// @param inMinAngle The pie will be drawn between [inMinAngle, inMaxAngle] (in radians).
  132. /// @param inMaxAngle The pie will be drawn between [inMinAngle, inMaxAngle] (in radians).
  133. /// @param inColor Color to use for drawing the pie.
  134. /// @param inCastShadow determines if this geometry should cast a shadow or not.
  135. /// @param inDrawMode determines if we draw the geometry solid or in wireframe.
  136. void DrawPie(RVec3Arg inCenter, float inRadius, Vec3Arg inNormal, Vec3Arg inAxis, float inMinAngle, float inMaxAngle, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);
  137. /// Singleton instance
  138. static DebugRenderer * sInstance;
  139. /// Vertex format used by the triangle renderer
  140. class Vertex
  141. {
  142. public:
  143. Float3 mPosition;
  144. Float3 mNormal;
  145. Float2 mUV;
  146. Color mColor;
  147. };
  148. /// A single triangle
  149. class JPH_DEBUG_RENDERER_EXPORT Triangle
  150. {
  151. public:
  152. Triangle() = default;
  153. Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor);
  154. Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor, Vec3Arg inUVOrigin, Vec3Arg inUVDirection);
  155. Vertex mV[3];
  156. };
  157. /// Handle for a batch of triangles
  158. using Batch = Ref<RefTargetVirtual>;
  159. /// A single level of detail
  160. class LOD
  161. {
  162. public:
  163. Batch mTriangleBatch;
  164. float mDistance;
  165. };
  166. /// A geometry primitive containing triangle batches for various lods
  167. class Geometry : public RefTarget<Geometry>
  168. {
  169. public:
  170. JPH_OVERRIDE_NEW_DELETE
  171. /// Constructor
  172. Geometry(const AABox &inBounds) : mBounds(inBounds) { }
  173. Geometry(const Batch &inBatch, const AABox &inBounds) : mBounds(inBounds) { mLODs.push_back({ inBatch, FLT_MAX }); }
  174. /// Determine which LOD to render
  175. /// @param inCameraPosition Current position of the camera
  176. /// @param inWorldSpaceBounds World space bounds for this geometry (transform mBounds by model space matrix)
  177. /// @param inLODScaleSq is the squared scale of the model matrix, it is multiplied with the LOD distances in inGeometry to calculate the real LOD distance (so a number > 1 will force a higher LOD).
  178. /// @return The selected LOD.
  179. const LOD & GetLOD(Vec3Arg inCameraPosition, const AABox &inWorldSpaceBounds, float inLODScaleSq) const
  180. {
  181. float dist_sq = inWorldSpaceBounds.GetSqDistanceTo(inCameraPosition);
  182. for (const LOD &lod : mLODs)
  183. if (dist_sq <= inLODScaleSq * Square(lod.mDistance))
  184. return lod;
  185. return mLODs.back();
  186. }
  187. /// All level of details for this mesh
  188. Array<LOD> mLODs;
  189. /// Bounding box that encapsulates all LODs
  190. AABox mBounds;
  191. };
  192. /// Handle for a lodded triangle batch
  193. using GeometryRef = Ref<Geometry>;
  194. /// Calculate bounding box for a batch of triangles
  195. static AABox sCalculateBounds(const Vertex *inVertices, int inVertexCount);
  196. /// Create a batch of triangles that can be drawn efficiently
  197. virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) = 0;
  198. virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) = 0;
  199. Batch CreateTriangleBatch(const Array<Triangle> &inTriangles) { return CreateTriangleBatch(inTriangles.empty()? nullptr : &inTriangles[0], int(inTriangles.size())); }
  200. Batch CreateTriangleBatch(const Array<Vertex> &inVertices, const Array<uint32> &inIndices) { return CreateTriangleBatch(inVertices.empty()? nullptr : &inVertices[0], int(inVertices.size()), inIndices.empty()? nullptr : &inIndices[0], int(inIndices.size())); }
  201. Batch CreateTriangleBatch(const VertexList &inVertices, const IndexedTriangleNoMaterialList &inTriangles);
  202. /// Create a primitive for a convex shape using its support function
  203. using SupportFunction = function<Vec3 (Vec3Arg inDirection)>;
  204. Batch CreateTriangleBatchForConvex(SupportFunction inGetSupport, int inLevel, AABox *outBounds = nullptr);
  205. GeometryRef CreateTriangleGeometryForConvex(SupportFunction inGetSupport);
  206. /// Determines which polygons are culled
  207. enum class ECullMode
  208. {
  209. CullBackFace, ///< Don't draw backfacing polygons
  210. CullFrontFace, ///< Don't draw front facing polygons
  211. Off ///< Don't do culling and draw both sides
  212. };
  213. /// Draw some geometry
  214. /// @param inModelMatrix is the matrix that transforms the geometry to world space.
  215. /// @param inWorldSpaceBounds is the bounding box of the geometry after transforming it into world space.
  216. /// @param inLODScaleSq is the squared scale of the model matrix, it is multiplied with the LOD distances in inGeometry to calculate the real LOD distance (so a number > 1 will force a higher LOD).
  217. /// @param inModelColor is the color with which to multiply the vertex colors in inGeometry.
  218. /// @param inGeometry The geometry to draw.
  219. /// @param inCullMode determines which polygons are culled.
  220. /// @param inCastShadow determines if this geometry should cast a shadow or not.
  221. /// @param inDrawMode determines if we draw the geometry solid or in wireframe.
  222. virtual void DrawGeometry(RMat44Arg inModelMatrix, const AABox &inWorldSpaceBounds, float inLODScaleSq, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode = ECullMode::CullBackFace, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid) = 0;
  223. void DrawGeometry(RMat44Arg inModelMatrix, ColorArg inModelColor, const GeometryRef &inGeometry, ECullMode inCullMode = ECullMode::CullBackFace, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid) { DrawGeometry(inModelMatrix, inGeometry->mBounds.Transformed(inModelMatrix), max(max(inModelMatrix.GetAxisX().LengthSq(), inModelMatrix.GetAxisY().LengthSq()), inModelMatrix.GetAxisZ().LengthSq()), inModelColor, inGeometry, inCullMode, inCastShadow, inDrawMode); }
  224. /// Draw text
  225. virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor = Color::sWhite, float inHeight = 0.5f) = 0;
  226. protected:
  227. /// Initialize the system, must be called from the constructor of the DebugRenderer implementation
  228. void Initialize();
  229. private:
  230. /// Recursive helper function for DrawWireUnitSphere
  231. void DrawWireUnitSphereRecursive(RMat44Arg inMatrix, ColorArg inColor, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel);
  232. /// Helper functions to create a box
  233. void CreateQuad(Array<uint32> &ioIndices, Array<Vertex> &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4);
  234. /// Helper functions to create a vertex and index buffer for a sphere
  235. void Create8thSphereRecursive(Array<uint32> &ioIndices, Array<Vertex> &ioVertices, Vec3Arg inDir1, uint32 &ioIdx1, Vec3Arg inDir2, uint32 &ioIdx2, Vec3Arg inDir3, uint32 &ioIdx3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel);
  236. void Create8thSphere(Array<uint32> &ioIndices, Array<Vertex> &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel);
  237. /// Helper function for DrawSwingConeLimits and DrawSwingPyramidLimits
  238. Geometry * CreateSwingLimitGeometry(int inNumSegments, const Vec3 *inVertices);
  239. // Predefined shapes
  240. GeometryRef mBox;
  241. GeometryRef mSphere;
  242. GeometryRef mCapsuleTop;
  243. GeometryRef mCapsuleMid;
  244. GeometryRef mCapsuleBottom;
  245. GeometryRef mOpenCone;
  246. GeometryRef mCylinder;
  247. struct SwingConeLimits
  248. {
  249. bool operator == (const SwingConeLimits &inRHS) const
  250. {
  251. return mSwingYHalfAngle == inRHS.mSwingYHalfAngle
  252. && mSwingZHalfAngle == inRHS.mSwingZHalfAngle;
  253. }
  254. float mSwingYHalfAngle;
  255. float mSwingZHalfAngle;
  256. };
  257. JPH_MAKE_HASH_STRUCT(SwingConeLimits, SwingConeLimitsHasher, t.mSwingYHalfAngle, t.mSwingZHalfAngle)
  258. using SwingConeBatches = UnorderedMap<SwingConeLimits, GeometryRef, SwingConeLimitsHasher>;
  259. SwingConeBatches mSwingConeLimits;
  260. SwingConeBatches mPrevSwingConeLimits;
  261. struct SwingPyramidLimits
  262. {
  263. bool operator == (const SwingPyramidLimits &inRHS) const
  264. {
  265. return mMinSwingYAngle == inRHS.mMinSwingYAngle
  266. && mMaxSwingYAngle == inRHS.mMaxSwingYAngle
  267. && mMinSwingZAngle == inRHS.mMinSwingZAngle
  268. && mMaxSwingZAngle == inRHS.mMaxSwingZAngle;
  269. }
  270. float mMinSwingYAngle;
  271. float mMaxSwingYAngle;
  272. float mMinSwingZAngle;
  273. float mMaxSwingZAngle;
  274. };
  275. JPH_MAKE_HASH_STRUCT(SwingPyramidLimits, SwingPyramidLimitsHasher, t.mMinSwingYAngle, t.mMaxSwingYAngle, t.mMinSwingZAngle, t.mMaxSwingZAngle)
  276. using SwingPyramidBatches = UnorderedMap<SwingPyramidLimits, GeometryRef, SwingPyramidLimitsHasher>;
  277. SwingPyramidBatches mSwingPyramidLimits;
  278. SwingPyramidBatches mPrevSwingPyramidLimits;
  279. using PieBatces = UnorderedMap<float, GeometryRef>;
  280. PieBatces mPieLimits;
  281. PieBatces mPrevPieLimits;
  282. };
  283. JPH_NAMESPACE_END