ConvexHullBuilder.h 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. //#define JPH_CONVEX_BUILDER_DEBUG
  5. //#define JPH_CONVEX_BUILDER_DUMP_SHAPE
  6. #ifdef JPH_CONVEX_BUILDER_DEBUG
  7. #include <Core/Color.h>
  8. #endif
  9. #include <Core/StaticArray.h>
  10. #include <Core/NonCopyable.h>
  11. namespace JPH {
  12. /// A convex hull builder that tries to create hulls as accurately as possible. Used for offline processing.
  13. class ConvexHullBuilder : public NonCopyable
  14. {
  15. public:
  16. // Forward declare
  17. class Face;
  18. /// Class that holds the information of an edge
  19. class Edge : public NonCopyable
  20. {
  21. public:
  22. /// Constructor
  23. Edge(Face *inFace, int inStartIdx) : mFace(inFace), mStartIdx(inStartIdx) { }
  24. /// Get the previous edge
  25. inline Edge * GetPreviousEdge()
  26. {
  27. Edge *prev_edge = this;
  28. while (prev_edge->mNextEdge != this)
  29. prev_edge = prev_edge->mNextEdge;
  30. return prev_edge;
  31. }
  32. Face * mFace; ///< Face that this edge belongs to
  33. Edge * mNextEdge = nullptr; ///< Next edge of this face
  34. Edge * mNeighbourEdge = nullptr; ///< Edge that this edge is connected to
  35. int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge
  36. };
  37. using ConflictList = vector<int>;
  38. /// Class that holds the information of one face
  39. class Face : public NonCopyable
  40. {
  41. public:
  42. /// Destructor
  43. ~Face();
  44. /// Initialize a face with three indices
  45. void Initialize(int inIdx0, int inIdx1, int inIdx2, const Vec3 *inPositions);
  46. /// Calculates the centroid and normal for this face
  47. void CalculateNormalAndCentroid(const Vec3 *inPositions);
  48. /// Check if face inFace is facing inPosition
  49. inline bool IsFacing(Vec3Arg inPosition) const
  50. {
  51. JPH_ASSERT(!mRemoved);
  52. return mNormal.Dot(inPosition - mCentroid) > 0.0f;
  53. }
  54. Vec3 mNormal; ///< Normal of this face, length is 2 times area of face
  55. Vec3 mCentroid; ///< Center of the face
  56. ConflictList 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.
  57. Edge * mFirstEdge = nullptr; ///< First edge of this face
  58. float mFurthestPointDistanceSq = 0.0f; ///< Squared distance of furtest point from the conflict list to the face
  59. bool mRemoved = false; ///< Flag that indicates that face has been removed (face will be freed later)
  60. #ifdef JPH_CONVEX_BUILDER_DEBUG
  61. int mIteration; ///< Iteration that this face was created
  62. #endif
  63. };
  64. // Typedefs
  65. using Positions = vector<Vec3>;
  66. using Faces = vector<Face *>;
  67. /// Constructor
  68. explicit ConvexHullBuilder(const Positions &inPositions);
  69. /// Destructor
  70. ~ConvexHullBuilder() { FreeFaces(); }
  71. /// Result enum that indicates how the hull got created
  72. enum class EResult
  73. {
  74. Success, ///< Hull building finished successfully
  75. MaxVerticesReached, ///< Hull building finished successfully, but the desired accuracy was not reached because the max vertices limit was reached
  76. TooFewPoints, ///< Too few points to create a hull
  77. Degenerate, ///< Degenerate hull detected
  78. };
  79. /// Takes all positions as provided by the constructor and use them to build a hull
  80. /// Any points that are closer to the hull than inTolerance will be discarded
  81. /// @param inMaxVertices Max vertices to allow in the hull. Specify INT_MAX if there is no limit.
  82. /// @param inTolerance Max distance that a point is allowed to be outside of the hull
  83. /// @param outError Error message when building fails
  84. /// @return Status code that reports if the hull was created or not
  85. EResult Initialize(int inMaxVertices, float inTolerance, string &outError);
  86. /// Returns the amount of vertices that are currently used by the hull
  87. int GetNumVerticesUsed() const;
  88. /// Returns true if the hull contains a polygon with inIndices (counter clockwise indices in mPositions)
  89. bool ContainsFace(const vector<int> &inIndices);
  90. /// Calculate the center of mass and the volume of the current convex hull
  91. void GetCenterOfMassAndVolume(Vec3 &outCenterOfMass, float &outVolume) const;
  92. /// 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)
  93. /// @param outFaceWithMaxError The face that caused the error
  94. /// @param outMaxError The maximum distance of a point to the hull
  95. /// @param outMaxErrorPositionIdx The index of the point that had this distance
  96. /// @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.
  97. void DetermineMaxError(Face *&outFaceWithMaxError, float &outMaxError, int &outMaxErrorPositionIdx, float &outCoplanarDistance) const;
  98. /// Access to the created faces. Memory is owned by the convex hull builder.
  99. const Faces & GetFaces() const { return mFaces; }
  100. private:
  101. #ifdef JPH_CONVEX_BUILDER_DEBUG
  102. /// Factor to scale convex hull when debug drawing the construction process
  103. static constexpr float cDrawScale = 10.0f;
  104. #endif
  105. /// Class that holds an edge including start and end index
  106. class FullEdge
  107. {
  108. public:
  109. Edge * mNeighbourEdge; ///< Edge that this edge is connected to
  110. int mStartIdx; ///< Vertex index in mPositions that indicates the start vertex of this edge
  111. int mEndIdx; ///< Vertex index in mPosition that indicats the end vertex of this edge
  112. };
  113. // Private typedefs
  114. using FullEdges = vector<FullEdge>;
  115. // Determine a suitable tolerance for detecting that points are coplanar
  116. float DetermineCoplanarDistance() const;
  117. /// Assigns a position to one of the supplied faces based on which face is closest.
  118. /// @param inPositionIdx Index of the position to add
  119. /// @param inFaces List of faces to consider
  120. void AssignPointToFace(int inPositionIdx, const Faces &inFaces) const;
  121. /// Add a new point to the convex hull
  122. void AddPoint(Face *inFacingFace, int inIdx, float inToleranceSq, Faces &outNewFaces);
  123. /// Remove all faces that have been marked 'removed' from mFaces list
  124. void GarbageCollectFaces();
  125. /// Create a new face
  126. Face * CreateFace();
  127. /// Create a new triangle
  128. Face * CreateTriangle(int inIdx1, int inIdx2, int inIdx3);
  129. /// Delete a face (checking that it is not connected to any other faces)
  130. void FreeFace(Face *inFace);
  131. /// Release all faces and edges
  132. void FreeFaces();
  133. /// Link face edge to other face edge
  134. void LinkFace(Edge *inEdge1, Edge *inEdge2);
  135. /// Unlink this face from all of its neighbours
  136. void UnlinkFace(Face *inFace);
  137. /// Given one face that faces inVertex, find the edges of the faces that are not facing inVertex.
  138. /// Will flag all those faces for removal.
  139. void FindEdge(Face *inFacingFace, Vec3Arg inVertex, FullEdges &outEdges);
  140. /// Merges the two faces that share inEdge into the face inEdge->mFace
  141. void MergeFaces(Edge *inEdge);
  142. /// Merges inFace with a neighbour if it is degenerate (a sliver)
  143. void MergeDegenerateFace(Face *inFace, float inMinAreaSq, Faces &ioAffectedFaces);
  144. /// Merges any coplanar as well as neighbours that form a non-convex edge into inFace.
  145. /// Faces are considered coplanar if the distance^2 of the other face's centroid is smaller than inToleranceSq.
  146. void MergeCoplanarOrConcaveFaces(Face *inFace, float inToleranceSq, Faces &ioAffectedFaces);
  147. /// Mark face as affected if it is not already in the list
  148. static void sMarkAffected(Face *inFace, Faces &ioAffectedFaces);
  149. /// Removes all invalid edges.
  150. /// 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.
  151. /// 2. Removes edges that are interior to inFace (that have inFace on both sides)
  152. /// Any faces that need to be checked for validity will be added to ioAffectedFaces.
  153. void RemoveInvalidEdges(Face *inFace, Faces &ioAffectedFaces);
  154. /// Removes inFace if it consists of only 2 edges, linking its neighbouring faces together
  155. /// Any faces that need to be checked for validity will be added to ioAffectedFaces.
  156. /// @return True if face was removed.
  157. bool RemoveTwoEdgeFace(Face *inFace, Faces &ioAffectedFaces);
  158. #ifdef JPH_ENABLE_ASSERTS
  159. /// Dumps the text representation of a face to the TTY
  160. void DumpFace(const Face *inFace) const;
  161. /// Dumps the text representation of all faces to the TTY
  162. void DumpFaces() const;
  163. /// Check consistency of 1 face
  164. void ValidateFace(const Face *inFace) const;
  165. /// Check consistency of all faces
  166. void ValidateFaces() const;
  167. #endif
  168. #ifdef JPH_CONVEX_BUILDER_DEBUG
  169. /// Draw state of algorithm
  170. void DrawState(bool inDrawConflictList = false);
  171. /// Draw a face for debugging purposes
  172. void DrawWireFace(const Face *inFace, ColorArg inColor) const;
  173. /// Draw an edge for debugging purposes
  174. void DrawEdge(const Edge *inEdge, ColorArg inColor) const;
  175. #endif
  176. #ifdef JPH_CONVEX_BUILDER_DUMP_SHAPE
  177. void DumpShape() const;
  178. #endif
  179. const Positions & mPositions; ///< List of positions (some of them are part of the hull)
  180. Faces mFaces; ///< List of faces that are part of the hull (if !mRemoved)
  181. #ifdef JPH_CONVEX_BUILDER_DEBUG
  182. int mIteration; ///< Number of iterations we've had so far (for debug purposes)
  183. Vec3 mOffset; ///< Offset to use for state drawing
  184. Vec3 mDelta; ///< Delta offset between next states
  185. #endif
  186. };
  187. } // JPH