From 6c136123d9b2be1b05bcaca0e003ba2095e6deb4 Mon Sep 17 00:00:00 2001 From: Irlan <-> Date: Fri, 11 May 2018 03:14:17 -0300 Subject: [PATCH] face merging control in convex hull simplification; more asserts; consistency; now user can pass arbitrary number of vertices to b3QHull --- include/bounce/collision/shapes/qhull.h | 4 +- include/bounce/quickhull/qh_hull.h | 37 +++--- src/bounce/collision/shapes/qhull.cpp | 36 +++-- src/bounce/quickhull/qh_hull.cpp | 166 ++++++++++++++---------- 4 files changed, 143 insertions(+), 100 deletions(-) diff --git a/include/bounce/collision/shapes/qhull.h b/include/bounce/collision/shapes/qhull.h index cdb540f..2a67a13 100644 --- a/include/bounce/collision/shapes/qhull.h +++ b/include/bounce/collision/shapes/qhull.h @@ -43,9 +43,7 @@ struct b3QHull : public b3Hull } // Create a convex hull from an array of points. - // The counter must be in the range [0, B3_MAX_HULL_VERTICES]. - // Coincident points are removed. - // Some coplanar faces are merged. + // If the creation has failed then this convex hull is not modified. void Set(const b3Vec3* points, u32 count); // Set this hull as a cylinder located at the origin. diff --git a/include/bounce/quickhull/qh_hull.h b/include/bounce/quickhull/qh_hull.h index 5e2ba34..c5bc553 100644 --- a/include/bounce/quickhull/qh_hull.h +++ b/include/bounce/quickhull/qh_hull.h @@ -95,8 +95,6 @@ struct qhVertex // A convex hull builder. // Given a list of points constructs its convex hull. -// The output convex hull might contain polygonal faces and not only triangles. -// Coplanar face merging is necessary for stable physics simulation. class qhHull { public: @@ -112,26 +110,12 @@ public: // Get the list of faces in this convex hull. const qhList& GetFaceList() const; + // Get the number of Quickhull iterations. + u32 GetIterations() const; + // Translate this hull. void Translate(const b3Vec3& translation); - // Get the number of iterations this algorithm ran. - u32 GetIterations() const; - - // Validate convexity. - // Called at each iteration. - void ValidateConvexity() const; - - // Validate connectivity. - // Called at each iteration. - void Validate() const; - - // Called anywhere. - void Validate(const qhFace* face) const; - - // Called anywhere. - void Validate(const qhHalfEdge* edge) const; - // Draw this hull. void Draw() const; private: @@ -141,6 +125,7 @@ private: qhFace* RemoveEdge(qhHalfEdge* edge); qhFace* AddFace(qhVertex* v1, qhVertex* v2, qhVertex* v3); + qhFace* RemoveFace(qhFace* face); bool MergeFace(qhFace* face); @@ -164,6 +149,20 @@ private: void ResolveOrphans(); + // Validate convexity. + // Called at each iteration. + void ValidateConvexity() const; + + // Validate connectivity. + // Called at each iteration. + void Validate() const; + + // Called anywhere. + void Validate(const qhFace* face) const; + + // Called anywhere. + void Validate(const qhHalfEdge* edge) const; + // List of active vertices qhList m_vertexList; diff --git a/src/bounce/collision/shapes/qhull.cpp b/src/bounce/collision/shapes/qhull.cpp index 9918169..9f7faa5 100644 --- a/src/bounce/collision/shapes/qhull.cpp +++ b/src/bounce/collision/shapes/qhull.cpp @@ -144,15 +144,12 @@ static b3Vec3 b3ComputeCentroid(b3QHull* hull) void b3QHull::Set(const b3Vec3* points, u32 count) { - B3_ASSERT(count >= 4 && count <= B3_MAX_HULL_VERTICES); + B3_ASSERT(count >= 4); - // Clamp vertices into range [0, B3_MAX_HULL_VERTICES] - u32 n = b3Min(count, u32(B3_MAX_HULL_VERTICES)); - - // Copy points into local buffer, weld coincident points. - b3Vec3 ps[B3_MAX_HULL_VERTICES]; + // Copy points into local buffer, perform welding. u32 psCount = 0; - for (u32 i = 0; i < n; ++i) + b3Vec3* ps = (b3Vec3*) b3Alloc(count * sizeof(b3Vec3)); + for (u32 i = 0; i < count; ++i) { b3Vec3 p = points[i]; @@ -185,6 +182,7 @@ void b3QHull::Set(const b3Vec3* points, u32 count) qhHull primary; primary.Construct(ps, psCount); + b3Free(ps); // Simplify the constructed hull. @@ -209,18 +207,20 @@ void b3QHull::Set(const b3Vec3* points, u32 count) b3Plane plane = f->plane; B3_ASSERT(plane.offset > 0.0f); b3Vec3 v = plane.normal / plane.offset; - + b3Vec3 vn = plane.normal; + bool unique = true; - // There's a magic portion of code lost in the island that would cluster those planes. - // While we can't find it, simply keep the face which has the largest area. for (u32 j = 0; j < dvCount; ++j) { qhFace*& df = dfs[j]; b3Vec3& dv = dvs[j]; + b3Vec3 dvn = b3Normalize(dv); - const float32 kTol = 0.1f; - if (b3DistanceSquared(v, dv) <= kTol) + // ~20 degrees + const float32 kTol = 0.95f; + + if (b3Dot(vn, dvn) > kTol) { if (f->area > df->area) { @@ -243,6 +243,12 @@ void b3QHull::Set(const b3Vec3* points, u32 count) b3Free(dfs); + if (dvCount < 4) + { + b3Free(dvs); + return; + } + qhHull dual; dual.Construct(dvs, dvCount); b3Free(dvs); @@ -273,6 +279,12 @@ void b3QHull::Set(const b3Vec3* points, u32 count) } } + if (pvCount < 4) + { + b3Free(pvs); + return; + } + qhHull hull; hull.Construct(pvs, pvCount); b3Free(pvs); diff --git a/src/bounce/quickhull/qh_hull.cpp b/src/bounce/quickhull/qh_hull.cpp index 73cb76d..6663eb6 100644 --- a/src/bounce/quickhull/qh_hull.cpp +++ b/src/bounce/quickhull/qh_hull.cpp @@ -17,10 +17,9 @@ */ #include -#include #include -static float32 qhFindAABB(u32 iMin[3], u32 iMax[3], const b3Vec3* vertices, u32 count) +static float32 qhFindAABB(u32 iMin[3], u32 iMax[3], const b3Vec3* vs, u32 count) { b3Vec3 min(B3_MAX_FLOAT, B3_MAX_FLOAT, B3_MAX_FLOAT); iMin[0] = 0; @@ -34,7 +33,7 @@ static float32 qhFindAABB(u32 iMin[3], u32 iMax[3], const b3Vec3* vertices, u32 for (u32 i = 0; i < count; ++i) { - b3Vec3 v = vertices[i]; + b3Vec3 v = vs[i]; for (u32 j = 0; j < 3; ++j) { @@ -98,6 +97,8 @@ qhHull::~qhHull() void qhHull::Construct(const b3Vec3* vs, u32 count) { + B3_ASSERT(count > 0 && count >= 4); + // Compute memory buffer size for the worst case. u32 size = 0; @@ -233,10 +234,10 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount) { // Find the points that maximizes the distance along the // canonical axes. - // Store tolerance for coplanarity checks. + // Also store a tolerance for coplanarity checks. u32 aabbMin[3], aabbMax[3]; m_tolerance = qhFindAABB(aabbMin, aabbMax, vertices, vertexCount); - + // Find the longest segment. float32 d0 = 0.0f; @@ -670,7 +671,7 @@ static void b3ResetFaceData(qhFace* face) // Compute polygon centroid b3Vec3 c; c.SetZero(); - + u32 count = 0; qhHalfEdge* e = face->edge; do @@ -685,9 +686,9 @@ static void b3ResetFaceData(qhFace* face) c /= float32(count); // Compute normal - b3Vec3 n; + b3Vec3 n; n.SetZero(); - + e = face->edge; do { @@ -700,13 +701,13 @@ static void b3ResetFaceData(qhFace* face) // Apply Newell's method n += b3Newell(v1, v2); - + e = e->next; } while (e != face->edge); // Centroid face->center = c; - + float32 len = b3Length(n); B3_ASSERT(len > B3_EPSILON); n /= len; @@ -1295,7 +1296,7 @@ void qhHull::ValidateConvexity() const // Ensure polygon convexity b3Vec3 P = edge->tail->position; b3Vec3 Q = edge->twin->tail->position; - + b3Vec3 E = Q - P; b3Vec3 D = b3Cross(E, face->plane.normal); @@ -1339,20 +1340,41 @@ void qhHull::Validate(const qhHalfEdge* edge) const B3_ASSERT(next->active == true); B3_ASSERT(twin->tail == next->tail); - bool found = false; - const qhFace* face = edge->face; - const qhHalfEdge* e = face->edge; - do { - if (e == edge) + // CCW + bool found = false; + const qhFace* face = edge->face; + const qhHalfEdge* e = face->edge; + do { - found = true; - break; - } - e = e->next; - } while (e != face->edge); + if (e == edge) + { + found = true; + break; + } + e = e->next; + } while (e != face->edge); - B3_ASSERT(found == true); + B3_ASSERT(found == true); + } + + { + // CW + bool found = false; + const qhFace* face = edge->face; + const qhHalfEdge* e = face->edge; + do + { + if (e == edge) + { + found = true; + break; + } + e = e->prev; + } while (e != face->edge); + + B3_ASSERT(found == true); + } } void qhHull::Validate(const qhFace* face) const @@ -1360,45 +1382,56 @@ void qhHull::Validate(const qhFace* face) const B3_ASSERT(face->active == true); // CCW - const qhHalfEdge* begin = face->edge; - const qhHalfEdge* edge = begin; - do { - B3_ASSERT(edge->active == true); - B3_ASSERT(edge->face == face); - - B3_ASSERT(edge->twin != NULL); - B3_ASSERT(edge->twin->active == true); - - if (edge->twin->face != NULL) + const qhHalfEdge* edge = face->edge; + do { - B3_ASSERT(edge->twin->face->active == true); - B3_ASSERT(edge->twin->face != face); - } + B3_ASSERT(edge->active == true); + B3_ASSERT(edge->face == face); - edge = edge->next; - } while (edge != begin); + B3_ASSERT(edge->twin != NULL); + B3_ASSERT(edge->twin->active == true); + + if (edge->twin->face != NULL) + { + B3_ASSERT(edge->twin->face->active == true); + B3_ASSERT(edge->twin->face != face); + } + + edge = edge->next; + } while (edge != face->edge); + } // CW - edge = begin; - do { - B3_ASSERT(edge->active == true); - B3_ASSERT(edge->face == face); - - B3_ASSERT(edge->twin != NULL); - B3_ASSERT(edge->twin->active == true); - - if (edge->twin->face != NULL) + const qhHalfEdge* edge = face->edge; + do { - B3_ASSERT(edge->twin->face->active == true); - B3_ASSERT(edge->twin->face != face); - } + B3_ASSERT(edge->active == true); + B3_ASSERT(edge->face == face); - edge = edge->prev; - } while (edge != begin); + B3_ASSERT(edge->twin != NULL); + B3_ASSERT(edge->twin->active == true); - Validate(face->edge); + if (edge->twin->face != NULL) + { + B3_ASSERT(edge->twin->face->active == true); + B3_ASSERT(edge->twin->face != face); + } + + edge = edge->prev; + } while (edge != face->edge); + } + + { + const qhHalfEdge* edge = face->edge; + do + { + Validate(edge); + + edge = edge->next; + } while (edge != face->edge); + } } void qhHull::Validate() const @@ -1436,21 +1469,24 @@ void qhHull::Draw() const { for (qhFace* face = m_faceList.head; face != NULL; face = face->next) { - b3StackArray polygon; - polygon.Resize(0); - - const qhHalfEdge* begin = face->edge; - const qhHalfEdge* edge = begin; - do - { - polygon.PushBack(edge->tail->position); - edge = edge->next; - } while (edge != begin); - b3Vec3 c = face->center; b3Vec3 n = face->plane.normal; - b3Draw_draw->DrawSolidPolygon(n, polygon.Begin(), polygon.Count(), b3Color(1.0f, 1.0f, 1.0f, 0.5f)); + b3Draw_draw->DrawSegment(c, c + n, b3Color(1.0f, 1.0f, 1.0f)); + + const qhHalfEdge* edge = face->edge; + do + { + qhVertex* v1 = face->edge->tail; + qhVertex* v2 = edge->tail; + const qhHalfEdge* next = edge->next; + qhVertex* v3 = next->tail; + + b3Draw_draw->DrawTriangle(v1->position, v2->position, v3->position, b3Color(0.0f, 0.0f, 0.0f, 1.0f)); + b3Draw_draw->DrawSolidTriangle(n, v1->position, v2->position, v3->position, b3Color(1.0f, 1.0f, 1.0f, 0.5f)); + + edge = next; + } while (edge->next != face->edge); qhVertex* v = face->conflictList.head; while (v) @@ -1459,7 +1495,5 @@ void qhHull::Draw() const b3Draw_draw->DrawSegment(c, v->position, b3Color(1.0f, 1.0f, 0.0f)); v = v->next; } - - b3Draw_draw->DrawSegment(c, c + n, b3Color(1.0f, 1.0f, 1.0f)); } } \ No newline at end of file