diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/PhysXCooking/src/convex/ConvexPolygonsBuilder.cpp | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/PhysXCooking/src/convex/ConvexPolygonsBuilder.cpp')
| -rw-r--r-- | PhysX_3.4/Source/PhysXCooking/src/convex/ConvexPolygonsBuilder.cpp | 1328 |
1 files changed, 1328 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/PhysXCooking/src/convex/ConvexPolygonsBuilder.cpp b/PhysX_3.4/Source/PhysXCooking/src/convex/ConvexPolygonsBuilder.cpp new file mode 100644 index 00000000..44725819 --- /dev/null +++ b/PhysX_3.4/Source/PhysXCooking/src/convex/ConvexPolygonsBuilder.cpp @@ -0,0 +1,1328 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "foundation/PxMemory.h" +#include "EdgeList.h" +#include "Adjacencies.h" +#include "MeshCleaner.h" +#include "CmRadixSortBuffered.h" +#include "CookingUtils.h" +#include "PsArray.h" +#include "PsFoundation.h" + +#include "ConvexPolygonsBuilder.h" + + +using namespace physx; + +#define USE_PRECOMPUTED_HULL_PROJECTION + +static PX_INLINE void Flip(HullTriangleData& data) +{ + PxU32 tmp = data.mRef[2]; + data.mRef[2] = data.mRef[1]; + data.mRef[1] = tmp; +} + +////////////////////////////////////////////////////////////////////////// +//! A generic couple structure +class Pair : public Ps::UserAllocated +{ +public: + PX_FORCE_INLINE Pair() {} + PX_FORCE_INLINE Pair(PxU32 i0, PxU32 i1) : id0(i0), id1(i1) {} + PX_FORCE_INLINE ~Pair() {} + + //! Operator for "if(Pair==Pair)" + PX_FORCE_INLINE bool operator==(const Pair& p) const { return (id0==p.id0) && (id1==p.id1); } + //! Operator for "if(Pair!=Pair)" + PX_FORCE_INLINE bool operator!=(const Pair& p) const { return (id0!=p.id0) || (id1!=p.id1); } + + PxU32 id0; //!< First index of the pair + PxU32 id1; //!< Second index of the pair +}; +PX_COMPILE_TIME_ASSERT(sizeof(Pair)==8); + +////////////////////////////////////////////////////////////////////////// +// construct a plane +template <class T> +PX_INLINE PxPlane PlaneEquation(const T& t, const PxVec3* verts) +{ + const PxVec3& p0 = verts[t.v[0]]; + const PxVec3& p1 = verts[t.v[1]]; + const PxVec3& p2 = verts[t.v[2]]; + return PxPlane(p0, p1, p2); +} + +////////////////////////////////////////////////////////////////////////// +// negate plane +static PX_FORCE_INLINE void negatePlane(Gu::HullPolygonData& data) +{ + data.mPlane.n = -data.mPlane.n; + data.mPlane.d = -data.mPlane.d; +} + +////////////////////////////////////////////////////////////////////////// +// Inverse a buffer in-place +static bool inverseBuffer(PxU32 nbEntries, PxU8* entries) +{ + if(!nbEntries || !entries) return false; + + for(PxU32 i=0; i < (nbEntries>>1); i++) + Ps::swap(entries[i], entries[nbEntries-1-i]); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// Extracts a line-strip from a list of non-sorted line-segments (slow) +static bool findLineStrip(Ps::Array<PxU32>& lineStrip, const Ps::Array<Pair>& lineSegments) +{ + // Ex: + // + // 4-2 + // 0-1 + // 2-3 + // 4-0 + // 7-3 + // 7-1 + // + // => 0-1-7-3-2-4-0 + + // 0-0-1-1-2-2-3-3-4-4-7-7 + + // 0-1 + // 0-4 + // 1-7 + // 2-3 + // 2-4 + // 3-7 + + // Naive implementation below + + Ps::Array<Pair> Copy(lineSegments); + +RunAgain: + { + PxU32 nbSegments = Copy.size(); + for(PxU32 j=0;j<nbSegments;j++) + { + PxU32 ID0 = Copy[j].id0; + PxU32 ID1 = Copy[j].id1; + + for(PxU32 i=j+1;i<nbSegments;i++) + { + if( + (Copy[i].id0==ID0 && Copy[i].id1==ID1) + || (Copy[i].id1==ID0 && Copy[i].id0==ID1) + ) + { + // Duplicate segment found => remove both + PX_ASSERT(Copy.size()>=2); + Copy.remove(i); + Copy.remove(j); + goto RunAgain; + } + } + } + // Goes through when everything's fine + } + + PxU32 ref0 = 0xffffffff; + PxU32 ref1 = 0xffffffff; + if(Copy.size()>=1) + { + Pair* Segments = Copy.begin(); + if(Segments) + { + ref0 = Segments->id0; + ref1 = Segments->id1; + lineStrip.pushBack(ref0); + lineStrip.pushBack(ref1); + PX_ASSERT(Copy.size()>=1); + Copy.remove(0); + } + } + +Wrap: + // Look for same vertex ref in remaining segments + PxU32 nb = Copy.size(); + if(!nb) + { + // ### check the line is actually closed? + return true; + } + + for(PxU32 i=0;i<nb;i++) + { + PxU32 newRef0 = Copy[i].id0; + PxU32 newRef1 = Copy[i].id1; + + // We look for Ref1 only + if(newRef0==ref1) + { + // r0 - r1 + // r1 - x + lineStrip.pushBack(newRef1); // Output the other reference + ref0 = newRef0; + ref1 = newRef1; + Copy.remove(i); + goto Wrap; + } + else if(newRef1==ref1) + { + // r0 - r1 + // x - r1 => r1 - x + lineStrip.pushBack(newRef0); // Output the other reference + ref0 = newRef1; + ref1 = newRef0; + Copy.remove(i); + goto Wrap; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Test for duplicate triangles +PX_COMPILE_TIME_ASSERT(sizeof(Gu::TriangleT<PxU32>)==sizeof(PxVec3)); // ... +static bool TestDuplicateTriangles(PxU32& nbFaces, Gu::TriangleT<PxU32>* faces, bool repair) +{ + if(!nbFaces || !faces) + return true; + + Gu::TriangleT<PxU32>* indices32 = reinterpret_cast<Gu::TriangleT<PxU32>*>(PxAlloca(nbFaces*sizeof(Gu::TriangleT<PxU32>))); + for(PxU32 i=0;i<nbFaces;i++) + { + indices32[i].v[0] = faces[i].v[0]; + indices32[i].v[1] = faces[i].v[1]; + indices32[i].v[2] = faces[i].v[2]; + } + + // Radix-sort power... + ReducedVertexCloud reducer(reinterpret_cast<PxVec3*>(indices32), nbFaces); + REDUCEDCLOUD rc; + reducer.Reduce(&rc); + if(rc.NbRVerts<nbFaces) + { + if(repair) + { + nbFaces = rc.NbRVerts; + for(PxU32 i=0;i<nbFaces;i++) + { + const Gu::TriangleT<PxU32>* curTri = reinterpret_cast<const Gu::TriangleT<PxU32>*>(&rc.RVerts[i]); + faces[i].v[0] = curTri->v[0]; + faces[i].v[1] = curTri->v[1]; + faces[i].v[2] = curTri->v[2]; + } + } + return false; // Test failed + } + return true; // Test succeeded +} + +////////////////////////////////////////////////////////////////////////// +// plane culling test +static PX_FORCE_INLINE bool testCulling(const Gu::TriangleT<PxU32>& triangle, const PxVec3* verts, const PxVec3& center) +{ + const PxPlane plane(verts[triangle.v[0]], verts[triangle.v[1]], verts[triangle.v[2]]); + return plane.distance(center)>0.0f; +} + +////////////////////////////////////////////////////////////////////////// +// face normals test +static bool TestUnifiedNormals(PxU32 nbVerts, const PxVec3* verts, PxU32 nbFaces, Gu::TriangleT<PxU32>* faces, bool repair) +{ + if(!nbVerts || !verts || !nbFaces || !faces) + return false; + + // Unify normals so that all hull faces are well oriented + + // Compute geometric center - we need a vertex inside the hull + const float coeff = 1.0f / float(nbVerts); + PxVec3 geomCenter(0.0f, 0.0f, 0.0f); + for(PxU32 i=0;i<nbVerts;i++) + { + geomCenter.x += verts[i].x * coeff; + geomCenter.y += verts[i].y * coeff; + geomCenter.z += verts[i].z * coeff; + } + + // We know the hull is (hopefully) convex so we can easily test whether a point is inside the hull or not. + // The previous geometric center must be invisible from any hull face: that's our test to decide whether a normal + // must be flipped or not. + bool status = true; + for(PxU32 i=0;i<nbFaces;i++) + { + // Test face visibility from the geometric center (supposed to be inside the hull). + // All faces must be invisible from this point to ensure a strict CCW order. + if(testCulling(faces[i], verts, geomCenter)) + { + if(repair) faces[i].flip(); + status = false; + } + } + + return status; +} + +////////////////////////////////////////////////////////////////////////// +// clean the mesh +static bool CleanFaces(PxU32& nbFaces, Gu::TriangleT<PxU32>* faces, PxU32& nbVerts, PxVec3* verts) +{ + // Brute force mesh cleaning. + // PT: I added this back on Feb-18-05 because it fixes bugs with hulls from QHull. + MeshCleaner cleaner(nbVerts, verts, nbFaces, faces->v, 0.0f); + if (!cleaner.mNbTris) + return false; + + nbVerts = cleaner.mNbVerts; + nbFaces = cleaner.mNbTris; + + PxMemCopy(verts, cleaner.mVerts, cleaner.mNbVerts*sizeof(PxVec3)); + + for (PxU32 i = 0; i < cleaner.mNbTris; i++) + { + faces[i].v[0] = cleaner.mIndices[i * 3 + 0]; + faces[i].v[1] = cleaner.mIndices[i * 3 + 1]; + faces[i].v[2] = cleaner.mIndices[i * 3 + 2]; + } + + // Get rid of duplicates + TestDuplicateTriangles(nbFaces, faces, true); + + // Unify normals + TestUnifiedNormals(nbVerts, verts, nbFaces, faces, true); + + // Remove zero-area triangles + // TestZeroAreaTriangles(nbFaces, faces, verts, true); + + // Unify normals again + TestUnifiedNormals(nbVerts, verts, nbFaces, faces, true); + + // Get rid of duplicates again + TestDuplicateTriangles(nbFaces, faces, true); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// check the newly constructed faces +static bool CheckFaces(PxU32 nbFaces, const Gu::TriangleT<PxU32>* faces, PxU32 nbVerts, const PxVec3* verts) +{ + // Remove const since we use functions that can do both testing & repairing. But we won't change the data. + Gu::TriangleT<PxU32>* f = const_cast<Gu::TriangleT<PxU32>*>(faces); + + // Test duplicate faces + if(!TestDuplicateTriangles(nbFaces, f, false)) + return false; + + // Test unified normals + if(!TestUnifiedNormals(nbVerts, verts, nbFaces, f, false)) + return false; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// compute the newell plane from the face verts +static bool computeNewellPlane(PxPlane& plane, PxU32 nbVerts, const PxU8* indices, const PxVec3* verts) +{ + if(!nbVerts || !indices || !verts) + return false; + + PxVec3 centroid(0,0,0), normal(0,0,0); + for(PxU32 i=nbVerts-1, j=0; j<nbVerts; i=j, j++) + { + normal.x += (verts[indices[i]].y - verts[indices[j]].y) * (verts[indices[i]].z + verts[indices[j]].z); + normal.y += (verts[indices[i]].z - verts[indices[j]].z) * (verts[indices[i]].x + verts[indices[j]].x); + normal.z += (verts[indices[i]].x - verts[indices[j]].x) * (verts[indices[i]].y + verts[indices[j]].y); + centroid += verts[indices[j]]; + } + plane.n = normal; + plane.n.normalize(); + plane.d = -(centroid.dot(plane.n))/float(nbVerts); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Analyses a redundant vertices and splits the polygons if necessary. +* \relates ConvexHull +* \fn extractHullPolygons(Container& polygon_data, const ConvexHull& hull) +* \param nb_polygons [out] number of extracted polygons +* \param polygon_data [out] polygon data: (Nb indices, index 0, index 1... index N)(Nb indices, index 0, index 1... index N)(...) +* \param hull [in] convex hull +* \param redundantVertices [out] redundant vertices found inside the polygons - we want to remove them because of PCM +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static void checkRedundantVertices(PxU32& nb_polygons, Ps::Array<PxU32>& polygon_data, const ConvexPolygonsBuilder& hull, Ps::Array<PxU32>& triangle_data, Ps::Array<PxU32>& redundantVertices) +{ + const PxU32* dFaces = reinterpret_cast<const PxU32*>(hull.getFaces()); + bool needToSplitPolygons = false; + + bool* polygonMarkers = reinterpret_cast<bool*>(PxAlloca(nb_polygons*sizeof(bool))); + PxMemZero(polygonMarkers, nb_polygons*sizeof(bool)); + + bool* redundancyMarkers = reinterpret_cast<bool*>(PxAlloca(redundantVertices.size()*sizeof(bool))); + PxMemZero(redundancyMarkers, redundantVertices.size()*sizeof(bool)); + + // parse through the redundant vertices and if we cannot remove them split just the actual polygon if possible + Ps::Array<PxU32> polygonsContainer; + PxU32 numEntries = 0; + for (PxU32 i = redundantVertices.size(); i--;) + { + numEntries = 0; + polygonsContainer.clear(); + // go through polygons, if polygons does have only 3 verts we cannot remove any vertex from it, try to decompose the second one + PxU32* Data = polygon_data.begin(); + for(PxU32 t=0;t<nb_polygons;t++) + { + PxU32 nbVerts = *Data++; + PX_ASSERT(nbVerts>=3); // Else something very wrong happened... + + for(PxU32 j=0;j<nbVerts;j++) + { + if(redundantVertices[i] == Data[j]) + { + polygonsContainer.pushBack(t); + polygonsContainer.pushBack(nbVerts); + numEntries++; + break; + } + } + Data += nbVerts; + } + + bool needToSplit = false; + for (PxU32 j = 0; j < numEntries; j++) + { + PxU32 numInternalVertices = polygonsContainer[j*2 + 1]; + if(numInternalVertices == 3) + { + needToSplit = true; + } + } + + // now lets mark the polygons for split + if(needToSplit) + { + // mark the redundant vertex, it is solved by spliting, dont report it + needToSplitPolygons = true; + redundancyMarkers[i] = true; + for (PxU32 j = 0; j < numEntries; j++) + { + PxU32 polygonNumber = polygonsContainer[j*2]; + PxU32 numInternalPolygons = polygonsContainer[j*2 + 1]; + if(numInternalPolygons != 3) + { + polygonMarkers[polygonNumber] = true; + } + } + } + } + + if(needToSplitPolygons) + { + // parse from the end so we can remove it and not change the order + for (PxU32 i = redundantVertices.size(); i--;) + { + // remove it + if(redundancyMarkers[i]) + { + redundantVertices.remove(i); + } + } + + Ps::Array<PxU32> newPolygon_data; + Ps::Array<PxU32> newTriangle_data; + PxU32 newNb_polygons = 0; + + PxU32* data = polygon_data.begin(); + PxU32* triData = triangle_data.begin(); + for(PxU32 i=0;i<nb_polygons;i++) + { + PxU32 nbVerts = *data++; + PxU32 nbTris = *triData++; + if(polygonMarkers[i]) + { + // split the polygon into triangles + for(PxU32 k=0;k< nbTris; k++) + { + newNb_polygons++; + const PxU32 faceIndex = triData[k]; + newPolygon_data.pushBack(PxU32(3)); + newPolygon_data.pushBack(dFaces[3*faceIndex]); + newPolygon_data.pushBack(dFaces[3*faceIndex + 1]); + newPolygon_data.pushBack(dFaces[3*faceIndex + 2]); + newTriangle_data.pushBack(PxU32(1)); + newTriangle_data.pushBack(faceIndex); + } + } + else + { + newNb_polygons++; + // copy the original polygon + newPolygon_data.pushBack(nbVerts); + for(PxU32 j=0;j<nbVerts;j++) + newPolygon_data.pushBack(data[j]); + + // copy the original polygon triangles + newTriangle_data.pushBack(nbTris); + for(PxU32 k=0;k< nbTris; k++) + { + newTriangle_data.pushBack(triData[k]); + } + } + data += nbVerts; + triData += nbTris; + } + + // now put the data to output + polygon_data.clear(); + triangle_data.clear(); + + // the copy does copy even the data + polygon_data = newPolygon_data; + triangle_data = newTriangle_data; + nb_polygons = newNb_polygons; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Analyses a convex hull made of triangles and extracts polygon data out of it. +* \relates ConvexHull +* \fn extractHullPolygons(Ps::Array<PxU32>& polygon_data, const ConvexHull& hull) +* \param nb_polygons [out] number of extracted polygons +* \param polygon_data [out] polygon data: (Nb indices, index 0, index 1... index N)(Nb indices, index 0, index 1... index N)(...) +* \param hull [in] convex hull +* \param triangle_data [out] triangle data +* \param rendundantVertices [out] redundant vertices found inside the polygons - we want to remove them because of PCM +* \return true if success +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static bool extractHullPolygons(PxU32& nb_polygons, Ps::Array<PxU32>& polygon_data, const ConvexPolygonsBuilder& hull, Ps::Array<PxU32>* triangle_data, Ps::Array<PxU32>& rendundantVertices) +{ + PxU32 nbFaces = hull.getNbFaces(); + const PxVec3* hullVerts = hull.mHullDataHullVertices; + const PxU32 nbVertices = hull.mHull->mNbHullVertices; + + const PxU16* wFaces = NULL; + const PxU32* dFaces = reinterpret_cast<const PxU32*>(hull.getFaces()); + PX_ASSERT(wFaces || dFaces); + + ADJACENCIESCREATE create; + create.NbFaces = nbFaces; + create.DFaces = dFaces; + create.WFaces = wFaces; + create.Verts = hullVerts; + //Create.Epsilon = 0.01f; // PT: trying to fix Rob Elam bug. Also fixes TTP 2467 + // Create.Epsilon = 0.001f; // PT: for "Bruno's bug" + create.Epsilon = 0.005f; // PT: middle-ground seems to fix both. Expose this param? + + + AdjacenciesBuilder adj; + if(!adj.Init(create)) return false; + + PxU32 nbBoundaryEdges = adj.ComputeNbBoundaryEdges(); + if(nbBoundaryEdges) return false; // A valid hull shouldn't have open edges!! + + bool* markers = reinterpret_cast<bool*>(PxAlloca(nbFaces*sizeof(bool))); + PxMemZero(markers, nbFaces*sizeof(bool)); + + PxU8* vertexMarkers = reinterpret_cast<PxU8*>(PxAlloca(nbVertices*sizeof(PxU8))); + PxMemZero(vertexMarkers, nbVertices*sizeof(PxU8)); + + PxU32 currentFace = 0; // Start with first triangle + nb_polygons = 0; + do + { + currentFace = 0; + while(currentFace<nbFaces && markers[currentFace]) currentFace++; + + // Start from "closest" face and floodfill through inactive edges + struct Local + { + static void FloodFill(Ps::Array<PxU32>& indices, const AdjTriangle* faces, PxU32 current, bool* inMarkers) + { + if(inMarkers[current]) return; + inMarkers[current] = true; + + indices.pushBack(current); + const AdjTriangle& AT = faces[current]; + + // We can floodfill through inactive edges since the mesh is convex (inactive==planar) + if(!AT.HasActiveEdge01()) FloodFill(indices, faces, AT.GetAdjTri(EDGE01), inMarkers); + if(!AT.HasActiveEdge20()) FloodFill(indices, faces, AT.GetAdjTri(EDGE02), inMarkers); + if(!AT.HasActiveEdge12()) FloodFill(indices, faces, AT.GetAdjTri(EDGE12), inMarkers); + } + + static bool GetNeighborFace(PxU32 index,PxU32 triangleIndex,const AdjTriangle* faces, const PxU32* dfaces, PxU32& neighbor, PxU32& current) + { + PxU32 currentIndex = index; + PxU32 previousIndex = index; + bool firstFace = true; + bool next = true; + while (next) + { + const AdjTriangle& currentAT = faces[currentIndex]; + PxU32 refTr0 = dfaces[currentIndex*3 + 0]; + PxU32 refTr1 = dfaces[currentIndex*3 + 1]; + + PxU32 edge[2]; + edge[0] = 1; + edge[1] = 2; + if(triangleIndex == refTr0) + { + edge[0] = 0; + edge[1] = 1; + } + else + { + if(triangleIndex == refTr1) + { + edge[0] = 0; + edge[1] = 2; + } + } + + if(currentAT.HasActiveEdge(edge[0]) && currentAT.HasActiveEdge(edge[1])) + { + return false; + } + + if(!currentAT.HasActiveEdge(edge[0]) && !currentAT.HasActiveEdge(edge[1])) + { + // not interested in testing transition vertices + if(currentIndex == index) + { + return false; + } + + // transition one + for (PxU32 i = 0; i < 2; i++) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[i])); + + // exit if we circle around the vertex back to beginning + if(testIndex == index && previousIndex != index) + { + return false; + } + + if(testIndex != previousIndex) + { + // move to next + previousIndex = currentIndex; + currentIndex = testIndex; + break; + } + } + } + else + { + if(!currentAT.HasActiveEdge(edge[0])) + { + PxU32 t = edge[0]; + edge[0] = edge[1]; + edge[1] = t; + } + + if(currentAT.HasActiveEdge(edge[0])) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[0])); + if(firstFace) + { + firstFace = false; + } + else + { + neighbor = testIndex; + current = currentIndex; + return true; + } + } + + if(!currentAT.HasActiveEdge(edge[1])) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[1])); + if(testIndex != index) + { + previousIndex = currentIndex; + currentIndex = testIndex; + } + } + } + + } + + return false; + } + + static bool CheckFloodFillFace(PxU32 index,const AdjTriangle* faces, const PxU32* dfaces) + { + if(!dfaces) + return true; + + const AdjTriangle& checkedAT = faces[index]; + + PxU32 refTr0 = dfaces[index*3 + 0]; + PxU32 refTr1 = dfaces[index*3 + 1]; + PxU32 refTr2 = dfaces[index*3 + 2]; + + for (PxU32 i = 0; i < 3; i++) + { + if(!checkedAT.HasActiveEdge(i)) + { + PxU32 testTr0 = refTr1; + PxU32 testTr1 = refTr2; + PxU32 testIndex0 = 0; + PxU32 testIndex1 = 1; + if(i == 0) + { + testTr0 = refTr0; + testTr1 = refTr1; + testIndex0 = 1; + testIndex1 = 2; + } + else + { + if(i == 1) + { + testTr0 = refTr0; + testTr1 = refTr2; + testIndex0 = 0; + testIndex1 = 2; + } + } + + PxU32 adjFaceTested = checkedAT.GetAdjTri(SharedEdgeIndex(testIndex0)); + + PxU32 neighborIndex00; + PxU32 neighborIndex01; + bool found0 = GetNeighborFace(index,testTr0,faces,dfaces, neighborIndex00, neighborIndex01); + PxU32 neighborIndex10; + PxU32 neighborIndex11; + bool found1 = GetNeighborFace(adjFaceTested,testTr0,faces,dfaces, neighborIndex10, neighborIndex11); + + if(found0 && found1 && neighborIndex00 == neighborIndex11 && neighborIndex01 == neighborIndex10) + { + return false; + } + + adjFaceTested = checkedAT.GetAdjTri(SharedEdgeIndex(testIndex1)); + found0 = GetNeighborFace(index,testTr1,faces,dfaces,neighborIndex00,neighborIndex01); + found1 = GetNeighborFace(adjFaceTested,testTr1,faces,dfaces,neighborIndex10,neighborIndex11); + + if(found0 && found1 && neighborIndex00 == neighborIndex11 && neighborIndex01 == neighborIndex10) + { + return false; + } + + } + } + + return true; + } + + static bool CheckFloodFill(Ps::Array<PxU32>& indices,AdjTriangle* faces,bool* inMarkers, const PxU32* dfaces) + { + bool valid = true; + + for(PxU32 i=0;i<indices.size();i++) + { + //const AdjTriangle& AT = faces[indices.GetEntry(i)]; + + for(PxU32 j= i + 1;j<indices.size();j++) + { + const AdjTriangle& testAT = faces[indices[j]]; + + if(testAT.GetAdjTri(EDGE01) == indices[i]) + { + if(testAT.HasActiveEdge01()) + { + valid = false; + } + } + if(testAT.GetAdjTri(EDGE02) == indices[i]) + { + if(testAT.HasActiveEdge20()) + { + valid = false; + } + } + if(testAT.GetAdjTri(EDGE12) == indices[i]) + { + if(testAT.HasActiveEdge12()) + { + valid = false; + } + } + + if(!valid) + break; + } + + if(!CheckFloodFillFace(indices[i], faces, dfaces)) + { + valid = false; + } + + if(!valid) + break; + } + + if(!valid) + { + for(PxU32 i=0;i<indices.size();i++) + { + AdjTriangle& AT = faces[indices[i]]; + AT.mATri[0] |= 0x20000000; + AT.mATri[1] |= 0x20000000; + AT.mATri[2] |= 0x20000000; + + inMarkers[indices[i]] = false; + } + + indices.forceSize_Unsafe(0); + + return true; + } + + return false; + } + }; + + if(currentFace!=nbFaces) + { + Ps::Array<PxU32> indices; // Indices of triangles forming hull polygon + + bool doFill = true; + while (doFill) + { + Local::FloodFill(indices, adj.mFaces, currentFace, markers); + + doFill = Local::CheckFloodFill(indices,adj.mFaces,markers, dFaces); + } + + // Now it would be nice to recreate a closed linestrip, similar to silhouette extraction. The line is composed of active edges, this time. + + + Ps::Array<Pair> activeSegments; + //Container ActiveSegments; + // Loop through triangles composing the polygon + for(PxU32 i=0;i<indices.size();i++) + { + const PxU32 currentTriIndex = indices[i]; // Catch current triangle + const PxU32 vRef0 = dFaces ? dFaces[currentTriIndex*3+0] : wFaces[currentTriIndex*3+0]; + const PxU32 vRef1 = dFaces ? dFaces[currentTriIndex*3+1] : wFaces[currentTriIndex*3+1]; + const PxU32 vRef2 = dFaces ? dFaces[currentTriIndex*3+2] : wFaces[currentTriIndex*3+2]; + + // Keep active edges + if(adj.mFaces[currentTriIndex].HasActiveEdge01()) { activeSegments.pushBack(Pair(vRef0,vRef1)); } + if(adj.mFaces[currentTriIndex].HasActiveEdge20()) { activeSegments.pushBack(Pair(vRef0,vRef2)); } + if(adj.mFaces[currentTriIndex].HasActiveEdge12()) { activeSegments.pushBack(Pair(vRef1,vRef2)); } + } + + // We assume the polygon is convex. In that case it should always be possible to retriangulate it so that the triangles are + // implicit (in particular, it should always be possible to remove interior triangles) + + Ps::Array<PxU32> lineStrip; + if(findLineStrip(lineStrip, activeSegments)) + { + PxU32 nb = lineStrip.size(); + if(nb) + { + const PxU32* entries = lineStrip.begin(); + PX_ASSERT(entries[0] == entries[nb-1]); // findLineStrip() is designed that way. Might not be what we want! + + // We get rid of the last (duplicated) index + polygon_data.pushBack(nb-1); + for (PxU32 i = 0; i < nb-1; i++) + { + vertexMarkers[entries[i]]++; + polygon_data.pushBack(entries[i]); + } + nb_polygons++; + + // Loop through vertices composing the line strip polygon end mark the redundant vertices inside the polygon + for(PxU32 i=0;i<indices.size();i++) + { + const PxU32 CurrentTriIndex = indices[i]; // Catch current triangle + const PxU32 VRef0 = dFaces ? dFaces[CurrentTriIndex*3+0] : wFaces[CurrentTriIndex*3+0]; + const PxU32 VRef1 = dFaces ? dFaces[CurrentTriIndex*3+1] : wFaces[CurrentTriIndex*3+1]; + const PxU32 VRef2 = dFaces ? dFaces[CurrentTriIndex*3+2] : wFaces[CurrentTriIndex*3+2]; + + bool found0 = false; + bool found1 = false; + bool found2 = false; + + for (PxU32 j=0;j < nb - 1; j++) + { + if(VRef0 == entries[j]) + { + found0 = true; + } + + if(VRef1 == entries[j]) + { + found1 = true; + } + + if(VRef2 == entries[j]) + { + found2 = true; + } + + if(found0 && found1 && found2) + break; + } + + if(!found0) + { + if(rendundantVertices.find(VRef0) == rendundantVertices.end()) + rendundantVertices.pushBack(VRef0); + } + + if(!found1) + { + if(rendundantVertices.find(VRef1) == rendundantVertices.end()) + rendundantVertices.pushBack(VRef1); + + } + + if(!found2) + { + if(rendundantVertices.find(VRef2) == rendundantVertices.end()) + rendundantVertices.pushBack(VRef2); + } + } + + // If needed, output triangle indices used to build this polygon + if(triangle_data) + { + triangle_data->pushBack(indices.size()); + for (PxU32 j = 0; j < indices.size(); j++) + triangle_data->pushBack(indices[j]); + } + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Meshmerizer::extractHullPolygons: line strip extraction failed"); + return false; + } + } + } + while(currentFace!=nbFaces); + + for (PxU32 i = 0; i < nbVertices; i++) + { + if(vertexMarkers[i] < 3) + { + if(rendundantVertices.find(i) == rendundantVertices.end()) + rendundantVertices.pushBack(i); + } + } + + if(rendundantVertices.size() > 0 && triangle_data) + checkRedundantVertices(nb_polygons,polygon_data,hull,*triangle_data,rendundantVertices); + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +ConvexPolygonsBuilder::ConvexPolygonsBuilder(Gu::ConvexHullData* hull, const bool buildGRBData) + : ConvexHullBuilder(hull, buildGRBData), mNbHullFaces(0), mFaces(NULL) +{ +} + +////////////////////////////////////////////////////////////////////////// + +ConvexPolygonsBuilder::~ConvexPolygonsBuilder() +{ + PX_DELETE_POD(mFaces); +} + +////////////////////////////////////////////////////////////////////////// +// compute hull polygons from given hull triangles +bool ConvexPolygonsBuilder::computeHullPolygons(const PxU32& nbVerts,const PxVec3* verts, const PxU32& nbTriangles, const PxU32* triangles) +{ + PX_ASSERT(triangles); + PX_ASSERT(verts); + + mHullDataHullVertices = NULL; + mHullDataPolygons = NULL; + mHullDataVertexData8 = NULL; + mHullDataFacesByEdges8 = NULL; + mHullDataFacesByVertices8 = NULL; + + mNbHullFaces = nbTriangles; + mHull->mNbHullVertices = Ps::to8(nbVerts); + // allocate additional vec3 for V4 safe load in VolumeInteration + mHullDataHullVertices = reinterpret_cast<PxVec3*>(PX_ALLOC(sizeof(PxVec3) * mHull->mNbHullVertices + 1, "PxVec3")); + PxMemCopy(mHullDataHullVertices, verts, mHull->mNbHullVertices*sizeof(PxVec3)); + + mFaces = PX_NEW(HullTriangleData)[mNbHullFaces]; + for(PxU32 i=0;i<mNbHullFaces;i++) + { + PX_ASSERT(triangles[i*3+0]<=0xffff); + PX_ASSERT(triangles[i*3+1]<=0xffff); + PX_ASSERT(triangles[i*3+2]<=0xffff); + mFaces[i].mRef[0] = triangles[i*3+0]; + mFaces[i].mRef[1] = triangles[i*3+1]; + mFaces[i].mRef[2] = triangles[i*3+2]; + } + + Gu::TriangleT<PxU32>* hullAsIndexedTriangle = reinterpret_cast<Gu::TriangleT<PxU32>*>(mFaces); + + // We don't trust the user at all... So, clean the hull. + PxU32 nbHullVerts = mHull->mNbHullVertices; + CleanFaces(mNbHullFaces, hullAsIndexedTriangle, nbHullVerts, mHullDataHullVertices); + PX_ASSERT(nbHullVerts<256); + mHull->mNbHullVertices = Ps::to8(nbHullVerts); + + // ...and then run the full tests again. + if(!CheckFaces(mNbHullFaces, hullAsIndexedTriangle, mHull->mNbHullVertices, mHullDataHullVertices)) + return false; + + // Transform triangles-to-polygons + if(!createPolygonData()) + return false; + + return checkHullPolygons(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Computes polygon data. +* \return true if success +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool ConvexPolygonsBuilder::createPolygonData() +{ + // Cleanup + mHull->mNbPolygons = 0; + PX_DELETE_POD(mHullDataVertexData8); + PX_DELETE_POD(mHullDataFacesByVertices8); + PX_FREE_AND_RESET(mHullDataPolygons); + + // Extract polygon data from triangle data + Ps::Array<PxU32> temp; + Ps::Array<PxU32> temp2; + Ps::Array<PxU32> rendundantVertices; + PxU32 nbPolygons; + if(!extractHullPolygons(nbPolygons, temp, *this, &temp2,rendundantVertices)) + return false; + + PxVec3* reducedHullDataHullVertices = mHullDataHullVertices; + PxU8 numReducedHullDataVertices = mHull->mNbHullVertices; + + if(rendundantVertices.size() > 0) + { + numReducedHullDataVertices = Ps::to8(mHull->mNbHullVertices - rendundantVertices.size()); + reducedHullDataHullVertices = static_cast<PxVec3*> (PX_ALLOC_TEMP(sizeof(PxVec3)*numReducedHullDataVertices,"Reduced vertices hull data")); + PxU8* remapTable = PX_NEW(PxU8)[mHull->mNbHullVertices]; + + PxU8 currentIndex = 0; + for (PxU8 i = 0; i < mHull->mNbHullVertices; i++) + { + if(rendundantVertices.find(i) == rendundantVertices.end()) + { + PX_ASSERT(currentIndex < numReducedHullDataVertices); + reducedHullDataHullVertices[currentIndex] = mHullDataHullVertices[i]; + remapTable[i] = currentIndex; + currentIndex++; + } + else + { + remapTable[i] = 0xFF; + } + } + + PxU32* data = temp.begin(); + for(PxU32 i=0;i<nbPolygons;i++) + { + PxU32 nbVerts = *data++; + PX_ASSERT(nbVerts>=3); // Else something very wrong happened... + + for(PxU32 j=0;j<nbVerts;j++) + { + PX_ASSERT(data[j] < mHull->mNbHullVertices); + data[j] = remapTable[data[j]]; + } + + data += nbVerts; + } + + PX_DELETE_POD(remapTable); + } + + if(nbPolygons>255) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder: convex hull has more than 255 polygons!"); + return false; + } + + // Precompute hull polygon structures + mHull->mNbPolygons = Ps::to8(nbPolygons); + mHullDataPolygons = reinterpret_cast<Gu::HullPolygonData*>(PX_ALLOC(sizeof(Gu::HullPolygonData)*mHull->mNbPolygons, "Gu::HullPolygonData")); + PxMemZero(mHullDataPolygons, sizeof(Gu::HullPolygonData)*mHull->mNbPolygons); + + // The winding hasn't been preserved so we need to handle this. Basically we need to "unify normals" + // exactly as we did at hull creation time - except this time we work on polygons + PxVec3 geomCenter; + computeGeomCenter(geomCenter, mNbHullFaces, mFaces); + + // Loop through polygons + // We have N polygons => remove N entries for number of vertices + PxU32 tmp = temp.size() - nbPolygons; + mHullDataVertexData8 = PX_NEW(PxU8)[tmp]; + PxU8* dest = mHullDataVertexData8; + const PxU32* data = temp.begin(); + const PxU32* triData = temp2.begin(); + for(PxU32 i=0;i<nbPolygons;i++) + { + mHullDataPolygons[i].mVRef8 = PxU16(dest - mHullDataVertexData8); // Setup link for current polygon + PxU32 nbVerts = *data++; + PX_ASSERT(nbVerts>=3); // Else something very wrong happened... + mHullDataPolygons[i].mNbVerts = Ps::to8(nbVerts); + + PxU32 index = 0; + for(PxU32 j=0;j<nbVerts;j++) + { + if(data[j] != 0xFF) + { + dest[index] = Ps::to8(data[j]); + index++; + } + else + { + mHullDataPolygons[i].mNbVerts--; + } + } + + // Compute plane equation + { + computeNewellPlane(mHullDataPolygons[i].mPlane, mHullDataPolygons[i].mNbVerts, dest, reducedHullDataHullVertices); + + PxU32 nbTris = *triData++; // #tris in current poly + bool flip = false; + for(PxU32 k=0;k< nbTris; k++) + { + PxU32 triIndex = *triData++; // Index of one triangle composing polygon + PX_ASSERT(triIndex<mNbHullFaces); + const Gu::TriangleT<PxU32>& T = reinterpret_cast<const Gu::TriangleT<PxU32>&>(mFaces[triIndex]); + const PxPlane PL = PlaneEquation(T, mHullDataHullVertices); + if(k==0 && PL.n.dot(mHullDataPolygons[i].mPlane.n) < 0.0f) + { + flip = true; + } + } + if(flip) + { + negatePlane(mHullDataPolygons[i]); + inverseBuffer(mHullDataPolygons[i].mNbVerts, dest); + } + + for(PxU32 j=0;j<mHull->mNbHullVertices;j++) + { + float d = - (mHullDataPolygons[i].mPlane.n).dot(mHullDataHullVertices[j]); + if(d<mHullDataPolygons[i].mPlane.d) mHullDataPolygons[i].mPlane.d=d; + } + } + + // "Unify normal" + if(mHullDataPolygons[i].mPlane.distance(geomCenter)>0.0f) + { + inverseBuffer(mHullDataPolygons[i].mNbVerts, dest); + + negatePlane(mHullDataPolygons[i]); + PX_ASSERT(mHullDataPolygons[i].mPlane.distance(geomCenter)<=0.0f); + } + + // Next one + data += nbVerts; // Skip vertex indices + dest += mHullDataPolygons[i].mNbVerts; + } + + if(reducedHullDataHullVertices != mHullDataHullVertices) + { + PxMemCopy(mHullDataHullVertices,reducedHullDataHullVertices,sizeof(PxVec3)*numReducedHullDataVertices); + PX_FREE(reducedHullDataHullVertices); + + mHull->mNbHullVertices = numReducedHullDataVertices; + } + + //calculate the vertex map table + if(!calculateVertexMapTable(nbPolygons)) + return false; + +#ifdef USE_PRECOMPUTED_HULL_PROJECTION + // Loop through polygons + for(PxU32 j=0;j<nbPolygons;j++) + { + // Precompute hull projection along local polygon normal + PxU32 nbVerts = mHull->mNbHullVertices; + const PxVec3* verts = mHullDataHullVertices; + Gu::HullPolygonData& polygon = mHullDataPolygons[j]; + PxReal min = PX_MAX_F32; + PxU8 minIndex = 0xff; + for (PxU8 i = 0; i < nbVerts; i++) + { + float dp = (*verts++).dot(polygon.mPlane.n); + if(dp < min) + { + min = dp; + minIndex = i; + } + } + polygon.mMinIndex = minIndex; + } +#endif + + // Triangulate newly created polygons to recreate a clean vertex cloud. + return createTrianglesFromPolygons(); +} + +////////////////////////////////////////////////////////////////////////// +// create back triangles from polygons +bool ConvexPolygonsBuilder::createTrianglesFromPolygons() +{ + if (!mHull->mNbPolygons || !mHullDataPolygons) return false; + + PxU32 maxNbTriangles = 0; + for (PxU32 i = 0; i < mHull->mNbPolygons; i++) + { + if (mHullDataPolygons[i].mNbVerts < 3) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder::CreateTrianglesFromPolygons: convex hull has a polygon with less than 3 vertices!"); + return false; + } + maxNbTriangles += mHullDataPolygons[i].mNbVerts - 2; + } + + HullTriangleData* tmpFaces = PX_NEW(HullTriangleData)[maxNbTriangles]; + + HullTriangleData* currFace = tmpFaces; + PxU32 nbTriangles = 0; + const PxU8* vertexData = mHullDataVertexData8; + const PxVec3* hullVerts = mHullDataHullVertices; + for (PxU32 i = 0; i < mHull->mNbPolygons; i++) + { + const PxU8* data = vertexData + mHullDataPolygons[i].mVRef8; + PxU32 nbVerts = mHullDataPolygons[i].mNbVerts; + + // Triangulate the polygon such that all all generated triangles have one and the same vertex + // in common. + // + // Make sure to avoid creating zero area triangles. Imagine the following polygon: + // + // 4 3 + // *------------------* + // | | + // *---*----*----*----* + // 5 6 0 1 2 + // + // Choosing vertex 0 as the shared vertex, the following zero area triangles will be created: + // [0 1 2], [0 5 6] + // + // Check for these triangles and discard them + // Note: Such polygons should only occur if the user defines the convex hull, i.e., the triangles + // of the convex shape, himself. If the convex hull is built from the vertices only, the + // hull algorithm removes the useless vertices. + // + for (PxU32 j = 0; j < nbVerts - 2; j++) + { + currFace->mRef[0] = data[0]; + currFace->mRef[1] = data[(j + 1) % nbVerts]; + currFace->mRef[2] = data[(j + 2) % nbVerts]; + + const PxVec3& p0 = hullVerts[currFace->mRef[0]]; + const PxVec3& p1 = hullVerts[currFace->mRef[1]]; + const PxVec3& p2 = hullVerts[currFace->mRef[2]]; + + const float area = ((p1 - p0).cross(p2 - p0)).magnitudeSquared(); + + if (area != 0.0f) // Else discard the triangle + { + nbTriangles++; + currFace++; + } + } + } + + PX_DELETE_POD(mFaces); + HullTriangleData* faces; + PX_ASSERT(nbTriangles <= maxNbTriangles); + if (maxNbTriangles == nbTriangles) + { + // No zero area triangles, hence the face buffer has correct size and can be used directly. + faces = tmpFaces; + } + else + { + // Resize face buffer because some triangles were discarded. + faces = PX_NEW(HullTriangleData)[nbTriangles]; + if (!faces) + { + PX_DELETE_POD(tmpFaces); + return false; + } + PxMemCopy(faces, tmpFaces, sizeof(HullTriangleData)*nbTriangles); + PX_DELETE_POD(tmpFaces); + } + mFaces = faces; + mNbHullFaces = nbTriangles; + // TODO: at this point useless vertices should be removed from the hull. The current fix is to initialize + // support vertices to known valid vertices, but it's not really convincing. + + // Re-unify normals + PxVec3 geomCenter; + computeGeomCenter(geomCenter, mNbHullFaces, mFaces); + + for (PxU32 i = 0; i < mNbHullFaces; i++) + { + const PxPlane P(hullVerts[mFaces[i].mRef[0]], + hullVerts[mFaces[i].mRef[1]], + hullVerts[mFaces[i].mRef[2]]); + if (P.distance(geomCenter) > 0.0f) + { + Flip(mFaces[i]); + } + } + return true; +} + |