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:
@ -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.
|
||||||
|
@ -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,26 +110,12 @@ 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;
|
||||||
|
|
||||||
|
// Get the number of Quickhull iterations.
|
||||||
|
u32 GetIterations() const;
|
||||||
|
|
||||||
// Translate this hull.
|
// Translate this hull.
|
||||||
void Translate(const b3Vec3& translation);
|
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.
|
// Draw this hull.
|
||||||
void Draw() const;
|
void Draw() const;
|
||||||
private:
|
private:
|
||||||
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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,10 +234,10 @@ 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);
|
||||||
|
|
||||||
// Find the longest segment.
|
// Find the longest segment.
|
||||||
float32 d0 = 0.0f;
|
float32 d0 = 0.0f;
|
||||||
|
|
||||||
@ -670,7 +671,7 @@ static void b3ResetFaceData(qhFace* face)
|
|||||||
// Compute polygon centroid
|
// Compute polygon centroid
|
||||||
b3Vec3 c;
|
b3Vec3 c;
|
||||||
c.SetZero();
|
c.SetZero();
|
||||||
|
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
qhHalfEdge* e = face->edge;
|
qhHalfEdge* e = face->edge;
|
||||||
do
|
do
|
||||||
@ -685,9 +686,9 @@ static void b3ResetFaceData(qhFace* face)
|
|||||||
c /= float32(count);
|
c /= float32(count);
|
||||||
|
|
||||||
// Compute normal
|
// Compute normal
|
||||||
b3Vec3 n;
|
b3Vec3 n;
|
||||||
n.SetZero();
|
n.SetZero();
|
||||||
|
|
||||||
e = face->edge;
|
e = face->edge;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -700,13 +701,13 @@ static void b3ResetFaceData(qhFace* face)
|
|||||||
|
|
||||||
// Apply Newell's method
|
// Apply Newell's method
|
||||||
n += b3Newell(v1, v2);
|
n += b3Newell(v1, v2);
|
||||||
|
|
||||||
e = e->next;
|
e = e->next;
|
||||||
} while (e != face->edge);
|
} while (e != face->edge);
|
||||||
|
|
||||||
// Centroid
|
// Centroid
|
||||||
face->center = c;
|
face->center = c;
|
||||||
|
|
||||||
float32 len = b3Length(n);
|
float32 len = b3Length(n);
|
||||||
B3_ASSERT(len > B3_EPSILON);
|
B3_ASSERT(len > B3_EPSILON);
|
||||||
n /= len;
|
n /= len;
|
||||||
@ -1295,7 +1296,7 @@ void qhHull::ValidateConvexity() const
|
|||||||
// Ensure polygon convexity
|
// Ensure polygon convexity
|
||||||
b3Vec3 P = edge->tail->position;
|
b3Vec3 P = edge->tail->position;
|
||||||
b3Vec3 Q = edge->twin->tail->position;
|
b3Vec3 Q = edge->twin->tail->position;
|
||||||
|
|
||||||
b3Vec3 E = Q - P;
|
b3Vec3 E = Q - P;
|
||||||
b3Vec3 D = b3Cross(E, face->plane.normal);
|
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(next->active == true);
|
||||||
B3_ASSERT(twin->tail == next->tail);
|
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;
|
if (e == edge)
|
||||||
break;
|
{
|
||||||
}
|
found = true;
|
||||||
e = e->next;
|
break;
|
||||||
} while (e != face->edge);
|
}
|
||||||
|
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
|
void qhHull::Validate(const qhFace* face) const
|
||||||
@ -1360,45 +1382,56 @@ 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;
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
B3_ASSERT(edge->active == true);
|
const qhHalfEdge* edge = face->edge;
|
||||||
B3_ASSERT(edge->face == face);
|
do
|
||||||
|
|
||||||
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->active == true);
|
||||||
B3_ASSERT(edge->twin->face != face);
|
B3_ASSERT(edge->face == face);
|
||||||
}
|
|
||||||
|
|
||||||
edge = edge->next;
|
B3_ASSERT(edge->twin != NULL);
|
||||||
} while (edge != begin);
|
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
|
// CW
|
||||||
edge = begin;
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
B3_ASSERT(edge->active == true);
|
const qhHalfEdge* edge = face->edge;
|
||||||
B3_ASSERT(edge->face == face);
|
do
|
||||||
|
|
||||||
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->active == true);
|
||||||
B3_ASSERT(edge->twin->face != face);
|
B3_ASSERT(edge->face == face);
|
||||||
}
|
|
||||||
|
|
||||||
edge = edge->prev;
|
B3_ASSERT(edge->twin != NULL);
|
||||||
} while (edge != begin);
|
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
|
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user