Path: blob/master/thirdparty/jolt_physics/Jolt/Renderer/DebugRenderer.h
9906 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#ifndef JPH_DEBUG_RENDERER7#error This file should only be included when JPH_DEBUG_RENDERER is defined8#endif // !JPH_DEBUG_RENDERER910#ifndef JPH_DEBUG_RENDERER_EXPORT11// By default export the debug renderer12#define JPH_DEBUG_RENDERER_EXPORT JPH_EXPORT13#endif // !JPH_DEBUG_RENDERER_EXPORT1415#include <Jolt/Core/Color.h>16#include <Jolt/Core/Reference.h>17#include <Jolt/Core/HashCombine.h>18#include <Jolt/Core/UnorderedMap.h>19#include <Jolt/Core/NonCopyable.h>20#include <Jolt/Math/Float2.h>21#include <Jolt/Geometry/IndexedTriangle.h>22#include <Jolt/Geometry/AABox.h>2324JPH_NAMESPACE_BEGIN2526class OrientedBox;2728/// Simple triangle renderer for debugging purposes.29///30/// Inherit from this class to provide your own implementation.31///32/// Implement the following virtual functions:33/// - DrawLine34/// - DrawTriangle35/// - DrawText3D36/// - CreateTriangleBatch37/// - DrawGeometry38///39/// Make sure you call Initialize() from the constructor of your implementation.40///41/// The CreateTriangleBatch is used to prepare a batch of triangles to be drawn by a single DrawGeometry call,42/// 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.43///44/// Note that an implementation that implements CreateTriangleBatch and DrawGeometry is provided by DebugRendererSimple which can be used to start quickly.45class JPH_DEBUG_RENDERER_EXPORT DebugRenderer : public NonCopyable46{47public:48JPH_OVERRIDE_NEW_DELETE4950/// Constructor51DebugRenderer();52virtual ~DebugRenderer();5354/// Call once after frame is complete. Releases unused dynamically generated geometry assets.55void NextFrame();5657/// Draw line58virtual void DrawLine(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor) = 0;5960/// Draw wireframe box61void DrawWireBox(const AABox &inBox, ColorArg inColor);62void DrawWireBox(const OrientedBox &inBox, ColorArg inColor);63void DrawWireBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor);6465/// Draw a marker on a position66void DrawMarker(RVec3Arg inPosition, ColorArg inColor, float inSize);6768/// Draw an arrow69void DrawArrow(RVec3Arg inFrom, RVec3Arg inTo, ColorArg inColor, float inSize);7071/// Draw coordinate system (3 arrows, x = red, y = green, z = blue)72void DrawCoordinateSystem(RMat44Arg inTransform, float inSize = 1.0f);7374/// Draw a plane through inPoint with normal inNormal75void DrawPlane(RVec3Arg inPoint, Vec3Arg inNormal, ColorArg inColor, float inSize);7677/// Draw wireframe triangle78void DrawWireTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor);7980/// Draw a wireframe polygon81template <class VERTEX_ARRAY>82void 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); }8384/// Draw wireframe sphere85void DrawWireSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, int inLevel = 3);86void DrawWireUnitSphere(RMat44Arg inMatrix, ColorArg inColor, int inLevel = 3);8788/// Enum that determines if a shadow should be cast or not89enum class ECastShadow90{91On, ///< This shape should cast a shadow92Off ///< This shape should not cast a shadow93};9495/// Determines how triangles are drawn96enum class EDrawMode97{98Solid, ///< Draw as a solid shape99Wireframe, ///< Draw as wireframe100};101102/// Draw a single back face culled triangle103virtual void DrawTriangle(RVec3Arg inV1, RVec3Arg inV2, RVec3Arg inV3, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::Off) = 0;104105/// Draw a box106void DrawBox(const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);107void DrawBox(RMat44Arg inMatrix, const AABox &inBox, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);108109/// Draw a sphere110void DrawSphere(RVec3Arg inCenter, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);111void DrawUnitSphere(RMat44Arg inMatrix, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);112113/// Draw a capsule with one half sphere at (0, -inHalfHeightOfCylinder, 0) and the other half sphere at (0, inHalfHeightOfCylinder, 0) and radius inRadius.114/// The capsule will be transformed by inMatrix.115void DrawCapsule(RMat44Arg inMatrix, float inHalfHeightOfCylinder, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);116117/// Draw a cylinder with top (0, inHalfHeight, 0) and bottom (0, -inHalfHeight, 0) and radius inRadius.118/// The cylinder will be transformed by inMatrix119void DrawCylinder(RMat44Arg inMatrix, float inHalfHeight, float inRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);120121/// Draw a bottomless cone.122/// @param inTop Top of cone, center of base is at inTop + inAxis.123/// @param inAxis Height and direction of cone124/// @param inPerpendicular Perpendicular vector to inAxis.125/// @param inHalfAngle Specifies the cone angle in radians (angle measured between inAxis and cone surface).126/// @param inLength The length of the cone.127/// @param inColor Color to use for drawing the cone.128/// @param inCastShadow determines if this geometry should cast a shadow or not.129/// @param inDrawMode determines if we draw the geometry solid or in wireframe.130void DrawOpenCone(RVec3Arg inTop, Vec3Arg inAxis, Vec3Arg inPerpendicular, float inHalfAngle, float inLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);131132/// Draws cone rotation limits as used by the SwingTwistConstraintPart.133/// @param inMatrix Matrix that transforms from constraint space to world space134/// @param inSwingYHalfAngle See SwingTwistConstraintPart135/// @param inSwingZHalfAngle See SwingTwistConstraintPart136/// @param inEdgeLength Size of the edge of the cone shape137/// @param inColor Color to use for drawing the cone.138/// @param inCastShadow determines if this geometry should cast a shadow or not.139/// @param inDrawMode determines if we draw the geometry solid or in wireframe.140void DrawSwingConeLimits(RMat44Arg inMatrix, float inSwingYHalfAngle, float inSwingZHalfAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);141142/// Draws rotation limits as used by the SwingTwistConstraintPart.143/// @param inMatrix Matrix that transforms from constraint space to world space144/// @param inMinSwingYAngle See SwingTwistConstraintPart145/// @param inMaxSwingYAngle See SwingTwistConstraintPart146/// @param inMinSwingZAngle See SwingTwistConstraintPart147/// @param inMaxSwingZAngle See SwingTwistConstraintPart148/// @param inEdgeLength Size of the edge of the cone shape149/// @param inColor Color to use for drawing the cone.150/// @param inCastShadow determines if this geometry should cast a shadow or not.151/// @param inDrawMode determines if we draw the geometry solid or in wireframe.152void DrawSwingPyramidLimits(RMat44Arg inMatrix, float inMinSwingYAngle, float inMaxSwingYAngle, float inMinSwingZAngle, float inMaxSwingZAngle, float inEdgeLength, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);153154/// Draw a pie (part of a circle).155/// @param inCenter The center of the circle.156/// @param inRadius Radius of the circle.157/// @param inNormal The plane normal in which the pie resides.158/// @param inAxis The axis that defines an angle of 0 radians.159/// @param inMinAngle The pie will be drawn between [inMinAngle, inMaxAngle] (in radians).160/// @param inMaxAngle The pie will be drawn between [inMinAngle, inMaxAngle] (in radians).161/// @param inColor Color to use for drawing the pie.162/// @param inCastShadow determines if this geometry should cast a shadow or not.163/// @param inDrawMode determines if we draw the geometry solid or in wireframe.164void DrawPie(RVec3Arg inCenter, float inRadius, Vec3Arg inNormal, Vec3Arg inAxis, float inMinAngle, float inMaxAngle, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);165166/// Draw a tapered cylinder167/// @param inMatrix Matrix that transforms the cylinder to world space.168/// @param inTop Top of cylinder (along Y axis)169/// @param inBottom Bottom of cylinder (along Y axis)170/// @param inTopRadius Radius at the top171/// @param inBottomRadius Radius at the bottom172/// @param inColor Color to use for drawing the pie.173/// @param inCastShadow determines if this geometry should cast a shadow or not.174/// @param inDrawMode determines if we draw the geometry solid or in wireframe.175void DrawTaperedCylinder(RMat44Arg inMatrix, float inTop, float inBottom, float inTopRadius, float inBottomRadius, ColorArg inColor, ECastShadow inCastShadow = ECastShadow::On, EDrawMode inDrawMode = EDrawMode::Solid);176177/// Singleton instance178static DebugRenderer * sInstance;179180/// Vertex format used by the triangle renderer181class Vertex182{183public:184Float3 mPosition;185Float3 mNormal;186Float2 mUV;187Color mColor;188};189190/// A single triangle191class JPH_DEBUG_RENDERER_EXPORT Triangle192{193public:194Triangle() = default;195Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor);196Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor, Vec3Arg inUVOrigin, Vec3Arg inUVDirection);197198Vertex mV[3];199};200201/// Handle for a batch of triangles202using Batch = Ref<RefTargetVirtual>;203204/// A single level of detail205class LOD206{207public:208Batch mTriangleBatch;209float mDistance;210};211212/// A geometry primitive containing triangle batches for various lods213class Geometry : public RefTarget<Geometry>214{215public:216JPH_OVERRIDE_NEW_DELETE217218/// Constructor219Geometry(const AABox &inBounds) : mBounds(inBounds) { }220Geometry(const Batch &inBatch, const AABox &inBounds) : mBounds(inBounds) { mLODs.push_back({ inBatch, cLargeFloat }); }221222/// Determine which LOD to render223/// @param inCameraPosition Current position of the camera224/// @param inWorldSpaceBounds World space bounds for this geometry (transform mBounds by model space matrix)225/// @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).226/// @return The selected LOD.227const LOD & GetLOD(Vec3Arg inCameraPosition, const AABox &inWorldSpaceBounds, float inLODScaleSq) const228{229float dist_sq = inWorldSpaceBounds.GetSqDistanceTo(inCameraPosition);230for (const LOD &lod : mLODs)231if (dist_sq <= inLODScaleSq * Square(lod.mDistance))232return lod;233234return mLODs.back();235}236237/// All level of details for this mesh238Array<LOD> mLODs;239240/// Bounding box that encapsulates all LODs241AABox mBounds;242};243244/// Handle for a lodded triangle batch245using GeometryRef = Ref<Geometry>;246247/// Calculate bounding box for a batch of triangles248static AABox sCalculateBounds(const Vertex *inVertices, int inVertexCount);249250/// Create a batch of triangles that can be drawn efficiently251virtual Batch CreateTriangleBatch(const Triangle *inTriangles, int inTriangleCount) = 0;252virtual Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, const uint32 *inIndices, int inIndexCount) = 0;253Batch CreateTriangleBatch(const Array<Triangle> &inTriangles) { return CreateTriangleBatch(inTriangles.empty()? nullptr : &inTriangles[0], int(inTriangles.size())); }254Batch 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())); }255Batch CreateTriangleBatch(const VertexList &inVertices, const IndexedTriangleNoMaterialList &inTriangles);256257/// Create a primitive for a convex shape using its support function258using SupportFunction = function<Vec3 (Vec3Arg inDirection)>;259Batch CreateTriangleBatchForConvex(SupportFunction inGetSupport, int inLevel, AABox *outBounds = nullptr);260GeometryRef CreateTriangleGeometryForConvex(SupportFunction inGetSupport);261262/// Determines which polygons are culled263enum class ECullMode264{265CullBackFace, ///< Don't draw backfacing polygons266CullFrontFace, ///< Don't draw front facing polygons267Off ///< Don't do culling and draw both sides268};269270/// Draw some geometry271/// @param inModelMatrix is the matrix that transforms the geometry to world space.272/// @param inWorldSpaceBounds is the bounding box of the geometry after transforming it into world space.273/// @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).274/// @param inModelColor is the color with which to multiply the vertex colors in inGeometry.275/// @param inGeometry The geometry to draw.276/// @param inCullMode determines which polygons are culled.277/// @param inCastShadow determines if this geometry should cast a shadow or not.278/// @param inDrawMode determines if we draw the geometry solid or in wireframe.279virtual 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;280void 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); }281282/// Draw text283virtual void DrawText3D(RVec3Arg inPosition, const string_view &inString, ColorArg inColor = Color::sWhite, float inHeight = 0.5f) = 0;284285protected:286/// Initialize the system, must be called from the constructor of the DebugRenderer implementation287void Initialize();288289private:290/// Recursive helper function for DrawWireUnitSphere291void DrawWireUnitSphereRecursive(RMat44Arg inMatrix, ColorArg inColor, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, int inLevel);292293/// Helper functions to create a box294void CreateQuad(Array<uint32> &ioIndices, Array<Vertex> &ioVertices, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4);295296/// Helper functions to create a vertex and index buffer for a sphere297void 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);298void Create8thSphere(Array<uint32> &ioIndices, Array<Vertex> &ioVertices, Vec3Arg inDir1, Vec3Arg inDir2, Vec3Arg inDir3, const Float2 &inUV, SupportFunction inGetSupport, int inLevel);299300/// Helper functions to create a vertex and index buffer for a cylinder301Batch CreateCylinder(float inTop, float inBottom, float inTopRadius, float inBottomRadius, int inLevel);302303/// Helper function for DrawSwingConeLimits and DrawSwingPyramidLimits304Geometry * CreateSwingLimitGeometry(int inNumSegments, const Vec3 *inVertices);305306// Predefined shapes307GeometryRef mBox;308GeometryRef mSphere;309GeometryRef mCapsuleTop;310GeometryRef mCapsuleMid;311GeometryRef mCapsuleBottom;312GeometryRef mOpenCone;313GeometryRef mCylinder;314315struct SwingConeLimits316{317bool operator == (const SwingConeLimits &inRHS) const318{319return mSwingYHalfAngle == inRHS.mSwingYHalfAngle320&& mSwingZHalfAngle == inRHS.mSwingZHalfAngle;321}322323float mSwingYHalfAngle;324float mSwingZHalfAngle;325};326327JPH_MAKE_HASH_STRUCT(SwingConeLimits, SwingConeLimitsHasher, t.mSwingYHalfAngle, t.mSwingZHalfAngle)328329using SwingConeBatches = UnorderedMap<SwingConeLimits, GeometryRef, SwingConeLimitsHasher>;330SwingConeBatches mSwingConeLimits;331SwingConeBatches mPrevSwingConeLimits;332333struct SwingPyramidLimits334{335bool operator == (const SwingPyramidLimits &inRHS) const336{337return mMinSwingYAngle == inRHS.mMinSwingYAngle338&& mMaxSwingYAngle == inRHS.mMaxSwingYAngle339&& mMinSwingZAngle == inRHS.mMinSwingZAngle340&& mMaxSwingZAngle == inRHS.mMaxSwingZAngle;341}342343float mMinSwingYAngle;344float mMaxSwingYAngle;345float mMinSwingZAngle;346float mMaxSwingZAngle;347};348349JPH_MAKE_HASH_STRUCT(SwingPyramidLimits, SwingPyramidLimitsHasher, t.mMinSwingYAngle, t.mMaxSwingYAngle, t.mMinSwingZAngle, t.mMaxSwingZAngle)350351using SwingPyramidBatches = UnorderedMap<SwingPyramidLimits, GeometryRef, SwingPyramidLimitsHasher>;352SwingPyramidBatches mSwingPyramidLimits;353SwingPyramidBatches mPrevSwingPyramidLimits;354355using PieBatces = UnorderedMap<float, GeometryRef>;356PieBatces mPieLimits;357PieBatces mPrevPieLimits;358359struct TaperedCylinder360{361bool operator == (const TaperedCylinder &inRHS) const362{363return mTop == inRHS.mTop364&& mBottom == inRHS.mBottom365&& mTopRadius == inRHS.mTopRadius366&& mBottomRadius == inRHS.mBottomRadius;367}368369float mTop;370float mBottom;371float mTopRadius;372float mBottomRadius;373};374375JPH_MAKE_HASH_STRUCT(TaperedCylinder, TaperedCylinderHasher, t.mTop, t.mBottom, t.mTopRadius, t.mBottomRadius)376377using TaperedCylinderBatces = UnorderedMap<TaperedCylinder, GeometryRef, TaperedCylinderHasher>;378TaperedCylinderBatces mTaperedCylinders;379TaperedCylinderBatces mPrevTaperedCylinders;380};381382JPH_NAMESPACE_END383384385