remove quickhull array dependency (except for drawing)

This commit is contained in:
Irlan 2018-04-20 23:37:59 -03:00
parent bd4ca5d143
commit 4ebe826eea
4 changed files with 200 additions and 187 deletions

View File

@ -20,7 +20,6 @@
#define QH_HULL_H #define QH_HULL_H
#include <bounce/common/geometry.h> #include <bounce/common/geometry.h>
#include <bounce/common/template/array.h>
template<class T> template<class T>
struct qhList struct qhList
@ -100,7 +99,7 @@ public:
~qhHull(); ~qhHull();
// Construct this hull given a memory buffer and an array of points. // Construct this hull given a memory buffer and an array of points.
// Use qhGetBufferCapacity to get the buffer capacity from the point array size. // Use qhGetBufferSize to get the buffer size given the number of points.
void Construct(void* buffer, const b3Vec3* vertices, u32 vertexCount); void Construct(void* buffer, const b3Vec3* vertices, u32 vertexCount);
// Get the number of iterations this algorithm ran. // Get the number of iterations this algorithm ran.
@ -129,20 +128,17 @@ private:
void AddVertex(qhVertex* v); void AddVertex(qhVertex* v);
void BuildHorizon(b3Array<qhHalfEdge*>& horizon, qhVertex* eye); void BuildHorizon(qhVertex* eye);
void BuildHorizon(qhVertex* eye, qhHalfEdge* e0, qhFace* f);
void BuildHorizon(b3Array<qhHalfEdge*>& horizon, qhVertex* eye, qhHalfEdge* e0, qhFace* f); void AddNewFaces(qhVertex* eye);
void MergeFaces();
bool MergeFace(qhFace* face);
qhFace* AddTriangle(qhVertex* v1, qhVertex* v2, qhVertex* v3); qhFace* AddTriangle(qhVertex* v1, qhVertex* v2, qhVertex* v3);
qhHalfEdge* AddAdjoiningTriangle(qhVertex* v, qhHalfEdge* he); qhHalfEdge* AddAdjoiningTriangle(qhVertex* v, qhHalfEdge* he);
void AddNewFaces(b3Array<qhFace*>& newFaces, qhVertex* eye, const b3Array<qhHalfEdge*>& horizon);
bool MergeFace(qhFace* face);
void MergeFaces(b3Array<qhFace*>& newFaces);
qhHalfEdge* FindTwin(const qhVertex* tail, const qhVertex* head) const; qhHalfEdge* FindTwin(const qhVertex* tail, const qhVertex* head) const;
// Coplanarity tolerance // Coplanarity tolerance
@ -167,6 +163,12 @@ private:
qhVertex* m_freeVertices; qhVertex* m_freeVertices;
qhHalfEdge* m_freeEdges; qhHalfEdge* m_freeEdges;
qhFace* m_freeFaces; qhFace* m_freeFaces;
qhHalfEdge** m_horizon;
u32 m_horizonCount;
qhFace** m_newFaces;
u32 m_newFaceCount;
}; };
#include <bounce/quickhull/qh_hull.inl> #include <bounce/quickhull/qh_hull.inl>

View File

@ -128,20 +128,31 @@ inline void qhFace::ComputeCenterAndPlane()
// its value at compile-time. That is particularly usefull when you want to // its value at compile-time. That is particularly usefull when you want to
// create a stack buffer from a constant number of vertices. // create a stack buffer from a constant number of vertices.
// Due to the constexpr specifier, this function is automatically inlined. // Due to the constexpr specifier, this function is automatically inlined.
constexpr u32 qhGetBufferCapacity(u32 pointCount) constexpr u32 qhGetBufferSize(u32 pointCount)
{ {
u32 size = 0;
// Hull using Euler's Formula
u32 V = pointCount; u32 V = pointCount;
u32 E = 3 * V - 6; u32 E = 3 * V - 6;
u32 HE = 2 * E; u32 HE = 2 * E;
u32 F = 2 * V - 4; u32 F = 2 * V - 4;
HE *= 2;
F *= 2;
u32 size = 0;
size += V * sizeof(qhVertex); size += V * sizeof(qhVertex);
size += HE * sizeof(qhHalfEdge); size += HE * sizeof(qhHalfEdge);
size += F * sizeof(qhFace); size += F * sizeof(qhFace);
// Extra
size += HE * sizeof(qhHalfEdge);
size += F * sizeof(qhFace);
// Horizon
size += HE * sizeof(qhHalfEdge*);
// New Faces
// One face per horizon edge
size += HE * sizeof(qhFace*);
return size; return size;
} }

