35#include <unordered_set>
41#if defined(JPL_ENABLE_VALIDATION)
55 template<CVec3 Vec3Type>
58 using Vec3 = Vec3Type;
61 using Array = std::pmr::vector<T>;
73 Edge& operator =(
const Edge&) =
delete;
97 Face& operator=(
const Face&) =
delete;
103 void Initialize(
int inIdx0,
int inIdx1,
int inIdx2, std::span<const Vec3> inPositions);
108 template<
class Vec3i>
109 bool Triangulate(std::span<const Vec3> inPositions, Array<Vec3i>& outTris)
const;
112 bool IsFacing(
const Vec3& inPosition)
const;
120#ifdef JPL_CONVEX_BUILDER_DEBUG
125 Edge* AllocateEdge(
int inStartIdx);
126 void FreeEdge(
Edge* edge);
166 void DetermineMaxError(Face*& outFaceWithMaxError,
float& outMaxError,
int& outMaxErrorPositionIdx,
float& outCoplanarDistance)
const;
174 template<
class Vec3i>
179 static constexpr float cMinTriangleAreaSq = 1.0e-12f;
181#ifdef JPL_CONVEX_BUILDER_DEBUG
183 static constexpr Real cDrawScale = 10;
190 Edge* mNeighbourEdge;
196 using FullEdges = Array<FullEdge>;
199 float DetermineCoplanarDistance()
const;
206 void GetFaceForPoint(
const Vec3& inPoint,
const Faces& inFaces, Face*& outFace,
float& outDistSq)
const;
212 float GetDistanceToEdgeSq(
const Vec3& inPoint,
const Face* inFace)
const;
219 bool AssignPointToFace(
int inPositionIdx,
const Faces& inFaces,
float inToleranceSq);
222 void AddPoint(Face* inFacingFace,
int inIdx,
float inCoplanarToleranceSq,
Faces& outNewFaces);
225 void GarbageCollectFaces();
231 Face* CreateTriangle(
int inIdx1,
int inIdx2,
int inIdx3);
234 void FreeFace(Face* inFace);
239 static void sLinkFace(Edge* inEdge1, Edge* inEdge2);
242 static void sUnlinkFace(Face* inFace);
246 void FindEdge(Face* inFacingFace,
const Vec3& inVertex, FullEdges& outEdges)
const;
249 void MergeFaces(Edge* inEdge);
252 void MergeDegenerateFace(Face* inFace,
Faces& ioAffectedFaces);
256 void MergeCoplanarOrConcaveFaces(Face* inFace,
float inCoplanarToleranceSq,
Faces& ioAffectedFaces);
259 static void sMarkAffected(Face* inFace,
Faces& ioAffectedFaces);
265 void RemoveInvalidEdges(Face* inFace,
Faces& ioAffectedFaces);
270 bool RemoveTwoEdgeFace(Face* inFace,
Faces& ioAffectedFaces)
const;
272#ifdef JPL_ENABLE_VALIDATION
274 void DumpFace(
const Face* inFace)
const;
277 void ValidateFace(
const Face* inFace)
const;
280 void ValidateFaces()
const;
283#ifdef JPL_CONVEX_BUILDER_DEBUG
285 void DrawState(
bool inDrawConflictList =
false)
const;
288 void DrawWireFace(
const Face* inFace, ColorArg inColor)
const;
291 void DrawEdge(
const Edge* inEdge, ColorArg inColor)
const;
294#ifdef JPL_CONVEX_BUILDER_DUMP_SHAPE
295 void DumpShape()
const;
306 using CoplanarList = Array<Coplanar>;
308 CoplanarList mCoplanarList;
310#ifdef JPL_CONVEX_BUILDER_DEBUG
312 mutable RVec3 mOffset;
326 template<CVec3 Vec3Type>
329 Edge* prev_edge =
this;
335 template<CVec3 Vec3Type>
339 if (
Edge* e = mFirstEdge)
346 }
while (e != mFirstEdge);
351 template<CVec3 Vec3Type>
355 JPL_ASSERT(inIdx0 != inIdx1 && inIdx0 != inIdx2 && inIdx1 != inIdx2);
358 auto* e0 = AllocateEdge(inIdx0);
359 auto* e1 = AllocateEdge(inIdx1);
360 auto* e2 = AllocateEdge(inIdx2);
368 CalculateNormalAndCentroid(inPositions);
371 template<CVec3 Vec3Type>
377 template<CVec3 Vec3Type>
383 template<CVec3 Vec3Type>
387 Edge* e = mFirstEdge;
399 mNormal = Vec3{ 0.0f, 0.0f, 0.0f };
416 mNormal += LengthSquared(e1) < LengthSquared(e2) ? CrossProduct(e0, e1) : CrossProduct(e2, e0);
427 mCentroid /= float(n);
430 template<CVec3 Vec3Type>
431 template<
class Vec3i>
434 using IndexType = std::remove_cvref_t<decltype(std::declval<Vec3i>()[0])>;
440 poly.push_back(mFirstEdge->mStartIdx);
441 for (
Edge* e = mFirstEdge->
mNextEdge; e != mFirstEdge; e = e->mNextEdge)
442 poly.push_back(e->mStartIdx);
447 if (poly.size() == 3)
450 outTris.push_back(Vec3i{
451 static_cast<IndexType
>(poly[0]),
452 static_cast<IndexType
>(poly[1]),
453 static_cast<IndexType
>(poly[2])
465 template<CVec3 Vec3Type>
469 return DotProduct(mNormal, (inPosition - mCentroid)) > 0.0f;
473 template<CVec3 Vec3Type>
475 : mPositions(inPositions)
479#ifdef JPL_CONVEX_BUILDER_DEBUG
483 mOffset = RVec3::sZero();
484 if (mPositions.empty())
487 mDelta = Vec3::sZero();
491 Vec3 maxv = Vec3::sReplicate(-std::numeric_limits<float>::max()), minv = Vec3::sReplicate(std::numeric_limits<float>::max());
492 for (Vec3 v : mPositions)
494 minv = Vec3::sMin(minv, v);
495 maxv = Vec3::sMax(maxv, v);
498 mOffset /= Real(mPositions.size());
499 mDelta = Vec3((maxv - minv).
GetX() + 0.5f, 0, 0);
505 template<CVec3 Vec3Type>
511 template<CVec3 Vec3Type>
518 if (mPositions.size() < 3)
520 outError =
"Need at least 3 points to make a hull";
521 return EResult::TooFewPoints;
525 float coplanar_tolerance_sq = DetermineCoplanarDistance();
526 coplanar_tolerance_sq *= coplanar_tolerance_sq;
529 const auto sqrTolerance = inTolerance * inTolerance;
530 float tolerance_sq = std::max(coplanar_tolerance_sq, sqrTolerance);
534 float max_dist_sq = -1.0f;
535 for (
int i = 0; i < (int)mPositions.size(); ++i)
537 float dist_sq = LengthSquared(mPositions[i]);
538 if (dist_sq > max_dist_sq)
540 max_dist_sq = dist_sq;
549 for (
int i = 0; i < (int)mPositions.size(); ++i)
553 float dist_sq = LengthSquared(mPositions[i] - mPositions[idx1]);
554 if (dist_sq > max_dist_sq)
556 max_dist_sq = dist_sq;
566 float best_triangle_area_sq = -1.0f;
567 for (
int i = 0; i < (int)mPositions.size(); ++i)
569 if (i != idx1 && i != idx2)
571 float triangle_area_sq = LengthSquared(CrossProduct(mPositions[idx1] - mPositions[i], mPositions[idx2] - mPositions[i]));
572 if (triangle_area_sq > best_triangle_area_sq)
574 best_triangle_area_sq = triangle_area_sq;
581 if (best_triangle_area_sq < cMinTriangleAreaSq)
583 outError =
"Could not find a suitable initial triangle because its area was too small";
584 return EResult::Degenerate;
588 if (mPositions.size() == 3)
592 Face* t1 = CreateTriangle(idx1, idx2, idx3);
593 Face* t2 = CreateTriangle(idx1, idx3, idx2);
601#ifdef JPL_CONVEX_BUILDER_DEBUG
606 return EResult::Success;
610 Vec3 initial_plane_normal = Normalized(CrossProduct(mPositions[idx2] - mPositions[idx1], mPositions[idx3] - mPositions[idx1]));
611 Vec3 initial_plane_centroid = (mPositions[idx1] + mPositions[idx2] + mPositions[idx3]) / 3.0f;
613 float max_dist = 0.0f;
614 for (
int i = 0; i < (int)mPositions.size(); ++i)
616 if (i != idx1 && i != idx2 && i != idx3)
618 float dist = DotProduct(mPositions[i] - initial_plane_centroid, initial_plane_normal);
619 if (std::abs(dist) > std::abs(max_dist))
628 if ((max_dist * max_dist) <= 25.0f * coplanar_tolerance_sq)
634 return EResult::Degenerate;
637 Vec3 base1 = GetNormalizedPerpendicular(initial_plane_normal);
638 Vec3 base2 = CrossProduct(initial_plane_normal, base1);
639 Array<Vec3> positions_2d;
640 positions_2d.reserve(mPositions.size());
641 for (Vec3 v : mPositions)
642 positions_2d.emplace_back(DotProduct(base1, v), DotProduct(base2, v), 0.0f);
646 ConvexHullBuilder2D builder_2d(positions_2d);
647 ConvexHullBuilder2D::EResult result = builder_2d.Initialize(idx1, idx2, idx3, inMaxVertices, inTolerance, edges_2d);
650 Face* f1 = CreateFace();
651 Face* f2 = CreateFace();
654 Array<Edge*> edges_f1;
655 edges_f1.reserve(edges_2d.size());
656 for (
int start_idx : edges_2d)
658 Edge* edge =
new Edge(f1, start_idx);
659 if (edges_f1.empty())
663 edges_f1.push_back(edge);
668 Array<Edge*> edges_f2;
669 edges_f2.reserve(edges_2d.size());
670 for (
int i = (
int)edges_2d.size() - 1; i >= 0; --i)
672 Edge* edge =
new Edge(f2, edges_2d[i]);
673 if (edges_f2.empty())
677 edges_f2.push_back(edge);
682 for (
size_t i = 0; i < edges_2d.size(); ++i)
683 sLinkFace(edges_f1[i], edges_f2[(2 * edges_2d.size() - 2 - i) % edges_2d.size()]);
690#ifdef JPL_CONVEX_BUILDER_DEBUG
695 return result == ConvexHullBuilder2D::EResult::MaxVerticesReached ? EResult::MaxVerticesReached : EResult::Success;
701 std::swap(idx2, idx3);
704 Face* t1 = CreateTriangle(idx1, idx2, idx4);
705 Face* t2 = CreateTriangle(idx2, idx3, idx4);
706 Face* t3 = CreateTriangle(idx3, idx1, idx4);
707 Face* t4 = CreateTriangle(idx1, idx3, idx2);
719 for (
int idx = 0; idx < (int)mPositions.size(); ++idx)
720 if (idx != idx1 && idx != idx2 && idx != idx3 && idx != idx4)
721 AssignPointToFace(idx, faces, tolerance_sq);
723#ifdef JPL_CONVEX_BUILDER_DEBUG
732 int num_vertices_used = 4;
738 Face* face_with_furthest_point =
nullptr;
739 float furthest_dist_sq = 0.0f;
740 for (
Face* f : mFaces)
742 if (f->mFurthestPointDistanceSq > furthest_dist_sq)
744 furthest_dist_sq = f->mFurthestPointDistanceSq;
745 face_with_furthest_point = f;
749 int furthest_point_idx;
750 if (face_with_furthest_point !=
nullptr)
753 furthest_point_idx = face_with_furthest_point->
mConflictList.back();
756 else if (!mCoplanarList.empty())
760 mCoplanarList.swap(coplanar);
762 for (
const Coplanar& c : coplanar)
763 added |= AssignPointToFace(c.mPositionIdx, mFaces, tolerance_sq);
770 if (mCoplanarList.empty())
776 typename CoplanarList::size_type best_idx = 0;
777 float best_dist_sq = mCoplanarList.front().mDistanceSq;
778 for (
typename CoplanarList::size_type idx = 1; idx < mCoplanarList.size(); ++idx)
780 const Coplanar& c = mCoplanarList[idx];
781 if (c.mDistanceSq > best_dist_sq)
784 best_dist_sq = c.mDistanceSq;
789 std::swap(mCoplanarList[best_idx], mCoplanarList.back());
792 furthest_point_idx = mCoplanarList.back().mPositionIdx;
793 mCoplanarList.pop_back();
796 GetFaceForPoint(mPositions[furthest_point_idx], mFaces, face_with_furthest_point, best_dist_sq);
797 }
while (!mCoplanarList.empty() && face_with_furthest_point ==
nullptr);
799 if (face_with_furthest_point ==
nullptr)
809 if (num_vertices_used >= inMaxVertices)
812 num_vertices_used = GetNumVerticesUsed();
815 if (num_vertices_used >= inMaxVertices)
816 return EResult::MaxVerticesReached;
824 AddPoint(face_with_furthest_point, furthest_point_idx, coplanar_tolerance_sq, new_faces);
827 for (
const Face* face : mFaces)
829 for (
int idx : face->mConflictList)
830 AssignPointToFace(idx, new_faces, tolerance_sq);
833 GarbageCollectFaces();
835#ifdef JPL_CONVEX_BUILDER_DEBUG
845 if (mFaces.size() < 2)
847 outError =
"Too few faces in hull";
848 return EResult::TooFewFaces;
851 return EResult::Success;
854 template<CVec3 Vec3Type>
857 std::unordered_set<int> used_verts;
858 for (
const Face* f : mFaces)
860 const Edge* e = f->mFirstEdge;
865 }
while (e != f->mFirstEdge);
867 return (
int)used_verts.size();
870 template<CVec3 Vec3Type>
873 for (
Face* f : mFaces)
875 Edge* e = f->mFirstEdge;
876 Array<int>::const_iterator index = std::find(inIndices.begin(), inIndices.end(), e->
mStartIdx);
877 if (index != inIndices.end())
892 if (index == inIndices.end())
893 index = inIndices.begin();
897 }
while (e != f->mFirstEdge);
899 if (matches == inIndices.size())
907 template<CVec3 Vec3Type>
908 template<
class Vec3i>
912 outTris.reserve(mFaces.size());
914 for (
const Face* f : mFaces)
915 f->Triangulate(mPositions, outTris);
918 template<CVec3 Vec3Type>
921 auto vec3max = [](
const Vec3& a,
const Vec3& b)
925 auto vec3abs = [](
const Vec3& v)
927 return Vec3{ std::abs(
GetX(v)), std::abs(
GetY(v)), std::abs(
GetZ(v)) };
931 Vec3 vmax{ 0.0f, 0.0f, 0.0f };
932 for (Vec3 v : mPositions)
933 vmax = vec3max(vmax, vec3abs(v));
934 return 3.0f * std::numeric_limits<float>::epsilon() * (
GetX(vmax) +
GetY(vmax) +
GetZ(vmax));
937 template<CVec3 Vec3Type>
938 inline void ConvexHullBuilder<Vec3Type>::GetFaceForPoint(
const Vec3& inPoint,
const Faces& inFaces, Face*& outFace,
float& outDistSq)
const
943 for (Face* f : inFaces)
948 const float dot = DotProduct(f->mNormal, inPoint - f->mCentroid);
951 const float dist_sq = dot * dot / LengthSquared(f->mNormal);
952 if (dist_sq > outDistSq)
962 template<CVec3 Vec3Type>
963 inline float ConvexHullBuilder<Vec3Type>::GetDistanceToEdgeSq(
const Vec3& inPoint,
const Face* inFace)
const
965 bool all_inside =
true;
966 float edge_dist_sq = std::numeric_limits<float>::max();
969 Edge* edge = inFace->mFirstEdge;
970 Vec3 p1 = mPositions[edge->GetPreviousEdge()->mStartIdx];
973 Vec3 p2 = mPositions[edge->mStartIdx];
974 if (DotProduct(CrossProduct(p2 - p1, inPoint - p1), inFace->mNormal) < 0.0f)
981 edge_dist_sq = std::min(edge_dist_sq, LengthSquared(
GetClosestPointOnLine(p1 - inPoint, p2 - inPoint, s)));
984 edge = edge->mNextEdge;
985 }
while (edge != inFace->mFirstEdge);
987 return all_inside ? 0.0f : edge_dist_sq;
990 template<CVec3 Vec3Type>
991 inline bool ConvexHullBuilder<Vec3Type>::AssignPointToFace(
int inPositionIdx,
const Faces& inFaces,
float inToleranceSq)
993 Vec3 point = mPositions[inPositionIdx];
998 GetFaceForPoint(point, inFaces, best_face, best_dist_sq);
1000 if (best_face !=
nullptr)
1003 if (best_dist_sq <= inToleranceSq)
1006 float dist_to_edge_sq = GetDistanceToEdgeSq(point, best_face);
1007 if (dist_to_edge_sq > inToleranceSq)
1010 mCoplanarList.push_back({ inPositionIdx, dist_to_edge_sq });
1016 if (best_dist_sq > best_face->mFurthestPointDistanceSq)
1019 best_face->mFurthestPointDistanceSq = best_dist_sq;
1020 best_face->mConflictList.push_back(inPositionIdx);
1025 best_face->mConflictList.insert(best_face->mConflictList.begin() + best_face->mConflictList.size() - 1, inPositionIdx);
1035 template<CVec3 Vec3Type>
1036 inline void ConvexHullBuilder<Vec3Type>::AddPoint(Face* inFacingFace,
int inIdx,
float inCoplanarToleranceSq, Faces& outNewFaces)
1039 Vec3 pos = mPositions[inIdx];
1041#ifdef JPL_CONVEX_BUILDER_DEBUG
1043 DebugRenderer::sInstance->DrawMarker(cDrawScale * (mOffset + pos), Color::sYellow, 0.1f);
1044 DebugRenderer::sInstance->DrawText3D(cDrawScale * (mOffset + pos), ConvertToString(inIdx), Color::sWhite);
1047#ifdef JPL_ENABLE_VALIDATION
1054 FindEdge(inFacingFace, pos, edges);
1058 outNewFaces.reserve(edges.size());
1059 for (
const FullEdge& e : edges)
1062 Face* f = CreateTriangle(e.mStartIdx, e.mEndIdx, inIdx);
1063 outNewFaces.push_back(f);
1067 for (
typename Faces::size_type i = 0; i < outNewFaces.size(); ++i)
1069 sLinkFace(outNewFaces[i]->mFirstEdge, edges[i].mNeighbourEdge);
1070 sLinkFace(outNewFaces[i]->mFirstEdge->mNextEdge, outNewFaces[(i + 1) % outNewFaces.size()]->mFirstEdge->mNextEdge->mNextEdge);
1074 Faces affected_faces = outNewFaces;
1075 while (!affected_faces.empty())
1078 Face* face = affected_faces.back();
1079 affected_faces.pop_back();
1081 if (!face->mRemoved)
1084 MergeDegenerateFace(face, affected_faces);
1090 if (!face->mRemoved)
1091 MergeCoplanarOrConcaveFaces(face, inCoplanarToleranceSq, affected_faces);
1096#ifdef JPL_ENABLE_VALIDATION
1102 template<CVec3 Vec3Type>
1103 inline void ConvexHullBuilder<Vec3Type>::GarbageCollectFaces()
1105 for (
int i = (
int)mFaces.size() - 1; i >= 0; --i)
1107 Face* f = mFaces[i];
1111 mFaces.erase(mFaces.begin() + i);
1116 template<CVec3 Vec3Type>
1117 inline ConvexHullBuilder<Vec3Type>::Face* ConvexHullBuilder<Vec3Type>::CreateFace()
1122#ifdef JPL_CONVEX_BUILDER_DEBUG
1124 f->mIteration = mIteration;
1128 mFaces.push_back(f);
1132 template<CVec3 Vec3Type>
1133 inline ConvexHullBuilder<Vec3Type>::Face* ConvexHullBuilder<Vec3Type>::CreateTriangle(
int inIdx1,
int inIdx2,
int inIdx3)
1135 Face* f = CreateFace();
1136 f->Initialize(inIdx1, inIdx2, inIdx3, mPositions);
1140 template<CVec3 Vec3Type>
1141 inline void ConvexHullBuilder<Vec3Type>::FreeFace(Face* inFace)
1145#ifdef JPL_ENABLE_VALIDATION
1147 if (Edge* e = inFace->mFirstEdge)
1153 }
while (e != inFace->mFirstEdge);
1161 template<CVec3 Vec3Type>
1162 inline void ConvexHullBuilder<Vec3Type>::FreeFaces()
1164 for (Face* f : mFaces)
1171 template<CVec3 Vec3Type>
1172 inline void ConvexHullBuilder<Vec3Type>::sLinkFace(Edge* inEdge1, Edge* inEdge2)
1175 JPL_ASSERT(inEdge1->mNeighbourEdge ==
nullptr);
1176 JPL_ASSERT(inEdge2->mNeighbourEdge ==
nullptr);
1177 JPL_ASSERT(inEdge1->mFace != inEdge2->mFace);
1180 JPL_ASSERT(inEdge1->mStartIdx == inEdge2->mNextEdge->mStartIdx);
1181 JPL_ASSERT(inEdge2->mStartIdx == inEdge1->mNextEdge->mStartIdx);
1184 inEdge1->mNeighbourEdge = inEdge2;
1185 inEdge2->mNeighbourEdge = inEdge1;
1188 template<CVec3 Vec3Type>
1189 inline void ConvexHullBuilder<Vec3Type>::sUnlinkFace(Face* inFace)
1192 Edge* e = inFace->mFirstEdge;
1195 if (e->mNeighbourEdge !=
nullptr)
1198 JPL_ASSERT(e->mNeighbourEdge->mNeighbourEdge == e);
1201 e->mNeighbourEdge->mNeighbourEdge =
nullptr;
1202 e->mNeighbourEdge =
nullptr;
1205 }
while (e != inFace->mFirstEdge);
1208 template<CVec3 Vec3Type>
1209 inline void ConvexHullBuilder<Vec3Type>::FindEdge(Face* inFacingFace,
const Vec3& inVertex, FullEdges& outEdges)
const
1215 JPL_ASSERT(inFacingFace->IsFacing(inVertex));
1218 inFacingFace->mRemoved =
true;
1226 constexpr int cMaxEdgeLength = 128;
1227 StackEntry stack[cMaxEdgeLength];
1228 int cur_stack_pos = 0;
1230 static_assert(
alignof(Edge) >= 2,
"Need lowest bit to indicate to tell if we completed the loop");
1233 stack[0].mFirstEdge = inFacingFace->mFirstEdge;
1234 stack[0].mCurrentEdge =
reinterpret_cast<Edge*
>(
reinterpret_cast<uintptr_t
>(inFacingFace->mFirstEdge) | 1);
1238 StackEntry& cur_entry = stack[cur_stack_pos];
1241 Edge* raw_e = cur_entry.mCurrentEdge;
1242 Edge* e =
reinterpret_cast<Edge*
>(
reinterpret_cast<uintptr_t
>(raw_e) & ~uintptr_t(1));
1243 cur_entry.mCurrentEdge = e->mNextEdge;
1246 if (raw_e == cur_entry.mFirstEdge)
1249 sUnlinkFace(e->mFace);
1252 if (--cur_stack_pos < 0)
1258 Edge* ne = e->mNeighbourEdge;
1261 Face* n = ne->mFace;
1265 if (n->IsFacing(inVertex))
1273 StackEntry& new_entry = stack[cur_stack_pos];
1274 new_entry.mFirstEdge = ne;
1275 new_entry.mCurrentEdge = ne->mNextEdge;
1281 full.mNeighbourEdge = ne;
1282 full.mStartIdx = e->mStartIdx;
1283 full.mEndIdx = ne->mStartIdx;
1284 outEdges.push_back(full);
1292#ifdef JPL_ENABLE_VALIDATION
1293 for (
int i = 0; i < (int)outEdges.size(); ++i)
1294 JPL_ASSERT(outEdges[i].mEndIdx == outEdges[(i + 1) % outEdges.size()].mStartIdx);
1297#ifdef JPL_CONVEX_BUILDER_DEBUG
1299 for (
int i = 0; i < (int)outEdges.size(); ++i)
1300 DebugRenderer::sInstance->DrawArrow(cDrawScale * (mOffset + mPositions[outEdges[i].mStartIdx]), cDrawScale * (mOffset + mPositions[outEdges[i].mEndIdx]), Color::sWhite, 0.01f);
1305 template<CVec3 Vec3Type>
1306 inline void ConvexHullBuilder<Vec3Type>::MergeFaces(Edge* inEdge)
1309 Face* face = inEdge->mFace;
1312 Edge* next_edge = inEdge->mNextEdge;
1313 Edge* prev_edge = inEdge->GetPreviousEdge();
1316 Edge* other_edge = inEdge->mNeighbourEdge;
1317 Face* other_face = other_edge->mFace;
1322#ifdef JPL_CONVEX_BUILDER_DEBUG
1323 DrawWireFace(face, Color::sGreen);
1324 DrawWireFace(other_face, Color::sRed);
1329 Edge* edge = other_edge->mNextEdge;
1330 prev_edge->mNextEdge = edge;
1334 if (edge->mNextEdge == other_edge)
1337 edge->mNextEdge = next_edge;
1340 edge = edge->mNextEdge;
1346 if (face->mFirstEdge == inEdge)
1347 face->mFirstEdge = prev_edge->mNextEdge;
1354 other_face->mFirstEdge =
nullptr;
1355 other_face->mRemoved =
true;
1358 face->CalculateNormalAndCentroid(mPositions);
1361 if (face->mFurthestPointDistanceSq > other_face->mFurthestPointDistanceSq)
1364 face->mConflictList.insert(face->mConflictList.end() - 1, other_face->mConflictList.begin(), other_face->mConflictList.end());
1369 face->mConflictList.insert(face->mConflictList.end(), other_face->mConflictList.begin(), other_face->mConflictList.end());
1370 face->mFurthestPointDistanceSq = other_face->mFurthestPointDistanceSq;
1372 other_face->mConflictList.clear();
1374#ifdef JPL_CONVEX_BUILDER_DEBUG
1375 DrawWireFace(face, Color::sWhite);
1380 template<CVec3 Vec3Type>
1381 inline void ConvexHullBuilder<Vec3Type>::MergeDegenerateFace(Face* inFace, Faces& ioAffectedFaces)
1384 if (LengthSquared(inFace->mNormal) < cMinTriangleAreaSq)
1387 float max_length_sq = 0.0f;
1388 Edge* longest_edge =
nullptr;
1389 Edge* e = inFace->mFirstEdge;
1390 Vec3 p1 = mPositions[e->mStartIdx];
1393 Edge* next = e->mNextEdge;
1394 Vec3 p2 = mPositions[next->mStartIdx];
1395 float length_sq = LengthSquared(p2 - p1);
1396 if (length_sq >= max_length_sq)
1398 max_length_sq = length_sq;
1403 }
while (e != inFace->mFirstEdge);
1406 MergeFaces(longest_edge);
1409 RemoveInvalidEdges(inFace, ioAffectedFaces);
1413 template<CVec3 Vec3Type>
1414 inline void ConvexHullBuilder<Vec3Type>::MergeCoplanarOrConcaveFaces(Face* inFace,
float inCoplanarToleranceSq, Faces& ioAffectedFaces)
1416 bool merged =
false;
1418 Edge* edge = inFace->mFirstEdge;
1422 Edge* next_edge = edge->mNextEdge;
1426 const Face* other_face = edge->mNeighbourEdge->mFace;
1427 const Vec3 delta_centroid = other_face->mCentroid - inFace->mCentroid;
1428 const float dist_other_face_centroid = DotProduct(inFace->mNormal, delta_centroid);
1429 const float signed_dist_other_face_centroid_sq = std::abs(dist_other_face_centroid) * dist_other_face_centroid;
1430 const float dist_face_centroid = -DotProduct(other_face->mNormal, delta_centroid);
1431 const float signed_dist_face_centroid_sq = std::abs(dist_face_centroid) * dist_face_centroid;
1432 const float face_normal_len_sq = LengthSquared(inFace->mNormal);
1433 const float other_face_normal_len_sq = LengthSquared(other_face->mNormal);
1434 if ((signed_dist_other_face_centroid_sq > -inCoplanarToleranceSq * face_normal_len_sq
1435 || signed_dist_face_centroid_sq > -inCoplanarToleranceSq * other_face_normal_len_sq)
1436 && DotProduct(inFace->mNormal, other_face->mNormal) > 0.0f)
1443 }
while (edge != inFace->mFirstEdge);
1446 RemoveInvalidEdges(inFace, ioAffectedFaces);
1449 template<CVec3 Vec3Type>
1450 inline void ConvexHullBuilder<Vec3Type>::sMarkAffected(Face* inFace, Faces& ioAffectedFaces)
1452 if (std::find(ioAffectedFaces.begin(), ioAffectedFaces.end(), inFace) == ioAffectedFaces.end())
1453 ioAffectedFaces.push_back(inFace);
1456 template<CVec3 Vec3Type>
1457 inline void ConvexHullBuilder<Vec3Type>::RemoveInvalidEdges(Face* inFace, Faces& ioAffectedFaces)
1461 bool recalculate_plane =
false;
1470 Edge* edge = inFace->mFirstEdge;
1471 Face* neighbour_face = edge->mNeighbourEdge->mFace;
1474 Edge* next_edge = edge->mNextEdge;
1475 Face* next_neighbour_face = next_edge->mNeighbourEdge->mFace;
1477 if (neighbour_face == inFace)
1481 if (edge->mNeighbourEdge == next_edge)
1484#ifdef JPL_CONVEX_BUILDER_DEBUG
1485 DrawWireFace(inFace, Color::sBlue);
1490 Edge* prev_edge = edge->GetPreviousEdge();
1491 prev_edge->mNextEdge = next_edge->mNextEdge;
1492 if (inFace->mFirstEdge == edge || inFace->mFirstEdge == next_edge)
1493 inFace->mFirstEdge = prev_edge;
1497#ifdef JPL_CONVEX_BUILDER_DEBUG
1498 DrawWireFace(inFace, Color::sGreen);
1503 if (RemoveTwoEdgeFace(inFace, ioAffectedFaces))
1507 recalculate_plane =
true;
1512 else if (neighbour_face == next_neighbour_face)
1515#ifdef JPL_CONVEX_BUILDER_DEBUG
1516 DrawWireFace(inFace, Color::sYellow);
1517 DrawWireFace(neighbour_face, Color::sRed);
1522 Edge* neighbour_edge = next_edge->mNeighbourEdge;
1523 Edge* next_neighbour_edge = neighbour_edge->mNextEdge;
1524 if (neighbour_face->mFirstEdge == next_neighbour_edge)
1525 neighbour_face->mFirstEdge = neighbour_edge;
1526 neighbour_edge->mNextEdge = next_neighbour_edge->mNextEdge;
1527 neighbour_edge->mNeighbourEdge = edge;
1528 delete next_neighbour_edge;
1531 if (inFace->mFirstEdge == next_edge)
1532 inFace->mFirstEdge = edge;
1533 edge->mNextEdge = next_edge->mNextEdge;
1534 edge->mNeighbourEdge = neighbour_edge;
1537#ifdef JPL_CONVEX_BUILDER_DEBUG
1538 DrawWireFace(inFace, Color::sYellow);
1539 DrawWireFace(neighbour_face, Color::sGreen);
1544 if (!RemoveTwoEdgeFace(neighbour_face, ioAffectedFaces))
1547 neighbour_face->CalculateNormalAndCentroid(mPositions);
1550 sMarkAffected(neighbour_face, ioAffectedFaces);
1554 if (RemoveTwoEdgeFace(inFace, ioAffectedFaces))
1558 recalculate_plane =
true;
1565 neighbour_face = next_neighbour_face;
1567 }
while (edge != inFace->mFirstEdge);
1571 if (recalculate_plane)
1572 inFace->CalculateNormalAndCentroid(mPositions);
1575 template<CVec3 Vec3Type>
1576 inline bool ConvexHullBuilder<Vec3Type>::RemoveTwoEdgeFace(Face* inFace, Faces& ioAffectedFaces)
const
1579 Edge* edge = inFace->mFirstEdge;
1580 Edge* next_edge = edge->mNextEdge;
1582 if (next_edge->mNextEdge == edge)
1584#ifdef JPL_CONVEX_BUILDER_DEBUG
1585 DrawWireFace(inFace, Color::sRed);
1590 Edge* neighbour_edge = edge->mNeighbourEdge;
1591 Face* neighbour_face = neighbour_edge->mFace;
1592 Edge* next_neighbour_edge = next_edge->mNeighbourEdge;
1593 Face* next_neighbour_face = next_neighbour_edge->mFace;
1594 sMarkAffected(neighbour_face, ioAffectedFaces);
1595 sMarkAffected(next_neighbour_face, ioAffectedFaces);
1598 neighbour_edge->mNeighbourEdge = next_neighbour_edge;
1599 next_neighbour_edge->mNeighbourEdge = neighbour_edge;
1602 edge->mNeighbourEdge =
nullptr;
1603 next_edge->mNeighbourEdge =
nullptr;
1606 inFace->mRemoved =
true;
1614#ifdef JPL_ENABLE_VALIDATION
1615 template<CVec3 Vec3Type>
1616 inline void ConvexHullBuilder<Vec3Type>::DumpFace(
const Face* inFace)
const
1618 std::cout << std::format(
"f:{}", std::uintptr_t(inFace)) <<
'\n';
1620 const Edge* e = inFace->mFirstEdge;
1623 std::cout << std::format(
"e:{} || i:{} e:{} f:{} ",
1626 std::uintptr_t(e->mNeighbourEdge),
1627 std::uintptr_t(e->mNeighbourEdge->mFace)) <<
'\n';
1629 }
while (e != inFace->mFirstEdge);
1632 template<CVec3 Vec3Type>
1633 inline void ConvexHullBuilder<Vec3Type>::DumpFaces()
const
1635 std::cout << std::format(
"Dump Faces:") <<
'\n';
1637 for (
const Face* f : mFaces)
1642 template<CVec3 Vec3Type>
1643 inline void ConvexHullBuilder<Vec3Type>::ValidateFace(
const Face* inFace)
const
1645 if (inFace->mRemoved)
1647 const Edge* e = inFace->mFirstEdge;
1653 }
while (e != inFace->mFirstEdge);
1659 const Edge* e = inFace->mFirstEdge;
1666 if (mFaces.size() > 2)
1667 for (
const Edge* other_edge = e->mNextEdge; other_edge != inFace->mFirstEdge; other_edge = other_edge->mNextEdge)
1668 JPL_ASSERT(e->mNeighbourEdge->mFace != other_edge->mNeighbourEdge->mFace);
1674 const Edge* nb_edge = e->mNeighbourEdge;
1676 if (nb_edge !=
nullptr)
1685 JPL_ASSERT(nb_edge->mNextEdge->mStartIdx == e->mStartIdx);
1688 JPL_ASSERT(e->mNextEdge->mStartIdx == nb_edge->mStartIdx);
1691 }
while (e != inFace->mFirstEdge);
1698 template<CVec3 Vec3Type>
1699 inline void ConvexHullBuilder<Vec3Type>::ValidateFaces()
const
1701 for (
const Face* f : mFaces)
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:80
Class that holds the information of an edge.
Definition ConvexHullBuilder.h:71
Edge(Face *inFace, int inStartIdx)
Definition ConvexHullBuilder.h:76
int mStartIdx
Vertex index in mPositions that indicates the start vertex of this edge.
Definition ConvexHullBuilder.h:87
Face * mFace
Face that this edge belongs to.
Definition ConvexHullBuilder.h:84
Edge * mNextEdge
Next edge of this face.
Definition ConvexHullBuilder.h:85
Edge * GetPreviousEdge()
Get the previous edge.
Definition ConvexHullBuilder.h:327
Edge * mNeighbourEdge
Edge that this edge is connected to.
Definition ConvexHullBuilder.h:86
Class that holds the information of one face.
Definition ConvexHullBuilder.h:95
bool Triangulate(std::span< const Vec3 > inPositions, Array< Vec3i > &outTris) const
Definition ConvexHullBuilder.h:432
~Face()
Definition ConvexHullBuilder.h:336
void Initialize(int inIdx0, int inIdx1, int inIdx2, std::span< const Vec3 > inPositions)
Initialize a face with three indices.
Definition ConvexHullBuilder.h:352
ConflictList mConflictList
Positions associated with this edge (that are closest to this edge). The last position in the list is...
Definition ConvexHullBuilder.h:116
Vec3 mCentroid
Center of the face.
Definition ConvexHullBuilder.h:115
Vec3 mNormal
Normal of this face, length is 2 times area of face.
Definition ConvexHullBuilder.h:114
void CalculateNormalAndCentroid(std::span< const Vec3 > inPositions)
Calculates the centroid and normal for this face.
Definition ConvexHullBuilder.h:384
bool mRemoved
Flag that indicates that face has been removed (face will be freed later)
Definition ConvexHullBuilder.h:119
float mFurthestPointDistanceSq
Squared distance of furthest point from the conflict list to the face.
Definition ConvexHullBuilder.h:118
Edge * mFirstEdge
First edge of this face.
Definition ConvexHullBuilder.h:117
bool IsFacing(const Vec3 &inPosition) const
Check if face inFace is facing inPosition.
Definition ConvexHullBuilder.h:466
Definition ConvexHullBuilder.h:57
~ConvexHullBuilder()
Definition ConvexHullBuilder.h:506
EResult
Result enum that indicates how the hull got created.
Definition ConvexHullBuilder.h:138
@ TooFewFaces
Too few faces in the created hull (signifies precision errors during building)
@ Degenerate
Degenerate hull detected.
@ Success
Hull building finished successfully.
@ TooFewPoints
Too few points to create a hull.
@ MaxVerticesReached
Hull building finished successfully, but the desired accuracy was not reached because the max vertice...
int GetNumVerticesUsed() const
Returns the amount of vertices that are currently used by the hull.
Definition ConvexHullBuilder.h:855
std::span< const Vec3 > Positions
Definition ConvexHullBuilder.h:130
Array< int > ConflictList
Definition ConvexHullBuilder.h:91
bool ContainsFace(const Array< int > &inIndices) const
Returns true if the hull contains a polygon with inIndices (counter clockwise indices in mPositions)
Definition ConvexHullBuilder.h:871
void GetTriangles(Array< Vec3i > &outTris)
Definition ConvexHullBuilder.h:909
Array< Face * > Faces
Definition ConvexHullBuilder.h:131
EResult Initialize(int inMaxVertices, float inTolerance, const char *&outError)
Definition ConvexHullBuilder.h:512
const Faces & GetFaces() const
Access to the created faces. Memory is owned by the convex hull builder.
Definition ConvexHullBuilder.h:170
void TriangulatePoly(std::span< const Vec3 > inPositions, std::span< int > poly, ContainerType< Vec3i, Args... > &outTris)
Definition Triangulation.h:80
Definition AcousticMaterial.h:36
JPL_INLINE T * DefaultNew(Args &&...args)
Allocate new object from deafult global memory resource used in JPLSpatial.
Definition Memory.h:191
std::uint32_t uint32
Definition Core.h:311
JPL_INLINE auto GetX(const Vec3Type &v) noexcept
Definition Vec3Traits.h:35
JPL_INLINE auto GetZ(const Vec3Type &v) noexcept
Definition Vec3Traits.h:37
JPL_INLINE void DefaultDelete(T *object)
Delete object allcoated from deafult global memory resource. The object must had been allocated by ca...
Definition Memory.h:198
std::pmr::memory_resource * GetDefaultMemoryResource() noexcept
Definition Memory.h:42
Vec3 GetClosestPointOnLine(const Vec3 &inA, const Vec3 &inB, uint32_t &outSet)
Definition ClosestPoint.h:66
JPL_INLINE auto GetY(const Vec3Type &v) noexcept
Definition Vec3Traits.h:36