Skip to content

Commit 90062ae

Browse files
Working on Quickhull algorithm
1 parent 8492f88 commit 90062ae

File tree

9 files changed

+203
-266
lines changed

9 files changed

+203
-266
lines changed

include/reactphysics3d/collision/ConvexMesh.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ class ConvexMesh {
8787
/// Compute the faces normals
8888
bool computeFacesNormals(std::vector<Error>& errors);
8989

90-
/// Compute and return the area of a face
91-
decimal getFaceArea(uint32 faceIndex) const;
90+
/// Compute and return the face normal (not normalized)
91+
Vector3 computeFaceNormal(uint32 faceIndex) const;
9292

9393
/// Static factory method to create a convex mesh
9494
static ConvexMesh* create(MemoryAllocator& allocator);

include/reactphysics3d/collision/HalfEdgeStructure.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class HalfEdgeStructure {
5959
Face(MemoryAllocator& allocator) : edgeIndex(0), faceVertices(allocator) {}
6060

6161
/// Constructor
62-
Face(const Array<uint32>& vertices) : edgeIndex(0), faceVertices(vertices) {}
62+
Face(const Array<uint32>& vertices) : edgeIndex(0), faceVertices(vertices) {}
6363
};
6464

6565
/// Vertex

include/reactphysics3d/utils/quickhull/QHHalfEdgeStructure.h

Lines changed: 45 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,15 @@ class QHHalfEdgeStructure {
9696
Edge* edge; // One half-edge of the face
9797
Vector3 normal;
9898
Vector3 centroid; // Center of the face (average of the face vertices)
99+
decimal area; // Area of the face
99100
Array<uint32> conflictPoints; // Array with some remaining points visible from this face that need to be processed
100101

101102
// TODO : DELETE THIS
102103
Set<uint32> vertices;
103104

104105
/// Constructor
105-
Face(const Vector3& normal, MemoryAllocator& allocator)
106-
: nextFace(nullptr), previousFace(nullptr), normal(normal), conflictPoints(allocator, 8), vertices(allocator) {
106+
Face(MemoryAllocator& allocator)
107+
: nextFace(nullptr), previousFace(nullptr), normal(0, 0, 0), area(0), conflictPoints(allocator, 8), vertices(allocator) {
107108

108109
}
109110

@@ -112,6 +113,47 @@ class QHHalfEdgeStructure {
112113
return edge->startVertex;
113114
}
114115

116+
// Recalculate the face centroid and normal to better fit its new vertices (using Newell method)
117+
void recalculateFace(const Array<Vector3>& points) {
118+
119+
centroid.setToZero();
120+
normal.setToZero();
121+
uint32 nbVertices = 0;
122+
123+
//std::cout << "Recalculate Face" << std::endl;
124+
//std::cout << "Previous centroid:" << face->centroid.to_string() << std::endl;
125+
//std::cout << "Previous normal:" << face->normal.to_string() << std::endl;
126+
127+
// For each vertex of the face
128+
const QHHalfEdgeStructure::Edge* firstFaceEdge = edge;
129+
const QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge;
130+
do {
131+
132+
const Vector3 v1 = points[faceEdge->startVertex->externalIndex];
133+
const Vector3 v2 = points[faceEdge->endVertex->externalIndex];
134+
centroid += v1;
135+
normal += Vector3((v1.y - v2.y) * (v1.z + v2.z),
136+
(v1.z - v2.z) * (v1.x + v2.x),
137+
(v1.x - v2.x) * (v1.y + v2.y));
138+
139+
nbVertices++;
140+
141+
faceEdge = faceEdge->nextFaceEdge;
142+
143+
} while(faceEdge != firstFaceEdge);
144+
145+
assert(nbVertices > 0);
146+
147+
centroid = centroid / nbVertices;
148+
const decimal normalLength = normal.length();
149+
assert(normalLength > 0);
150+
normal = normal / normalLength;
151+
area = normalLength * decimal(0.5);
152+
153+
//std::cout << "New centroid:" << face->centroid.to_string() << std::endl;
154+
//std::cout << "New normal:" << face->normal.to_string() << std::endl;
155+
}
156+
115157
// Return a string with the vertices of the face
116158
std::string verticesString() const {
117159

@@ -161,54 +203,6 @@ class QHHalfEdgeStructure {
161203
return edge->nextFaceEdge->nextFaceEdge->nextFaceEdge == edge;
162204
}
163205

164-
// TODO: DELETE THIS
165-
decimal computeArea(const Array<Vector3>& points) const {
166-
167-
decimal area = 0.0;
168-
169-
Vector3 v1 = points[edge->startVertex->externalIndex];
170-
171-
// For each vertex of the face
172-
const QHHalfEdgeStructure::Edge* firstFaceEdge = edge;
173-
const QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge->nextFaceEdge;
174-
do {
175-
176-
Vector3 v2 = points[faceEdge->startVertex->externalIndex];
177-
Vector3 v3 = points[faceEdge->endVertex->externalIndex];
178-
179-
area += (v3 - v1).cross(v2 - v1).length();
180-
181-
faceEdge = faceEdge->nextFaceEdge;
182-
183-
} while(faceEdge->nextFaceEdge != firstFaceEdge);
184-
185-
return area * decimal(0.5);
186-
}
187-
188-
// Compute the centroid of a face (the average of face vertices)
189-
void computeCentroid(const Array<Vector3>& points) {
190-
191-
decimal nbVertices = 0;
192-
193-
// For each vertex of the face
194-
const QHHalfEdgeStructure::Edge* firstFaceEdge = edge;
195-
const QHHalfEdgeStructure::Edge* faceEdge = firstFaceEdge;
196-
do {
197-
198-
QHHalfEdgeStructure::Vertex* vertex = faceEdge->startVertex;
199-
200-
centroid += points[vertex->externalIndex];
201-
202-
faceEdge = faceEdge->nextFaceEdge;
203-
204-
nbVertices++;
205-
206-
} while(faceEdge != firstFaceEdge);
207-
208-
assert(nbVertices > 0);
209-
centroid /= nbVertices;
210-
}
211-
212206
// Return true if the face structure is valid (for debugging purpose)
213207
bool isValid() {
214208
bool isValid = true;
@@ -313,7 +307,7 @@ class QHHalfEdgeStructure {
313307
Vertex* addVertex(uint32 externalIndex);
314308

315309
/// Add a face
316-
Face* addFace(const Array<Vertex*>& faceVertices, const Vector3& normal, const Array<Vector3>& points, MemoryAllocator& allocator);
310+
Face* addFace(const Array<Vertex*>& faceVertices, const Array<Vector3>& points, MemoryAllocator& allocator);
317311

318312
/// Remove a face
319313
void removeFace(Face* face);

include/reactphysics3d/utils/quickhull/QuickHull.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@ class QuickHull {
106106
Array<QHHalfEdgeStructure::Face*>& outVisibleFaces,
107107
decimal epsilon);
108108

109-
/// Iterate over all new faces and fix faces that are forming a concave shape in order to always keep the hull convex
109+
/// Fix faces that are forming a concave or coplanar shape (by giving priority to large faces)
110+
static void mergeLargeConcaveFaces(QHHalfEdgeStructure& convexHull, Array<QHHalfEdgeStructure::Face*>& newFaces,
111+
const Array<Vector3>& points, decimal epsilon, Set<QHHalfEdgeStructure::Face*>& deletedFaces);
112+
113+
/// Fix faces that are forming a concave or coplanar shape
110114
static void mergeConcaveFaces(QHHalfEdgeStructure& convexHull, Array<QHHalfEdgeStructure::Face*>& newFaces,
111115
const Array<Vector3>& points, decimal epsilon,
112116
Set<QHHalfEdgeStructure::Face*>& deletedFaces);
@@ -124,9 +128,6 @@ class QuickHull {
124128
QHHalfEdgeStructure::Edge* inEdge, const Array<Vector3>& points,
125129
Set<QHHalfEdgeStructure::Face*>& deletedFaces);
126130

127-
/// Recalculate the face centroid and normal to better fit its new vertices (using Newell method)
128-
static void recalculateFace(QHHalfEdgeStructure::Face* face, const Array<Vector3>& points);
129-
130131
/// Remove duplicated vertices in the input array of points
131132
static void removeDuplicatedVertices(Array<Vector3>& points, MemoryAllocator& allocator);
132133

@@ -161,10 +162,6 @@ class QuickHull {
161162
Array<PolygonVertexArray::PolygonFace>& outFaces,
162163
MemoryAllocator& allocator);
163164

164-
// TODO : Remove this
165-
static std::string showMap(Map<const QHHalfEdgeStructure::Face*, Array<uint32>>& mapFaceIndexToRemainingClosestPoints);
166-
167-
168165
public:
169166

170167
// -------------------- Methods -------------------- //

src/collision/ConvexMesh.cpp

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,22 @@ bool ConvexMesh::copyVertices(const PolygonVertexArray& polygonVertexArray, std:
8282
bool isValid = true;
8383

8484
mCentroid.setToZero();
85-
mMinBounds = polygonVertexArray.getVertex(0);
86-
mMaxBounds = polygonVertexArray.getVertex(0);
8785

86+
if (polygonVertexArray.getNbVertices() > 0) {
87+
mMinBounds = polygonVertexArray.getVertex(0);
88+
mMaxBounds = polygonVertexArray.getVertex(0);
89+
}
90+
91+
// For each vertex
8892
for (uint32 i=0 ; i < polygonVertexArray.getNbVertices(); i++) {
8993

9094
const Vector3 vertex = polygonVertexArray.getVertex(i);
9195
mVertices.add(vertex);
96+
97+
// Compute centroid
9298
mCentroid += vertex;
9399

100+
// Compute mesh bounds
94101
if (vertex.x > mMaxBounds.x) mMaxBounds.x = vertex.x;
95102
if (vertex.x < mMinBounds.x) mMinBounds.x = vertex.x;
96103

@@ -199,14 +206,29 @@ bool ConvexMesh::computeFacesNormals(std::vector<Error>& errors) {
199206

200207
if (face.faceVertices.size() >= 3) {
201208

202-
const Vector3 vec1 = getVertex(face.faceVertices[1]) - getVertex(face.faceVertices[0]);
203-
const Vector3 vec2 = getVertex(face.faceVertices[2]) - getVertex(face.faceVertices[0]);
204-
mFacesNormals.add(vec1.cross(vec2));
209+
auto test0 = face.faceVertices[0];
210+
auto test1 = face.faceVertices[1];
211+
auto test2 = face.faceVertices[2];
212+
213+
auto v0 = getVertex(face.faceVertices[0]);
214+
auto v1 = getVertex(face.faceVertices[1]);
215+
auto v2 = getVertex(face.faceVertices[2]);
216+
auto v3 = Vector3(0, 0, 0);
217+
if (face.faceVertices.size() > 3) {
218+
219+
v3 = getVertex(face.faceVertices[3]);
220+
}
221+
222+
mFacesNormals.add(computeFaceNormal(f));
223+
decimal normalLength = mFacesNormals[f].length();
224+
225+
// TODO : DELETE THIS
226+
const decimal abc = mFacesNormals[f].length();
205227

206228
// TODO : Do not use MACHINE_EPSILON here
207-
if (mFacesNormals[f].lengthSquare() > MACHINE_EPSILON) {
229+
if (normalLength > MACHINE_EPSILON) {
208230

209-
mFacesNormals[f].normalize();
231+
mFacesNormals[f] /= normalLength;
210232
}
211233
else {
212234
isValid = false;
@@ -218,30 +240,26 @@ bool ConvexMesh::computeFacesNormals(std::vector<Error>& errors) {
218240
return isValid;
219241
}
220242

221-
// Compute and return the area of a face
222-
decimal ConvexMesh::getFaceArea(uint32 faceIndex) const {
243+
// Compute and return the face normal (not normalized)
244+
Vector3 ConvexMesh::computeFaceNormal(uint32 faceIndex) const {
223245

224-
Vector3 sumCrossProducts(0, 0, 0);
246+
Vector3 normal(0, 0, 0);
225247

226248
const HalfEdgeStructure::Face& face = mHalfEdgeStructure.getFace(faceIndex);
227249
assert(face.faceVertices.size() >= 3);
228250

229-
const Vector3& v1 = getVertex(face.faceVertices[0]);
230-
231-
// For each vertex of the face
232-
const uint32 nbFaceVertices = static_cast<uint32>(face.faceVertices.size());
233-
for (uint32 i=2; i < nbFaceVertices; i++) {
234-
235-
const Vector3& v2 = getVertex(face.faceVertices[i-1]);
236-
const Vector3& v3 = getVertex(face.faceVertices[i]);
251+
// Use Newell's method to compute the face normal
252+
for (uint32 i = face.faceVertices.size() - 1, j = 0; j < face.faceVertices.size(); i = j, j++) {
237253

238-
const Vector3 v1v2 = v2 - v1;
239-
const Vector3 v1v3 = v3 - v1;
254+
const Vector3& v1 = getVertex(face.faceVertices[i]);
255+
const Vector3& v2 = getVertex(face.faceVertices[j]);
240256

241-
sumCrossProducts += v1v2.cross(v1v3);
257+
normal += Vector3((v1.y - v2.y) * (v1.z + v2.z),
258+
(v1.z - v2.z) * (v1.x + v2.x),
259+
(v1.x - v2.x) * (v1.y + v2.y));
242260
}
243261

244-
return decimal(0.5) * sumCrossProducts.length();
262+
return normal;
245263
}
246264

247265
// Compute and return the volume of the convex mesh
@@ -254,7 +272,7 @@ decimal ConvexMesh::getVolume() const {
254272
for (uint32 f=0; f < getNbFaces(); f++) {
255273

256274
const HalfEdgeStructure::Face& face = mHalfEdgeStructure.getFace(f);
257-
const decimal faceArea = getFaceArea(f);
275+
const decimal faceArea = computeFaceNormal(f).length();
258276
const Vector3 faceNormal = mFacesNormals[f];
259277
const Vector3& faceVertex = getVertex(face.faceVertices[0]);
260278

src/utils/quickhull/QHHalfEdgeStructure.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,13 @@ QHHalfEdgeStructure::Vertex* QHHalfEdgeStructure::addVertex(uint32 externalIndex
9696
* @param faceVertices Array of the vertices in a face (ordered in CCW order as seen from outside
9797
* the polyhedron). The indices are the internal indices of the vertices inside the HalfEdgeStructure.
9898
*/
99-
QHHalfEdgeStructure::Face* QHHalfEdgeStructure::addFace(const Array<Vertex*>& faceVertices, const Vector3& normal, const Array<Vector3>& points,
99+
QHHalfEdgeStructure::Face* QHHalfEdgeStructure::addFace(const Array<Vertex*>& faceVertices, const Array<Vector3>& points,
100100
MemoryAllocator& allocator) {
101101

102102
assert(faceVertices.size() >= 3);
103103

104104
// Create a new face
105-
Face* face = new (mAllocator.allocate(sizeof(Face))) Face(normal, allocator);
105+
Face* face = new (mAllocator.allocate(sizeof(Face))) Face(allocator);
106106

107107
Edge* prevFaceEdge = nullptr;
108108
Edge* firstFaceEdge = nullptr;
@@ -161,7 +161,8 @@ QHHalfEdgeStructure::Face* QHHalfEdgeStructure::addFace(const Array<Vertex*>& fa
161161

162162
mNbFaces++;
163163

164-
face->computeCentroid(points);
164+
// Compute the normal, area and centroid
165+
face->recalculateFace(points);
165166

166167
return face;
167168
}
@@ -329,6 +330,7 @@ bool QHHalfEdgeStructure::isValid() const {
329330
isValid &= (face->previousFace == nullptr || face->previousFace == previousFace);
330331
isValid &= (previousFace == nullptr || previousFace->nextFace == face);
331332
isValid &= face->isValid();
333+
isValid &= face->area > 0.00001;
332334
nbFaces++;
333335
};
334336
isValid &= nbFaces > 0 || mFaces == nullptr;

0 commit comments

Comments
 (0)