View File

@ -175,8 +175,8 @@ void b3QHull::Set(const b3Vec3* points, u32 count)
// Create a convex hull. // Create a convex hull.
// Allocate memory buffer for the worst case. // Allocate memory buffer for the worst case.
const u32 qhBufferCapacity = qhGetBufferCapacity(B3_MAX_HULL_VERTICES); const u32 qhBufferSize = qhGetBufferSize(B3_MAX_HULL_VERTICES);
u8 qhBuffer[qhBufferCapacity]; u8 qhBuffer[qhBufferSize];
// Build // Build
qhHull hull; qhHull hull;

View File

@ -17,6 +17,7 @@
*/ */
#include <bounce/quickhull/qh_hull.h> #include <bounce/quickhull/qh_hull.h>
#include <bounce/common/template/array.h>
#include <bounce/common/template/stack.h> #include <bounce/common/template/stack.h>
#include <bounce/common/draw.h> #include <bounce/common/draw.h>
@ -99,6 +100,12 @@ void qhHull::Construct(void* memory, const b3Vec3* vs, u32 count)
FreeFace(f); FreeFace(f);
} }
m_horizon = (qhHalfEdge**)((u8*)faces + F * sizeof(qhFace*));
m_horizonCount = 0;
m_newFaces = (qhFace**)((u8*)m_horizon + HE * sizeof(qhHalfEdge*));
m_newFaceCount = 0;
m_faceList.head = NULL; m_faceList.head = NULL;
m_faceList.count = 0; m_faceList.count = 0;
m_iteration = 0; m_iteration = 0;
@ -346,45 +353,12 @@ qhVertex* qhHull::NextVertex()
void qhHull::AddVertex(qhVertex* eye) void qhHull::AddVertex(qhVertex* eye)
{ {
b3StackArray<qhHalfEdge*, 32> horizon; BuildHorizon(eye);
BuildHorizon(horizon, eye); AddNewFaces(eye);
MergeFaces();
b3StackArray<qhFace*, 32> newFaces;
AddNewFaces(newFaces, eye, horizon);
MergeFaces(newFaces);
} }
void qhHull::BuildHorizon(b3Array<qhHalfEdge*>& horizon, qhVertex* eye, qhHalfEdge* edge0, qhFace* face) void qhHull::BuildHorizon(qhVertex* eye)
{
// Mark face as visible/visited.
face->state = qhFace::e_visible;
qhHalfEdge* edge = edge0;
do
{
qhHalfEdge* adjEdge = edge->twin;
qhFace* adjFace = adjEdge->face;
if (adjFace->state == qhFace::e_invisible)
{
if (b3Distance(eye->position, adjFace->plane) > m_tolerance)
{
BuildHorizon(horizon, eye, adjEdge, adjFace);
}
else
{
horizon.PushBack(edge);
}
}
edge = edge->next;
} while (edge != edge0);
}
void qhHull::BuildHorizon(b3Array<qhHalfEdge*>& horizon, qhVertex* eye)
{ {
// Clean visited flags // Clean visited flags
{ {
@ -402,12 +376,150 @@ void qhHull::BuildHorizon(b3Array<qhHalfEdge*>& horizon, qhVertex* eye)
} }
} }
// todo
// Iterative DFS.
// Ensure CCW order of horizon edges.
// Build horizon. // Build horizon.
BuildHorizon(horizon, eye, eye->conflictFace->edge, eye->conflictFace); m_horizonCount = 0;
BuildHorizon(eye, eye->conflictFace->edge, eye->conflictFace);
}
void qhHull::BuildHorizon(qhVertex* eye, qhHalfEdge* edge0, qhFace* face)
{
// Mark face as visible/visited.
face->state = qhFace::e_visible;
qhHalfEdge* edge = edge0;
do
{
qhHalfEdge* adjEdge = edge->twin;
qhFace* adjFace = adjEdge->face;
if (adjFace->state == qhFace::e_invisible)
{
if (b3Distance(eye->position, adjFace->plane) > m_tolerance)
{
BuildHorizon(eye, adjEdge, adjFace);
}
else
{
m_horizon[m_horizonCount++] = edge;
}
}
edge = edge->next;
} while (edge != edge0);
}
void qhHull::AddNewFaces(qhVertex* eye)
{
m_newFaceCount = 0;
B3_ASSERT(m_horizonCount > 0);
qhHalfEdge* beginEdge = NULL;
qhHalfEdge* prevEdge = NULL;
{
qhHalfEdge* edge = m_horizon[0];
qhHalfEdge* leftEdge = AddAdjoiningTriangle(eye, edge);
qhHalfEdge* rightEdge = leftEdge->prev;
prevEdge = rightEdge;
beginEdge = leftEdge;
m_newFaces[m_newFaceCount++] = leftEdge->face;
}
for (u32 i = 1; i < m_horizonCount - 1; ++i)
{
qhHalfEdge* edge = m_horizon[i];
qhHalfEdge* leftEdge = AddAdjoiningTriangle(eye, edge);
qhHalfEdge* rightEdge = leftEdge->prev;
leftEdge->twin = prevEdge;
prevEdge->twin = leftEdge;
prevEdge = rightEdge;
m_newFaces[m_newFaceCount++] = leftEdge->face;
}
{
qhHalfEdge* edge = m_horizon[m_horizonCount - 1];
qhHalfEdge* leftEdge = AddAdjoiningTriangle(eye, edge);
qhHalfEdge* rightEdge = leftEdge->prev;
leftEdge->twin = prevEdge;
prevEdge->twin = leftEdge;
rightEdge->twin = beginEdge;
beginEdge->twin = rightEdge;
m_newFaces[m_newFaceCount++] = leftEdge->face;
}
qhFace* f = m_faceList.head;
while (f)
{
if (f->state == qhFace::e_invisible)
{
f = f->next;
continue;
}
// Partition conflict vertices.
qhVertex* v = f->conflictList.head;
while (v)
{
b3Vec3 p = v->position;
// Use tolerance and discard internal points.
float32 max = m_tolerance;
qhFace* iMax = NULL;
for (u32 i = 0; i < m_newFaceCount; ++i)
{
qhFace* newFace = m_newFaces[i];
float32 d = b3Distance(p, newFace->plane);
if (d > max)
{
max = d;
iMax = newFace;
}
}
if (iMax)
{
qhVertex* v0 = v;
v->conflictFace = NULL;
v = f->conflictList.Remove(v);
iMax->conflictList.PushFront(v0);
v0->conflictFace = iMax;
}
else
{
qhVertex* v0 = v;
v->conflictFace = NULL;
v = f->conflictList.Remove(v);
FreeVertex(v0);
}
}
// Remove face half-edges.
qhHalfEdge* e = f->edge;
do
{
qhHalfEdge* e0 = e;
e = e->next;
FreeEdge(e0);
} while (e != f->edge);
// Remove face.
qhFace* f0 = f;
f = m_faceList.Remove(f);
FreeFace(f0);
}
} }
qhFace* qhHull::AddTriangle(qhVertex* v1, qhVertex* v2, qhVertex* v3) qhFace* qhHull::AddTriangle(qhVertex* v1, qhVertex* v2, qhVertex* v3)
@ -503,116 +615,6 @@ qhHalfEdge* qhHull::AddAdjoiningTriangle(qhVertex* eye, qhHalfEdge* horizonEdge)
return e1; return e1;
} }
void qhHull::AddNewFaces(b3Array<qhFace*>& newFaces, qhVertex* eye, const b3Array<qhHalfEdge*>& horizon)
{
newFaces.Reserve(horizon.Count());
qhHalfEdge* beginEdge = NULL;
qhHalfEdge* prevEdge = NULL;
{
qhHalfEdge* edge = horizon[0];
qhHalfEdge* leftEdge = AddAdjoiningTriangle(eye, edge);
qhHalfEdge* rightEdge = leftEdge->prev;
prevEdge = rightEdge;
beginEdge = leftEdge;
newFaces.PushBack(leftEdge->face);
}
for (u32 i = 1; i < horizon.Count() - 1; ++i)
{
qhHalfEdge* edge = horizon[i];
qhHalfEdge* leftEdge = AddAdjoiningTriangle(eye, edge);
qhHalfEdge* rightEdge = leftEdge->prev;
leftEdge->twin = prevEdge;
prevEdge->twin = leftEdge;
prevEdge = rightEdge;
newFaces.PushBack(leftEdge->face);
}
{
qhHalfEdge* edge = horizon[horizon.Count() - 1];
qhHalfEdge* leftEdge = AddAdjoiningTriangle(eye, edge);
qhHalfEdge* rightEdge = leftEdge->prev;
leftEdge->twin = prevEdge;
prevEdge->twin = leftEdge;
rightEdge->twin = beginEdge;
beginEdge->twin = rightEdge;
newFaces.PushBack(leftEdge->face);
}
qhFace* f = m_faceList.head;
while (f)
{
if (f->state == qhFace::e_invisible)
{
f = f->next;
continue;
}
// Partition conflict vertices.
qhVertex* v = f->conflictList.head;
while (v)
{
b3Vec3 p = v->position;
// Use tolerance and discard internal points.
float32 max = m_tolerance;
qhFace* iMax = NULL;
for (u32 i = 0; i < newFaces.Count(); ++i)
{
qhFace* newFace = newFaces[i];
float32 d = b3Distance(p, newFace->plane);
if (d > max)
{
max = d;
iMax = newFace;
}
}
if (iMax)
{
qhVertex* v0 = v;
v->conflictFace = NULL;
v = f->conflictList.Remove(v);
iMax->conflictList.PushFront(v0);
v0->conflictFace = iMax;
}
else
{
qhVertex* v0 = v;
v->conflictFace = NULL;
v = f->conflictList.Remove(v);
FreeVertex(v0);
}
}
// Remove face half-edges.
qhHalfEdge* e = f->edge;
do
{
qhHalfEdge* e0 = e;
e = e->next;
FreeEdge(e0);
} while (e != f->edge);
// Remove face.
qhFace* f0 = f;
f = m_faceList.Remove(f);
FreeFace(f0);
}
}
bool qhHull::MergeFace(qhFace* rightFace) bool qhHull::MergeFace(qhFace* rightFace)
{ {
qhHalfEdge* e = rightFace->edge; qhHalfEdge* e = rightFace->edge;
@ -691,22 +693,18 @@ bool qhHull::MergeFace(qhFace* rightFace)
return false; return false;
} }
void qhHull::MergeFaces(b3Array<qhFace*>& newFaces) void qhHull::MergeFaces()
{ {
for (u32 i = 0; i < newFaces.Count(); ++i) for (u32 i = 0; i < m_newFaceCount; ++i)
{ {
qhFace* f = newFaces[i]; qhFace* f = m_newFaces[i];
if (f->state == qhFace::e_deleted) if (f->state == qhFace::e_deleted)
{ {
continue; continue;
} }
// todo // Merge the faces while there is no face left to merge.
// Fix topological and geometrical errors.
// Merge the faces while there is no face left
// to merge.
while (MergeFace(f)); while (MergeFace(f));
} }
} }
@ -754,23 +752,25 @@ void qhHull::Validate() const
void qhHull::Draw() const void qhHull::Draw() const
{ {
b3StackArray<b3Vec3, 256> polygon;
qhFace* face = m_faceList.head; qhFace* face = m_faceList.head;
while (face) while (face)
{ {
polygon.Resize(0);
b3Vec3 c = face->center; b3Vec3 c = face->center;
b3Vec3 n = face->plane.normal; b3Vec3 n = face->plane.normal;
b3StackArray<b3Vec3, 32> vs;
const qhHalfEdge* begin = face->edge; const qhHalfEdge* begin = face->edge;
const qhHalfEdge* edge = begin; const qhHalfEdge* edge = begin;
do do
{ {
vs.PushBack(edge->tail->position); polygon.PushBack(edge->tail->position);
edge = edge->next; edge = edge->next;
} while (edge != begin); } while (edge != begin);
b3Draw_draw->DrawSolidPolygon(n, vs.Begin(), vs.Count(), b3Color(1.0f, 1.0f, 1.0f, 0.5f)); b3Draw_draw->DrawSolidPolygon(n, polygon.Begin(), polygon.Count(), b3Color(1.0f, 1.0f, 1.0f, 0.5f));
qhVertex* v = face->conflictList.head; qhVertex* v = face->conflictList.head;
while (v) while (v)