2 Copyright (c) 2012 Advanced Micro Devices, Inc.
4 This software is provided 'as-is', without any express or implied warranty.
5 In no event will the authors be held liable for any damages arising from the use of this software.
6 Permission is granted to anyone to use this software for any purpose,
7 including commercial applications, and to alter it and redistribute it freely,
8 subject to the following restrictions:
10 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
11 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
12 3. This notice may not be removed or altered from any source distribution.
14 //Originally written by Erwin Coumans
16 #include "b3ConvexUtility.h"
17 #include "Bullet3Geometry/b3ConvexHullComputer.h"
18 #include "Bullet3Geometry/b3GrahamScan2dConvexHull.h"
19 #include "Bullet3Common/b3Quaternion.h"
20 #include "Bullet3Common/b3HashMap.h"
22 b3ConvexUtility::~b3ConvexUtility()
26 bool b3ConvexUtility::initializePolyhedralFeatures(const b3Vector3* orgVertices, int numPoints, bool mergeCoplanarTriangles)
28 b3ConvexHullComputer conv;
29 conv.compute(&orgVertices[0].getX(), sizeof(b3Vector3), numPoints, 0.f, 0.f);
31 b3AlignedObjectArray<b3Vector3> faceNormals;
32 int numFaces = conv.faces.size();
33 faceNormals.resize(numFaces);
34 b3ConvexHullComputer* convexUtil = &conv;
36 b3AlignedObjectArray<b3MyFace> tmpFaces;
37 tmpFaces.resize(numFaces);
39 int numVertices = convexUtil->vertices.size();
40 m_vertices.resize(numVertices);
41 for (int p = 0; p < numVertices; p++)
43 m_vertices[p] = convexUtil->vertices[p];
46 for (int i = 0; i < numFaces; i++)
48 int face = convexUtil->faces[i];
49 //printf("face=%d\n",face);
50 const b3ConvexHullComputer::Edge* firstEdge = &convexUtil->edges[face];
51 const b3ConvexHullComputer::Edge* edge = firstEdge;
55 //compute face normals
59 int src = edge->getSourceVertex();
60 tmpFaces[i].m_indices.push_back(src);
61 int targ = edge->getTargetVertex();
62 b3Vector3 wa = convexUtil->vertices[src];
64 b3Vector3 wb = convexUtil->vertices[targ];
65 b3Vector3 newEdge = wb - wa;
68 edges[numEdges++] = newEdge;
70 edge = edge->getNextEdgeOfFace();
71 } while (edge != firstEdge);
73 b3Scalar planeEq = 1e30f;
77 faceNormals[i] = edges[0].cross(edges[1]);
78 faceNormals[i].normalize();
79 tmpFaces[i].m_plane[0] = faceNormals[i].getX();
80 tmpFaces[i].m_plane[1] = faceNormals[i].getY();
81 tmpFaces[i].m_plane[2] = faceNormals[i].getZ();
82 tmpFaces[i].m_plane[3] = planeEq;
86 b3Assert(0); //degenerate?
87 faceNormals[i].setZero();
90 for (int v = 0; v < tmpFaces[i].m_indices.size(); v++)
92 b3Scalar eq = m_vertices[tmpFaces[i].m_indices[v]].dot(faceNormals[i]);
98 tmpFaces[i].m_plane[3] = -planeEq;
101 //merge coplanar faces and copy them to m_polyhedron
103 b3Scalar faceWeldThreshold = 0.999f;
104 b3AlignedObjectArray<int> todoFaces;
105 for (int i = 0; i < tmpFaces.size(); i++)
106 todoFaces.push_back(i);
108 while (todoFaces.size())
110 b3AlignedObjectArray<int> coplanarFaceGroup;
111 int refFace = todoFaces[todoFaces.size() - 1];
113 coplanarFaceGroup.push_back(refFace);
114 b3MyFace& faceA = tmpFaces[refFace];
115 todoFaces.pop_back();
117 b3Vector3 faceNormalA = b3MakeVector3(faceA.m_plane[0], faceA.m_plane[1], faceA.m_plane[2]);
118 for (int j = todoFaces.size() - 1; j >= 0; j--)
120 int i = todoFaces[j];
121 b3MyFace& faceB = tmpFaces[i];
122 b3Vector3 faceNormalB = b3MakeVector3(faceB.m_plane[0], faceB.m_plane[1], faceB.m_plane[2]);
123 if (faceNormalA.dot(faceNormalB) > faceWeldThreshold)
125 coplanarFaceGroup.push_back(i);
130 bool did_merge = false;
131 if (coplanarFaceGroup.size() > 1)
133 //do the merge: use Graham Scan 2d convex hull
135 b3AlignedObjectArray<b3GrahamVector3> orgpoints;
136 b3Vector3 averageFaceNormal = b3MakeVector3(0, 0, 0);
138 for (int i = 0; i < coplanarFaceGroup.size(); i++)
140 // m_polyhedron->m_faces.push_back(tmpFaces[coplanarFaceGroup[i]]);
142 b3MyFace& face = tmpFaces[coplanarFaceGroup[i]];
143 b3Vector3 faceNormal = b3MakeVector3(face.m_plane[0], face.m_plane[1], face.m_plane[2]);
144 averageFaceNormal += faceNormal;
145 for (int f = 0; f < face.m_indices.size(); f++)
147 int orgIndex = face.m_indices[f];
148 b3Vector3 pt = m_vertices[orgIndex];
152 for (int i = 0; i < orgpoints.size(); i++)
154 //if ((orgpoints[i].m_orgIndex == orgIndex) || ((rotatedPt-orgpoints[i]).length2()<0.0001))
155 if (orgpoints[i].m_orgIndex == orgIndex)
162 orgpoints.push_back(b3GrahamVector3(pt, orgIndex));
166 b3MyFace combinedFace;
167 for (int i = 0; i < 4; i++)
168 combinedFace.m_plane[i] = tmpFaces[coplanarFaceGroup[0]].m_plane[i];
170 b3AlignedObjectArray<b3GrahamVector3> hull;
172 averageFaceNormal.normalize();
173 b3GrahamScanConvexHull2D(orgpoints, hull, averageFaceNormal);
175 for (int i = 0; i < hull.size(); i++)
177 combinedFace.m_indices.push_back(hull[i].m_orgIndex);
178 for (int k = 0; k < orgpoints.size(); k++)
180 if (orgpoints[k].m_orgIndex == hull[i].m_orgIndex)
182 orgpoints[k].m_orgIndex = -1; // invalidate...
188 // are there rejected vertices?
189 bool reject_merge = false;
191 for (int i = 0; i < orgpoints.size(); i++)
193 if (orgpoints[i].m_orgIndex == -1)
194 continue; // this is in the hull...
195 // this vertex is rejected -- is anybody else using this vertex?
196 for (int j = 0; j < tmpFaces.size(); j++)
198 b3MyFace& face = tmpFaces[j];
199 // is this a face of the current coplanar group?
200 bool is_in_current_group = false;
201 for (int k = 0; k < coplanarFaceGroup.size(); k++)
203 if (coplanarFaceGroup[k] == j)
205 is_in_current_group = true;
209 if (is_in_current_group) // ignore this face...
211 // does this face use this rejected vertex?
212 for (int v = 0; v < face.m_indices.size(); v++)
214 if (face.m_indices[v] == orgpoints[i].m_orgIndex)
216 // this rejected vertex is used in another face -- reject merge
232 m_faces.push_back(combinedFace);
237 for (int i = 0; i < coplanarFaceGroup.size(); i++)
239 b3MyFace face = tmpFaces[coplanarFaceGroup[i]];
240 m_faces.push_back(face);
250 inline bool IsAlmostZero(const b3Vector3& v)
252 if (fabsf(v.getX()) > 1e-6 || fabsf(v.getY()) > 1e-6 || fabsf(v.getZ()) > 1e-6) return false;
256 struct b3InternalVertexPair
258 b3InternalVertexPair(short int v0, short int v1)
269 return m_v0 + (m_v1 << 16);
271 bool equals(const b3InternalVertexPair& other) const
273 return m_v0 == other.m_v0 && m_v1 == other.m_v1;
277 struct b3InternalEdge
290 #ifdef TEST_INTERNAL_OBJECTS
291 bool b3ConvexUtility::testContainment() const
293 for (int p = 0; p < 8; p++)
297 LocalPt = m_localCenter + b3Vector3(m_extents[0], m_extents[1], m_extents[2]);
299 LocalPt = m_localCenter + b3Vector3(m_extents[0], m_extents[1], -m_extents[2]);
301 LocalPt = m_localCenter + b3Vector3(m_extents[0], -m_extents[1], m_extents[2]);
303 LocalPt = m_localCenter + b3Vector3(m_extents[0], -m_extents[1], -m_extents[2]);
305 LocalPt = m_localCenter + b3Vector3(-m_extents[0], m_extents[1], m_extents[2]);
307 LocalPt = m_localCenter + b3Vector3(-m_extents[0], m_extents[1], -m_extents[2]);
309 LocalPt = m_localCenter + b3Vector3(-m_extents[0], -m_extents[1], m_extents[2]);
311 LocalPt = m_localCenter + b3Vector3(-m_extents[0], -m_extents[1], -m_extents[2]);
313 for (int i = 0; i < m_faces.size(); i++)
315 const b3Vector3 Normal(m_faces[i].m_plane[0], m_faces[i].m_plane[1], m_faces[i].m_plane[2]);
316 const b3Scalar d = LocalPt.dot(Normal) + m_faces[i].m_plane[3];
325 void b3ConvexUtility::initialize()
327 b3HashMap<b3InternalVertexPair, b3InternalEdge> edges;
329 b3Scalar TotalArea = 0.0f;
331 m_localCenter.setValue(0, 0, 0);
332 for (int i = 0; i < m_faces.size(); i++)
334 int numVertices = m_faces[i].m_indices.size();
335 int NbTris = numVertices;
336 for (int j = 0; j < NbTris; j++)
338 int k = (j + 1) % numVertices;
339 b3InternalVertexPair vp(m_faces[i].m_indices[j], m_faces[i].m_indices[k]);
340 b3InternalEdge* edptr = edges.find(vp);
341 b3Vector3 edge = m_vertices[vp.m_v1] - m_vertices[vp.m_v0];
345 b3Vector3 diff, diff2;
347 for (int p = 0; p < m_uniqueEdges.size(); p++)
349 diff = m_uniqueEdges[p] - edge;
350 diff2 = m_uniqueEdges[p] + edge;
352 // if ((diff.length2()==0.f) ||
353 // (diff2.length2()==0.f))
355 if (IsAlmostZero(diff) ||
365 m_uniqueEdges.push_back(edge);
370 //TBD: figure out why I added this assert
371 // b3Assert(edptr->m_face0>=0);
372 // b3Assert(edptr->m_face1<0);
379 edges.insert(vp, ed);
384 #ifdef USE_CONNECTED_FACES
385 for (int i = 0; i < m_faces.size(); i++)
387 int numVertices = m_faces[i].m_indices.size();
388 m_faces[i].m_connectedFaces.resize(numVertices);
390 for (int j = 0; j < numVertices; j++)
392 int k = (j + 1) % numVertices;
393 b3InternalVertexPair vp(m_faces[i].m_indices[j], m_faces[i].m_indices[k]);
394 b3InternalEdge* edptr = edges.find(vp);
396 b3Assert(edptr->m_face0 >= 0);
397 b3Assert(edptr->m_face1 >= 0);
399 int connectedFace = (edptr->m_face0 == i) ? edptr->m_face1 : edptr->m_face0;
400 m_faces[i].m_connectedFaces[j] = connectedFace;
403 #endif //USE_CONNECTED_FACES
405 for (int i = 0; i < m_faces.size(); i++)
407 int numVertices = m_faces[i].m_indices.size();
408 int NbTris = numVertices - 2;
410 const b3Vector3& p0 = m_vertices[m_faces[i].m_indices[0]];
411 for (int j = 1; j <= NbTris; j++)
413 int k = (j + 1) % numVertices;
414 const b3Vector3& p1 = m_vertices[m_faces[i].m_indices[j]];
415 const b3Vector3& p2 = m_vertices[m_faces[i].m_indices[k]];
416 b3Scalar Area = ((p0 - p1).cross(p0 - p2)).length() * 0.5f;
417 b3Vector3 Center = (p0 + p1 + p2) / 3.0f;
418 m_localCenter += Area * Center;
422 m_localCenter /= TotalArea;
424 #ifdef TEST_INTERNAL_OBJECTS
428 for (int i = 0; i < m_faces.size(); i++)
430 const b3Vector3 Normal(m_faces[i].m_plane[0], m_faces[i].m_plane[1], m_faces[i].m_plane[2]);
431 const b3Scalar dist = b3Fabs(m_localCenter.dot(Normal) + m_faces[i].m_plane[3]);
436 b3Scalar MinX = FLT_MAX;
437 b3Scalar MinY = FLT_MAX;
438 b3Scalar MinZ = FLT_MAX;
439 b3Scalar MaxX = -FLT_MAX;
440 b3Scalar MaxY = -FLT_MAX;
441 b3Scalar MaxZ = -FLT_MAX;
442 for (int i = 0; i < m_vertices.size(); i++)
444 const b3Vector3& pt = m_vertices[i];
445 if (pt.getX() < MinX) MinX = pt.getX();
446 if (pt.getX() > MaxX) MaxX = pt.getX();
447 if (pt.getY() < MinY) MinY = pt.getY();
448 if (pt.getY() > MaxY) MaxY = pt.getY();
449 if (pt.getZ() < MinZ) MinZ = pt.getZ();
450 if (pt.getZ() > MaxZ) MaxZ = pt.getZ();
452 mC.setValue(MaxX + MinX, MaxY + MinY, MaxZ + MinZ);
453 mE.setValue(MaxX - MinX, MaxY - MinY, MaxZ - MinZ);
455 // const b3Scalar r = m_radius / sqrtf(2.0f);
456 const b3Scalar r = m_radius / sqrtf(3.0f);
457 const int LargestExtent = mE.maxAxis();
458 const b3Scalar Step = (mE[LargestExtent] * 0.5f - r) / 1024.0f;
459 m_extents[0] = m_extents[1] = m_extents[2] = r;
460 m_extents[LargestExtent] = mE[LargestExtent] * 0.5f;
461 bool FoundBox = false;
462 for (int j = 0; j < 1024; j++)
464 if (testContainment())
470 m_extents[LargestExtent] -= Step;
474 m_extents[0] = m_extents[1] = m_extents[2] = r;
479 const b3Scalar Step = (m_radius - r) / 1024.0f;
480 const int e0 = (1 << LargestExtent) & 3;
481 const int e1 = (1 << e0) & 3;
483 for (int j = 0; j < 1024; j++)
485 const b3Scalar Saved0 = m_extents[e0];
486 const b3Scalar Saved1 = m_extents[e1];
487 m_extents[e0] += Step;
488 m_extents[e1] += Step;
490 if (!testContainment())
492 m_extents[e0] = Saved0;
493 m_extents[e1] = Saved1;