Path: blob/master/thirdparty/jolt_physics/Jolt/Geometry/ConvexHullBuilder.h
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56//#define JPH_CONVEX_BUILDER_DEBUG7//#define JPH_CONVEX_BUILDER_DUMP_SHAPE89#ifdef JPH_CONVEX_BUILDER_DEBUG10#include <Jolt/Core/Color.h>11#endif1213#include <Jolt/Core/StaticArray.h>14#include <Jolt/Core/NonCopyable.h>1516JPH_NAMESPACE_BEGIN1718/// A convex hull builder that tries to create hulls as accurately as possible. Used for offline processing.19class JPH_EXPORT ConvexHullBuilder : public NonCopyable20{21public:22// Forward declare23class Face;2425/// Class that holds the information of an edge26class Edge : public NonCopyable27{28public:29JPH_OVERRIDE_NEW_DELETE3031/// Constructor32Edge(Face *inFace, int inStartIdx) : mFace(inFace), mStartIdx(inStartIdx) { }3334/// Get the previous edge35inline Edge * GetPreviousEdge()36{37Edge *prev_edge = this;38while (prev_edge->mNextEdge != this)39prev_edge = prev_edge->mNextEdge;40return prev_edge;41}4243Face * mFace; ///< Face that this edge belongs to44Edge * mNextEdge = nullptr; ///< Next edge of this face45Edge * mNeighbourEdge = nullptr; ///< Edge that this edge is connected to46int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge47};4849using ConflictList = Array<int>;5051/// Class that holds the information of one face52class Face : public NonCopyable53{54public:55JPH_OVERRIDE_NEW_DELETE5657/// Destructor58~Face();5960/// Initialize a face with three indices61void Initialize(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions);6263/// Calculates the centroid and normal for this face64void CalculateNormalAndCentroid(const Vec3 *inPositions);6566/// Check if face inFace is facing inPosition67inline bool IsFacing(Vec3Arg inPosition) const68{69JPH_ASSERT(!mRemoved);70return mNormal.Dot(inPosition - mCentroid) > 0.0f;71}7273Vec3 mNormal; ///< Normal of this face, length is 2 times area of face74Vec3 mCentroid; ///< Center of the face75ConflictList mConflictList; ///< Positions associated with this edge (that are closest to this edge). The last position in the list is the point that is furthest away from the face.76Edge * mFirstEdge = nullptr; ///< First edge of this face77float mFurthestPointDistanceSq = 0.0f; ///< Squared distance of furthest point from the conflict list to the face78bool mRemoved = false; ///< Flag that indicates that face has been removed (face will be freed later)79#ifdef JPH_CONVEX_BUILDER_DEBUG80int mIteration; ///< Iteration that this face was created81#endif82};8384// Typedefs85using Positions = Array<Vec3>;86using Faces = Array<Face *>;8788/// Constructor89explicit ConvexHullBuilder(const Positions &inPositions);9091/// Destructor92~ConvexHullBuilder() { FreeFaces(); }9394/// Result enum that indicates how the hull got created95enum class EResult96{97Success, ///< Hull building finished successfully98MaxVerticesReached, ///< Hull building finished successfully, but the desired accuracy was not reached because the max vertices limit was reached99TooFewPoints, ///< Too few points to create a hull100TooFewFaces, ///< Too few faces in the created hull (signifies precision errors during building)101Degenerate, ///< Degenerate hull detected102};103104/// Takes all positions as provided by the constructor and use them to build a hull105/// Any points that are closer to the hull than inTolerance will be discarded106/// @param inMaxVertices Max vertices to allow in the hull. Specify INT_MAX if there is no limit.107/// @param inTolerance Max distance that a point is allowed to be outside of the hull108/// @param outError Error message when building fails109/// @return Status code that reports if the hull was created or not110EResult Initialize(int inMaxVertices, float inTolerance, const char *&outError);111112/// Returns the amount of vertices that are currently used by the hull113int GetNumVerticesUsed() const;114115/// Returns true if the hull contains a polygon with inIndices (counter clockwise indices in mPositions)116bool ContainsFace(const Array<int> &inIndices) const;117118/// Calculate the center of mass and the volume of the current convex hull119void GetCenterOfMassAndVolume(Vec3 &outCenterOfMass, float &outVolume) const;120121/// Determines the point that is furthest outside of the hull and reports how far it is outside of the hull (which indicates a failure during hull building)122/// @param outFaceWithMaxError The face that caused the error123/// @param outMaxError The maximum distance of a point to the hull124/// @param outMaxErrorPositionIdx The index of the point that had this distance125/// @param outCoplanarDistance Points that are less than this distance from the hull are considered on the hull. This should be used as a lowerbound for the allowed error.126void DetermineMaxError(Face *&outFaceWithMaxError, float &outMaxError, int &outMaxErrorPositionIdx, float &outCoplanarDistance) const;127128/// Access to the created faces. Memory is owned by the convex hull builder.129const Faces & GetFaces() const { return mFaces; }130131private:132/// Minimal square area of a triangle (used for merging and checking if a triangle is degenerate)133static constexpr float cMinTriangleAreaSq = 1.0e-12f;134135#ifdef JPH_CONVEX_BUILDER_DEBUG136/// Factor to scale convex hull when debug drawing the construction process137static constexpr Real cDrawScale = 10;138#endif139140/// Class that holds an edge including start and end index141class FullEdge142{143public:144Edge * mNeighbourEdge; ///< Edge that this edge is connected to145int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge146int mEndIdx; ///< Vertex index in mPosition that indicates the end vertex of this edge147};148149// Private typedefs150using FullEdges = Array<FullEdge>;151152// Determine a suitable tolerance for detecting that points are coplanar153float DetermineCoplanarDistance() const;154155/// Find the face for which inPoint is furthest to the front156/// @param inPoint Point to test157/// @param inFaces List of faces to test158/// @param outFace Returns the best face159/// @param outDistSq Returns the squared distance how much inPoint is in front of the plane of the face160void GetFaceForPoint(Vec3Arg inPoint, const Faces &inFaces, Face *&outFace, float &outDistSq) const;161162/// @brief Calculates the distance between inPoint and inFace163/// @param inFace Face to test164/// @param inPoint Point to test165/// @return If the projection of the point on the plane is interior to the face 0, otherwise the squared distance to the closest edge166float GetDistanceToEdgeSq(Vec3Arg inPoint, const Face *inFace) const;167168/// Assigns a position to one of the supplied faces based on which face is closest.169/// @param inPositionIdx Index of the position to add170/// @param inFaces List of faces to consider171/// @param inToleranceSq Tolerance of the hull, if the point is closer to the face than this, we ignore it172/// @return True if point was assigned, false if it was discarded or added to the coplanar list173bool AssignPointToFace(int inPositionIdx, const Faces &inFaces, float inToleranceSq);174175/// Add a new point to the convex hull176void AddPoint(Face *inFacingFace, int inIdx, float inToleranceSq, Faces &outNewFaces);177178/// Remove all faces that have been marked 'removed' from mFaces list179void GarbageCollectFaces();180181/// Create a new face182Face * CreateFace();183184/// Create a new triangle185Face * CreateTriangle(int inIdx1, int inIdx2, int inIdx3);186187/// Delete a face (checking that it is not connected to any other faces)188void FreeFace(Face *inFace);189190/// Release all faces and edges191void FreeFaces();192193/// Link face edge to other face edge194static void sLinkFace(Edge *inEdge1, Edge *inEdge2);195196/// Unlink this face from all of its neighbours197static void sUnlinkFace(Face *inFace);198199/// Given one face that faces inVertex, find the edges of the faces that are not facing inVertex.200/// Will flag all those faces for removal.201void FindEdge(Face *inFacingFace, Vec3Arg inVertex, FullEdges &outEdges) const;202203/// Merges the two faces that share inEdge into the face inEdge->mFace204void MergeFaces(Edge *inEdge);205206/// Merges inFace with a neighbour if it is degenerate (a sliver)207void MergeDegenerateFace(Face *inFace, Faces &ioAffectedFaces);208209/// Merges any coplanar as well as neighbours that form a non-convex edge into inFace.210/// Faces are considered coplanar if the distance^2 of the other face's centroid is smaller than inToleranceSq.211void MergeCoplanarOrConcaveFaces(Face *inFace, float inToleranceSq, Faces &ioAffectedFaces);212213/// Mark face as affected if it is not already in the list214static void sMarkAffected(Face *inFace, Faces &ioAffectedFaces);215216/// Removes all invalid edges.217/// 1. Merges inFace with faces that share two edges with it since this means inFace or the other face cannot be convex or the edge is colinear.218/// 2. Removes edges that are interior to inFace (that have inFace on both sides)219/// Any faces that need to be checked for validity will be added to ioAffectedFaces.220void RemoveInvalidEdges(Face *inFace, Faces &ioAffectedFaces);221222/// Removes inFace if it consists of only 2 edges, linking its neighbouring faces together223/// Any faces that need to be checked for validity will be added to ioAffectedFaces.224/// @return True if face was removed.225bool RemoveTwoEdgeFace(Face *inFace, Faces &ioAffectedFaces) const;226227#ifdef JPH_ENABLE_ASSERTS228/// Dumps the text representation of a face to the TTY229void DumpFace(const Face *inFace) const;230231/// Dumps the text representation of all faces to the TTY232void DumpFaces() const;233234/// Check consistency of 1 face235void ValidateFace(const Face *inFace) const;236237/// Check consistency of all faces238void ValidateFaces() const;239#endif240241#ifdef JPH_CONVEX_BUILDER_DEBUG242/// Draw state of algorithm243void DrawState(bool inDrawConflictList = false) const;244245/// Draw a face for debugging purposes246void DrawWireFace(const Face *inFace, ColorArg inColor) const;247248/// Draw an edge for debugging purposes249void DrawEdge(const Edge *inEdge, ColorArg inColor) const;250#endif251252#ifdef JPH_CONVEX_BUILDER_DUMP_SHAPE253void DumpShape() const;254#endif255256const Positions & mPositions; ///< List of positions (some of them are part of the hull)257Faces mFaces; ///< List of faces that are part of the hull (if !mRemoved)258259struct Coplanar260{261int mPositionIdx; ///< Index in mPositions262float mDistanceSq; ///< Distance to the edge of closest face (should be > 0)263};264using CoplanarList = Array<Coplanar>;265266CoplanarList mCoplanarList; ///< List of positions that are coplanar to a face but outside of the face, these are added to the hull at the end267268#ifdef JPH_CONVEX_BUILDER_DEBUG269int mIteration; ///< Number of iterations we've had so far (for debug purposes)270mutable RVec3 mOffset; ///< Offset to use for state drawing271Vec3 mDelta; ///< Delta offset between next states272#endif273};274275JPH_NAMESPACE_END276277278