face merging control in convex hull simplification; more asserts; consistency; now user can pass arbitrary number of vertices to b3QHull

This commit is contained in:
Irlan
2018-05-11 03:14:17 -03:00
parent 4da08af7fa
commit 6c136123d9
4 changed files with 143 additions and 100 deletions

View File

@ -43,9 +43,7 @@ struct b3QHull : public b3Hull
} }
// Create a convex hull from an array of points. // Create a convex hull from an array of points.
// The counter must be in the range [0, B3_MAX_HULL_VERTICES]. // If the creation has failed then this convex hull is not modified.
// Coincident points are removed.
// Some coplanar faces are merged.
void Set(const b3Vec3* points, u32 count); void Set(const b3Vec3* points, u32 count);
// Set this hull as a cylinder located at the origin. // Set this hull as a cylinder located at the origin.

View File

@ -95,8 +95,6 @@ struct qhVertex
// A convex hull builder. // A convex hull builder.
// Given a list of points constructs its convex hull. // 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 class qhHull
{ {
public: public:
@ -112,25 +110,11 @@ public:
// Get the list of faces in this convex hull. // Get the list of faces in this convex hull.
const qhList<qhFace>& GetFaceList() const; const qhList<qhFace>& GetFaceList() const;
// Translate this hull. // Get the number of Quickhull iterations.
void Translate(const b3Vec3& translation);
// Get the number of iterations this algorithm ran.
u32 GetIterations() const; u32 GetIterations() const;
// Validate convexity. // Translate this hull.
// Called at each iteration. void Translate(const b3Vec3& translation);
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. // Draw this hull.
void Draw() const; void Draw() const;
@ -141,6 +125,7 @@ private:
qhFace* RemoveEdge(qhHalfEdge* edge); qhFace* RemoveEdge(qhHalfEdge* edge);
qhFace* AddFace(qhVertex* v1, qhVertex* v2, qhVertex* v3); qhFace* AddFace(qhVertex* v1, qhVertex* v2, qhVertex* v3);
qhFace* RemoveFace(qhFace* face); qhFace* RemoveFace(qhFace* face);
bool MergeFace(qhFace* face); bool MergeFace(qhFace* face);
@ -164,6 +149,20 @@ private:
void ResolveOrphans(); 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 // List of active vertices
qhList<qhVertex> m_vertexList; qhList<qhVertex> m_vertexList;

View File

@ -144,15 +144,12 @@ static b3Vec3 b3ComputeCentroid(b3QHull* hull)
void b3QHull::Set(const b3Vec3* points, u32 count) 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] // Copy points into local buffer, perform welding.
u32 n = b3Min(count, u32(B3_MAX_HULL_VERTICES));
// Copy points into local buffer, weld coincident points.
b3Vec3 ps[B3_MAX_HULL_VERTICES];
u32 psCount = 0; 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]; b3Vec3 p = points[i];
@ -185,6 +182,7 @@ void b3QHull::Set(const b3Vec3* points, u32 count)
qhHull primary; qhHull primary;
primary.Construct(ps, psCount); primary.Construct(ps, psCount);
b3Free(ps);
// Simplify the constructed hull. // Simplify the constructed hull.
@ -209,18 +207,20 @@ void b3QHull::Set(const b3Vec3* points, u32 count)
b3Plane plane = f->plane; b3Plane plane = f->plane;
B3_ASSERT(plane.offset > 0.0f); B3_ASSERT(plane.offset > 0.0f);
b3Vec3 v = plane.normal / plane.offset; b3Vec3 v = plane.normal / plane.offset;
b3Vec3 vn = plane.normal;
bool unique = true; 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) for (u32 j = 0; j < dvCount; ++j)
{ {
qhFace*& df = dfs[j]; qhFace*& df = dfs[j];
b3Vec3& dv = dvs[j]; b3Vec3& dv = dvs[j];
b3Vec3 dvn = b3Normalize(dv);
const float32 kTol = 0.1f; // ~20 degrees
if (b3DistanceSquared(v, dv) <= kTol) const float32 kTol = 0.95f;
if (b3Dot(vn, dvn) > kTol)
{ {
if (f->area > df->area) if (f->area > df->area)
{ {
@ -243,6 +243,12 @@ void b3QHull::Set(const b3Vec3* points, u32 count)
b3Free(dfs); b3Free(dfs);
if (dvCount < 4)
{
b3Free(dvs);
return;
}
qhHull dual; qhHull dual;
dual.Construct(dvs, dvCount); dual.Construct(dvs, dvCount);
b3Free(dvs); b3Free(dvs);
@ -273,6 +279,12 @@ void b3QHull::Set(const b3Vec3* points, u32 count)
} }
} }
if (pvCount < 4)
{
b3Free(pvs);
return;
}
qhHull hull; qhHull hull;
hull.Construct(pvs, pvCount); hull.Construct(pvs, pvCount);
b3Free(pvs); b3Free(pvs);

View File

@ -17,10 +17,9 @@
*/ */
#include <bounce/quickhull/qh_hull.h> #include <bounce/quickhull/qh_hull.h>
#include <bounce/common/template/array.h>
#include <bounce/common/draw.h> #include <bounce/common/draw.h>
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); b3Vec3 min(B3_MAX_FLOAT, B3_MAX_FLOAT, B3_MAX_FLOAT);
iMin[0] = 0; 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) for (u32 i = 0; i < count; ++i)
{ {
b3Vec3 v = vertices[i]; b3Vec3 v = vs[i];
for (u32 j = 0; j < 3; ++j) for (u32 j = 0; j < 3; ++j)
{ {
@ -98,6 +97,8 @@ qhHull::~qhHull()
void qhHull::Construct(const b3Vec3* vs, u32 count) void qhHull::Construct(const b3Vec3* vs, u32 count)
{ {
B3_ASSERT(count > 0 && count >= 4);
// Compute memory buffer size for the worst case. // Compute memory buffer size for the worst case.
u32 size = 0; u32 size = 0;
@ -233,7 +234,7 @@ bool qhHull::BuildInitialHull(const b3Vec3* vertices, u32 vertexCount)
{ {
// Find the points that maximizes the distance along the // Find the points that maximizes the distance along the
// canonical axes. // canonical axes.
// Store tolerance for coplanarity checks. // Also store a tolerance for coplanarity checks.
u32 aabbMin[3], aabbMax[3]; u32 aabbMin[3], aabbMax[3];
m_tolerance = qhFindAABB(aabbMin, aabbMax, vertices, vertexCount); m_tolerance = qhFindAABB(aabbMin, aabbMax, vertices, vertexCount);
@ -1339,6 +1340,8 @@ void qhHull::Validate(const qhHalfEdge* edge) const
B3_ASSERT(next->active == true); B3_ASSERT(next->active == true);
B3_ASSERT(twin->tail == next->tail); B3_ASSERT(twin->tail == next->tail);
{
// CCW
bool found = false; bool found = false;
const qhFace* face = edge->face; const qhFace* face = edge->face;
const qhHalfEdge* e = face->edge; const qhHalfEdge* e = face->edge;
@ -1355,13 +1358,32 @@ void qhHull::Validate(const qhHalfEdge* edge) const
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 void qhHull::Validate(const qhFace* face) const
{ {
B3_ASSERT(face->active == true); B3_ASSERT(face->active == true);
// CCW // CCW
const qhHalfEdge* begin = face->edge; {
const qhHalfEdge* edge = begin; const qhHalfEdge* edge = face->edge;
do do
{ {
B3_ASSERT(edge->active == true); B3_ASSERT(edge->active == true);
@ -1377,10 +1399,12 @@ void qhHull::Validate(const qhFace* face) const
} }
edge = edge->next; edge = edge->next;
} while (edge != begin); } while (edge != face->edge);
}
// CW // CW
edge = begin; {
const qhHalfEdge* edge = face->edge;
do do
{ {
B3_ASSERT(edge->active == true); B3_ASSERT(edge->active == true);
@ -1396,9 +1420,18 @@ void qhHull::Validate(const qhFace* face) const
} }
edge = edge->prev; edge = edge->prev;
} while (edge != begin); } while (edge != face->edge);
}
Validate(face->edge); {
const qhHalfEdge* edge = face->edge;
do
{
Validate(edge);
edge = edge->next;
} while (edge != face->edge);
}
} }
void qhHull::Validate() const void qhHull::Validate() const
@ -1436,21 +1469,24 @@ void qhHull::Draw() const
{ {
for (qhFace* face = m_faceList.head; face != NULL; face = face->next) for (qhFace* face = m_faceList.head; face != NULL; face = face->next)
{ {
b3StackArray<b3Vec3, 256> 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 c = face->center;
b3Vec3 n = face->plane.normal; 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; qhVertex* v = face->conflictList.head;
while (v) while (v)
@ -1459,7 +1495,5 @@ void qhHull::Draw() const
b3Draw_draw->DrawSegment(c, v->position, b3Color(1.0f, 1.0f, 0.0f)); b3Draw_draw->DrawSegment(c, v->position, b3Color(1.0f, 1.0f, 0.0f));
v = v->next; v = v->next;
} }
b3Draw_draw->DrawSegment(c, c + n, b3Color(1.0f, 1.0f, 1.0f));
} }
} }