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. | ||||
| 	// 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. | ||||
|   | ||||
| @@ -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<qhFace>& 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<qhVertex> m_vertexList; | ||||
| 	 | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -17,10 +17,9 @@ | ||||
| */ | ||||
|  | ||||
| #include <bounce/quickhull/qh_hull.h> | ||||
| #include <bounce/common/template/array.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); | ||||
| 	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<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 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)); | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user