aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/PhysXCooking/src/mesh/TriangleMeshBuilder.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/PhysXCooking/src/mesh/TriangleMeshBuilder.cpp
downloadphysx-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/mesh/TriangleMeshBuilder.cpp')
-rw-r--r--PhysX_3.4/Source/PhysXCooking/src/mesh/TriangleMeshBuilder.cpp1443
1 files changed, 1443 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/PhysXCooking/src/mesh/TriangleMeshBuilder.cpp b/PhysX_3.4/Source/PhysXCooking/src/mesh/TriangleMeshBuilder.cpp
new file mode 100644
index 00000000..877d752e
--- /dev/null
+++ b/PhysX_3.4/Source/PhysXCooking/src/mesh/TriangleMeshBuilder.cpp
@@ -0,0 +1,1443 @@
+// 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 "RTreeCooking.h"
+#include "TriangleMeshBuilder.h"
+#include "EdgeList.h"
+#include "MeshCleaner.h"
+#include "GuConvexEdgeFlags.h"
+#include "PxTriangleMeshDesc.h"
+#include "GuSerialize.h"
+#include "Cooking.h"
+#include "GuMeshData.h"
+#include "GuTriangle32.h"
+#include "GuRTree.h"
+#include "GuInternal.h"
+#include "GuBV4Build.h"
+#include "GuBV32Build.h"
+#include "PsFoundation.h"
+#include "PsHashMap.h"
+#include "PsSort.h"
+
+namespace physx {
+
+struct int3
+{
+ int x, y, z;
+};
+
+struct uint3
+{
+ unsigned int x, y, z;
+};
+
+PX_ALIGN_PREFIX(16)
+struct uint4
+{
+ unsigned int x, y, z, w;
+}
+PX_ALIGN_SUFFIX(16);
+
+PX_ALIGN_PREFIX(16)
+struct float4
+{
+ float x, y, z, w;
+}
+PX_ALIGN_SUFFIX(16);
+
+}
+
+#include "GrbTriangleMeshCooking.h"
+
+using namespace physx;
+using namespace Gu;
+using namespace Ps;
+
+namespace physx {
+
+TriangleMeshBuilder::TriangleMeshBuilder(TriangleMeshData& m, const PxCookingParams& params) :
+ edgeList (NULL),
+ mParams (params),
+ mMeshData (m)
+{
+}
+
+TriangleMeshBuilder::~TriangleMeshBuilder()
+{
+ releaseEdgeList();
+}
+
+void TriangleMeshBuilder::remapTopology(const PxU32* order)
+{
+ if(!mMeshData.mNbTriangles)
+ return;
+
+ // Remap one array at a time to limit memory usage
+
+ Gu::TriangleT<PxU32>* newTopo = reinterpret_cast<Gu::TriangleT<PxU32>*>(PX_ALLOC(mMeshData.mNbTriangles * sizeof(Gu::TriangleT<PxU32>), "Gu::TriangleT<PxU32>"));
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++)
+ newTopo[i] = reinterpret_cast<Gu::TriangleT<PxU32>*>(mMeshData.mTriangles)[order[i]];
+ PX_FREE_AND_RESET(mMeshData.mTriangles);
+ mMeshData.mTriangles = newTopo;
+
+ if(mMeshData.mMaterialIndices)
+ {
+ PxMaterialTableIndex* newMat = PX_NEW(PxMaterialTableIndex)[mMeshData.mNbTriangles];
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++)
+ newMat[i] = mMeshData.mMaterialIndices[order[i]];
+ PX_DELETE_POD(mMeshData.mMaterialIndices);
+ mMeshData.mMaterialIndices = newMat;
+ }
+
+ if(!mParams.suppressTriangleMeshRemapTable || mParams.buildGPUData)
+ {
+ PxU32* newMap = PX_NEW(PxU32)[mMeshData.mNbTriangles];
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++)
+ newMap[i] = mMeshData.mFaceRemap ? mMeshData.mFaceRemap[order[i]] : order[i];
+ PX_DELETE_POD(mMeshData.mFaceRemap);
+ mMeshData.mFaceRemap = newMap;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool TriangleMeshBuilder::cleanMesh(bool validate, PxTriangleMeshCookingResult::Enum* condition)
+{
+ PX_ASSERT(mMeshData.mFaceRemap == NULL);
+
+ PxF32 meshWeldTolerance = 0.0f;
+ if(mParams.meshPreprocessParams & PxMeshPreprocessingFlag::eWELD_VERTICES)
+ {
+ if(mParams.meshWeldTolerance == 0.f)
+ {
+ Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "TriangleMesh: Enable mesh welding with 0 weld tolerance!");
+ }
+ else
+ {
+ meshWeldTolerance = mParams.meshWeldTolerance;
+ }
+ }
+ MeshCleaner cleaner(mMeshData.mNbVertices, mMeshData.mVertices, mMeshData.mNbTriangles, reinterpret_cast<const PxU32*>(mMeshData.mTriangles), meshWeldTolerance);
+ if(!cleaner.mNbTris)
+ return false;
+
+ if(validate)
+ {
+ // if we do only validate, we check if cleaning did not remove any verts or triangles.
+ // such a mesh can be then directly used for cooking without clean flag
+ if((cleaner.mNbVerts != mMeshData.mNbVertices) || (cleaner.mNbTris != mMeshData.mNbTriangles))
+ {
+ return false;
+ }
+ }
+
+ // PT: deal with the remap table
+ {
+ // PT: TODO: optimize this
+ if(cleaner.mRemap)
+ {
+ const PxU32 newNbTris = cleaner.mNbTris;
+
+ // Remap material array
+ if(mMeshData.mMaterialIndices)
+ {
+ PxMaterialTableIndex* tmp = PX_NEW(PxMaterialTableIndex)[newNbTris];
+ for(PxU32 i=0;i<newNbTris;i++)
+ tmp[i] = mMeshData.mMaterialIndices[cleaner.mRemap[i]];
+
+ PX_DELETE_POD(mMeshData.mMaterialIndices);
+ mMeshData.mMaterialIndices = tmp;
+ }
+
+ if (!mParams.suppressTriangleMeshRemapTable || mParams.buildGPUData)
+ {
+ mMeshData.mFaceRemap = PX_NEW(PxU32)[newNbTris];
+ PxMemCopy(mMeshData.mFaceRemap, cleaner.mRemap, newNbTris*sizeof(PxU32));
+ }
+ }
+ }
+
+ // PT: deal with geometry
+ {
+ if(mMeshData.mNbVertices!=cleaner.mNbVerts)
+ {
+ PX_FREE_AND_RESET(mMeshData.mVertices);
+ mMeshData.allocateVertices(cleaner.mNbVerts);
+ }
+ PxMemCopy(mMeshData.mVertices, cleaner.mVerts, mMeshData.mNbVertices*sizeof(PxVec3));
+ }
+
+ // PT: deal with topology
+ {
+ PX_ASSERT(!(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES));
+ if(mMeshData.mNbTriangles!=cleaner.mNbTris)
+ {
+ PX_FREE_AND_RESET(mMeshData.mTriangles);
+ mMeshData.allocateTriangles(cleaner.mNbTris, true);
+ }
+
+ const float testLength = 500.0f*500.0f*mParams.scale.length*mParams.scale.length;
+ bool bigTriangle = false;
+ const PxVec3* v = mMeshData.mVertices;
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++)
+ {
+ const PxU32 vref0 = cleaner.mIndices[i*3+0];
+ const PxU32 vref1 = cleaner.mIndices[i*3+1];
+ const PxU32 vref2 = cleaner.mIndices[i*3+2];
+ PX_ASSERT(vref0!=vref1 && vref0!=vref2 && vref1!=vref2);
+
+ reinterpret_cast<Gu::TriangleT<PxU32>*>(mMeshData.mTriangles)[i].v[0] = vref0;
+ reinterpret_cast<Gu::TriangleT<PxU32>*>(mMeshData.mTriangles)[i].v[1] = vref1;
+ reinterpret_cast<Gu::TriangleT<PxU32>*>(mMeshData.mTriangles)[i].v[2] = vref2;
+
+ if( (v[vref0] - v[vref1]).magnitudeSquared() >= testLength
+ || (v[vref1] - v[vref2]).magnitudeSquared() >= testLength
+ || (v[vref2] - v[vref0]).magnitudeSquared() >= testLength
+ )
+ bigTriangle = true;
+ }
+ if(bigTriangle)
+ {
+ if(condition)
+ *condition = PxTriangleMeshCookingResult::eLARGE_TRIANGLE;
+ Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "TriangleMesh: triangles are too big, reduce their size to increase simulation stability!");
+ }
+ }
+
+ return true;
+}
+
+void TriangleMeshBuilder::createSharedEdgeData(bool buildAdjacencies, bool buildActiveEdges)
+{
+ if(buildAdjacencies) // building edges is required if buildAdjacencies is requested
+ buildActiveEdges = true;
+
+ PX_ASSERT(mMeshData.mExtraTrigData == NULL);
+ PX_ASSERT(mMeshData.mAdjacencies == NULL);
+
+ if(!buildActiveEdges)
+ return;
+
+ const PxU32 nTrigs = mMeshData.mNbTriangles;
+
+ mMeshData.mExtraTrigData = PX_NEW(PxU8)[nTrigs];
+ memset(mMeshData.mExtraTrigData, 0, sizeof(PxU8)*nTrigs);
+
+ const Gu::TriangleT<PxU32>* trigs = reinterpret_cast<const Gu::TriangleT<PxU32>*>(mMeshData.mTriangles);
+ if(0x40000000 <= nTrigs)
+ {
+ //mesh is too big for this algo, need to be able to express trig indices in 30 bits, and still have an index reserved for "unused":
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh: mesh is too big for this algo!");
+ return;
+ }
+
+ createEdgeList();
+ if(edgeList)
+ {
+ PX_ASSERT(edgeList->getNbFaces()==mMeshData.mNbTriangles);
+ if(edgeList->getNbFaces()==mMeshData.mNbTriangles)
+ {
+ for(PxU32 i=0;i<edgeList->getNbFaces();i++)
+ {
+ const Gu::EdgeTriangleData& ET = edgeList->getEdgeTriangle(i);
+ // Replicate flags
+ if(Gu::EdgeTriangleAC::HasActiveEdge01(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_01;
+ if(Gu::EdgeTriangleAC::HasActiveEdge12(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_12;
+ if(Gu::EdgeTriangleAC::HasActiveEdge20(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_20;
+ }
+ }
+ }
+
+ // fill the adjacencies
+ if(buildAdjacencies)
+ {
+ mMeshData.mAdjacencies = PX_NEW(PxU32)[nTrigs*3];
+ memset(mMeshData.mAdjacencies, 0xFFFFffff, sizeof(PxU32)*nTrigs*3);
+
+ PxU32 NbEdges = edgeList->getNbEdges();
+ const Gu::EdgeDescData* ED = edgeList->getEdgeToTriangles();
+ const Gu::EdgeData* Edges = edgeList->getEdges();
+ const PxU32* FBE = edgeList->getFacesByEdges();
+
+ while(NbEdges--)
+ {
+ // Get number of triangles sharing current edge
+ PxU32 Count = ED->Count;
+
+ if(Count > 1)
+ {
+ PxU32 FaceIndex0 = FBE[ED->Offset+0];
+ PxU32 FaceIndex1 = FBE[ED->Offset+1];
+
+ const Gu::EdgeData& edgeData = *Edges;
+ const Gu::TriangleT<PxU32>& T0 = trigs[FaceIndex0];
+ const Gu::TriangleT<PxU32>& T1 = trigs[FaceIndex1];
+
+ PxU32 offset0 = T0.findEdgeCCW(edgeData.Ref0,edgeData.Ref1);
+ PxU32 offset1 = T1.findEdgeCCW(edgeData.Ref0,edgeData.Ref1);
+
+ mMeshData.setTriangleAdjacency(FaceIndex0, FaceIndex1, offset0);
+ mMeshData.setTriangleAdjacency(FaceIndex1, FaceIndex0, offset1);
+ }
+ ED++;
+ Edges++;
+ }
+ }
+
+#if PX_DEBUG
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++)
+ {
+ const Gu::TriangleT<PxU32>& T = trigs[i];
+ PX_UNUSED(T);
+ const Gu::EdgeTriangleData& ET = edgeList->getEdgeTriangle(i);
+ PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge01(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_01)) || (!Gu::EdgeTriangleAC::HasActiveEdge01(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_01)));
+ PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge12(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_12)) || (!Gu::EdgeTriangleAC::HasActiveEdge12(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_12)));
+ PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge20(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_20)) || (!Gu::EdgeTriangleAC::HasActiveEdge20(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_20)));
+ }
+#endif
+ return;
+}
+
+namespace GrbTrimeshCookerHelper
+{
+
+struct SortedNeighbor
+{
+ PxU32 v, a; // vertex and adjacent vertex
+ bool boundary;
+
+ SortedNeighbor(PxU32 v_, PxU32 a_, bool b_): v(v_), a(a_), boundary(b_) {}
+
+ // sort boundary edges to the front so that they are kept when duplicates are removed
+ bool operator<(const SortedNeighbor& b) const
+ {
+ return (v<b.v || (v == b.v && a<b.a) || (v == b.v && a == b.a && boundary && !b.boundary));
+ }
+};
+
+struct SharpEdgeRange
+{
+ PxU32 start, length;
+ SharpEdgeRange(): start(0), length(0) {}
+ SharpEdgeRange(PxU32 s, PxU32 l): start(s), length(l) {}
+};
+
+class LocalIndexer
+{
+public:
+ bool insert(PxU32 meshIndex) // returns true if this is a new index
+ {
+ bool isNew = mMeshToLocal.insert(meshIndex, mLocalToMesh.size());
+ if(isNew)
+ mLocalToMesh.pushBack(meshIndex);
+ return isNew;
+ }
+
+ PxU32 meshIndex(PxU32 localIndex)
+ {
+ PX_ASSERT(localIndex<mLocalToMesh.size());
+ return mLocalToMesh[localIndex];
+ }
+
+ PxU32 localIndex(PxU32 meshIndex)
+ {
+ PX_ASSERT(mMeshToLocal.find(meshIndex));
+ return mMeshToLocal[meshIndex];
+ }
+
+ bool contains(PxU32 meshIndex)
+ {
+ return mMeshToLocal.find(meshIndex) != 0;
+ }
+
+ PxU32 size()
+ {
+ return mLocalToMesh.size();
+ }
+
+private:
+ Ps::Array<PxU32> mLocalToMesh;
+ Ps::HashMap<PxU32, PxU32> mMeshToLocal;
+};
+
+#include <stdio.h>
+
+
+void findSharpVertices(
+ Ps::Array<SortedNeighbor>& pairList,
+ Ps::Array<SharpEdgeRange>& edgeRanges,
+ /*const Ps::Array<Triangle>& triangles,*/
+ const uint3* triIndices,
+ const uint4* triAdjacencies,
+ PxU32 nbTris,
+ PxU32 nbVerts
+ )
+{
+ // sort the edges which are sharp or boundary
+ for(PxU32 i=0;i<nbTris;i++)
+ {
+ const uint4& triAdj = triAdjacencies[i];
+ const uint3& triIdx = triIndices[i];
+
+ if (!isEdgeNonconvex(triAdj.x))
+ {
+ pairList.pushBack(SortedNeighbor(triIdx.x, triIdx.y, triAdj.x == BOUNDARY));
+ pairList.pushBack(SortedNeighbor(triIdx.y, triIdx.x, triAdj.x == BOUNDARY));
+ }
+
+ if (!isEdgeNonconvex(triAdj.y))
+ {
+ pairList.pushBack(SortedNeighbor(triIdx.y, triIdx.z, triAdj.y == BOUNDARY));
+ pairList.pushBack(SortedNeighbor(triIdx.z, triIdx.y, triAdj.y == BOUNDARY));
+ }
+
+ if (!isEdgeNonconvex(triAdj.z))
+ {
+ pairList.pushBack(SortedNeighbor(triIdx.z, triIdx.x, triAdj.z == BOUNDARY));
+ pairList.pushBack(SortedNeighbor(triIdx.x, triIdx.z, triAdj.z == BOUNDARY));
+ }
+ }
+
+ Ps::sort(pairList.begin(), pairList.size());
+
+ // remove duplicates - note that boundary edges are sorted earlier, so we keep them
+ PxU32 unique = 1;
+ for(PxU32 i=1;i<pairList.size();i++)
+ {
+ if(pairList[i].v != pairList[i-1].v || pairList[i].a != pairList[i-1].a)
+ pairList[unique++] = pairList[i];
+ }
+ pairList.resizeUninitialized(unique);
+
+ // a vertex is marked for sharp vertex processing if it has a boundary edge or at least three convex edges
+ edgeRanges.resize(nbVerts);
+ for(PxU32 p = 0, u ; p<pairList.size(); p = u)
+ {
+ bool boundary = false;
+ for(u=p+1; u<pairList.size() && pairList[u].v == pairList[p].v; u++)
+ boundary |= pairList[u].boundary;
+ if(boundary || u-p>=3)
+ edgeRanges[pairList[p].v] = SharpEdgeRange(p, u-p);
+ }
+}
+
+#if 0
+PxU32 buildVertexConnectionNew_p1(
+ Ps::Array<SortedNeighbor> & pairList,
+ Ps::Array<SharpEdgeRange> & edgeRanges,
+ LocalIndexer & vertexMap,
+
+ const uint4 * triIndices,
+ const uint4 * triAdjacencies,
+
+ PxU32 nbTris,
+ PxU32 nbVerts
+ )
+{
+ findSharpVertices(
+ pairList,
+ edgeRanges,
+ triIndices,
+ triAdjacencies,
+ nbTris,
+ nbVerts
+ );
+
+ // add all the original triangles and vertices and record how big the core is
+ for(PxU32 i=0; i<nbTris; i++)
+ {
+ const uint4 & triIdx = triIndices[i];
+ vertexMap.insert(triIdx.x);
+ vertexMap.insert(triIdx.y);
+ vertexMap.insert(triIdx.z);
+ }
+ PxU32 nbCoreVerts = vertexMap.size();
+
+ PX_ASSERT(nbCoreVerts == nbVerts);
+
+ // add adjacent triangles
+ for(PxU32 i=0;i<nbTris;i++)
+ {
+ const uint4 & triAdj = triAdjacencies[i];
+
+#define IS_TRI(triAdjIdx) (( (triAdjIdx) != BOUNDARY ) && ( !((triAdjIdx) & NONCONVEX_FLAG) ))
+
+ if(IS_TRI(triAdj.x))
+ {
+ const uint4 & triIdx = triIndices[triAdj.x];
+ vertexMap.insert(triIdx.x);
+ vertexMap.insert(triIdx.y);
+ vertexMap.insert(triIdx.z);
+ }
+
+ if(IS_TRI(triAdj.y))
+ {
+ const uint4 & triIdx = triIndices[triAdj.y];
+ vertexMap.insert(triIdx.x);
+ vertexMap.insert(triIdx.y);
+ vertexMap.insert(triIdx.z);
+ }
+
+ if(IS_TRI(triAdj.z))
+ {
+ const uint4 & triIdx = triIndices[triAdj.z];
+ vertexMap.insert(triIdx.x);
+ vertexMap.insert(triIdx.y);
+ vertexMap.insert(triIdx.z);
+
+ }
+
+#undef IS_TRI
+ }
+
+ // add the neighbors of the sharp vertices
+ PxU32 nbNeighbors = 0;
+ for(PxU32 i=0;i<nbCoreVerts;i++)
+ {
+ PxU32 meshIndex = vertexMap.meshIndex(i);
+ const SharpEdgeRange& er = edgeRanges[meshIndex];
+ for(PxU32 j = 0;j<er.length;j++)
+ {
+ PX_ASSERT(pairList[er.start+j].v == meshIndex);
+ vertexMap.insert(pairList[er.start + j].a);
+ }
+ nbNeighbors += er.length;
+ }
+
+ return nbNeighbors;
+}
+
+void buildVertexConnectionNew_p2(
+ PxU32 * adjVertStart,
+ PxU32 * vertValency,
+ PxU32 * adjVertices,
+
+ Ps::Array<SortedNeighbor>& pairList,
+ Ps::Array<SharpEdgeRange>& edgeRanges,
+ LocalIndexer & vertexMap,
+
+ const uint4 * /*triIndices*/,
+ const uint4 * /*triAdjacencies*/,
+
+ PxU32 /*nbTris*/,
+ PxU32 nbVerts,
+ PxU32 /*nbNeighbors*/
+ )
+{
+ PxU32 n = 0;
+ for(PxU32 i=0;i<nbVerts;i++)
+ {
+ PxU32 meshIdx = vertexMap.meshIndex(i);
+ const SharpEdgeRange& er = edgeRanges[vertexMap.meshIndex(i)];
+ adjVertStart[meshIdx] = n;
+ vertValency[meshIdx] = er.length;
+ for(PxU32 j = 0;j<er.length;j++)
+ adjVertices[n++] = pairList[er.start+j].a;
+ }
+}
+#else
+
+
+PxU32 buildVertexConnectionNew_p1(
+ Ps::Array<SortedNeighbor> & pairList,
+ Ps::Array<SharpEdgeRange> & edgeRanges,
+
+ const uint3* triIndices,
+ const uint4 * triAdjacencies,
+
+ PxU32 nbTris,
+ PxU32 nbVerts
+ )
+{
+ findSharpVertices(
+ pairList,
+ edgeRanges,
+ triIndices,
+ triAdjacencies,
+ nbTris,
+ nbVerts
+ );
+
+
+ // add the neighbors of the sharp vertices
+ PxU32 nbNeighbors = 0;
+ for (PxU32 i = 0; i<nbVerts; i++)
+ {
+ const SharpEdgeRange& er = edgeRanges[i];
+ nbNeighbors += er.length;
+ }
+
+ return nbNeighbors;
+}
+
+void buildVertexConnectionNew_p2(
+ PxU32 * adjVertStart,
+ PxU32 * vertValency,
+ PxU32 * adjVertices,
+
+ Ps::Array<SortedNeighbor>& pairList,
+ Ps::Array<SharpEdgeRange>& edgeRanges,
+ PxU32 nbVerts
+ )
+{
+ PxU32 n = 0;
+ for (PxU32 i = 0; i<nbVerts; i++)
+ {
+ const SharpEdgeRange& er = edgeRanges[i];
+ adjVertStart[i] = n;
+ vertValency[i] = er.length;
+ for (PxU32 j = 0; j<er.length; j++)
+ adjVertices[n++] = pairList[er.start + j].a;
+ }
+}
+#endif
+
+} // namespace GrbTrimeshCookerHelper
+
+void TriangleMeshBuilder::recordTriangleIndices()
+{
+ if (mParams.buildGPUData)
+ {
+ PX_ASSERT(!(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES));
+ PX_ASSERT(mMeshData.mGRB_triIndices);
+
+ //copy the original traingle indices to originalTrangles32
+ PxMemCopy(mMeshData.mGRB_triIndices, mMeshData.mTriangles, sizeof(IndTri32) *mMeshData.mNbTriangles);
+
+
+ if (mMeshData.mFaceRemap)
+ {
+ //We must have discarded some triangles so let's
+ mMeshData.mGRB_faceRemap = PX_NEW(PxU32)[mMeshData.mNbTriangles];
+ PxMemCopy(mMeshData.mGRB_faceRemap, mMeshData.mFaceRemap, sizeof(PxU32)*mMeshData.mNbTriangles);
+ }
+
+ }
+}
+
+void TriangleMeshBuilder::createGRBData()
+{
+
+ const PxU32 & numTris = mMeshData.mNbTriangles;
+ const PxU32 & numVerts = mMeshData.mNbVertices;
+
+ PX_ASSERT(!(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES));
+
+
+ // Core: Mesh data
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ // (by using adjacency info generated by physx cooker)
+ PxVec3 * tempNormalsPerTri_prealloc = reinterpret_cast<PxVec3 *>(PX_ALLOC(numTris * sizeof(PxVec3), PX_DEBUG_EXP("tempNormalsPerTri_prealloc")));
+
+ mMeshData.mGRB_triAdjacencies = PX_ALLOC(sizeof(uint4)*numTris, PX_DEBUG_EXP("GRB_triAdjacencies"));
+
+
+ buildAdjacencies(
+ reinterpret_cast<uint4 *>(mMeshData.mGRB_triAdjacencies),
+ tempNormalsPerTri_prealloc,
+ mMeshData.mVertices,
+ reinterpret_cast<uint3*>(mMeshData.mGRB_triIndices),
+ numTris
+ );
+
+
+ PX_FREE(tempNormalsPerTri_prealloc);
+
+ mMeshData.mGRB_vertValency = PX_NEW(PxU32)[numVerts];
+ mMeshData.mGRB_adjVertStart = PX_NEW(PxU32)[numVerts];
+
+
+ Ps::Array<GrbTrimeshCookerHelper::SortedNeighbor> pairsList;
+ Ps::Array<GrbTrimeshCookerHelper::SharpEdgeRange> edgeRanges;
+
+
+ mMeshData.mGRB_meshAdjVerticiesTotal = GrbTrimeshCookerHelper::buildVertexConnectionNew_p1(
+ pairsList,
+ edgeRanges,
+
+ reinterpret_cast<uint3*>(mMeshData.mGRB_triIndices),
+ reinterpret_cast<uint4 *>(mMeshData.mGRB_triAdjacencies),
+ numTris,
+ numVerts
+ );
+
+
+
+ mMeshData.mGRB_adjVertices = PX_NEW(PxU32)[mMeshData.mGRB_meshAdjVerticiesTotal];
+ GrbTrimeshCookerHelper::buildVertexConnectionNew_p2(
+ mMeshData.mGRB_adjVertStart,
+ mMeshData.mGRB_vertValency,
+ mMeshData.mGRB_adjVertices,
+ pairsList,
+ edgeRanges,
+ numVerts
+ );
+
+
+}
+
+void TriangleMeshBuilder::createGRBMidPhaseAndData(const PxU32 originalTriangleCount)
+{
+ if (mParams.buildGPUData)
+ {
+
+ PX_ASSERT(!(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES));
+
+ BV32Tree* bv32Tree = PX_NEW(BV32Tree);
+ mMeshData.mGRB_BV32Tree = bv32Tree;
+
+ BV32TriangleMeshBuilder::createMidPhaseStructure(mParams, mMeshData, *bv32Tree);
+
+ createGRBData();
+
+ //create a remap table from GPU to CPU remap table
+ PxU32* orignalToRemap = PX_NEW(PxU32)[originalTriangleCount];
+
+ PX_ASSERT(mMeshData.mFaceRemap);
+
+
+ for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i)
+ {
+ const PxU32 index = mMeshData.mFaceRemap[i];
+ PX_ASSERT(index < originalTriangleCount);
+ orignalToRemap[index] = i;
+ }
+
+
+ //map CPU remap triangle index to GPU remap triangle index
+ for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i)
+ {
+ const PxU32 index = mMeshData.mGRB_faceRemap[i];
+ mMeshData.mGRB_faceRemap[i] = orignalToRemap[index];
+ }
+
+#if BV32_VALIDATE
+ IndTri32* grbTriIndices = reinterpret_cast<IndTri32*>(mMeshData.mGRB_triIndices);
+ IndTri32* cpuTriIndices = reinterpret_cast<IndTri32*>(mMeshData.mTriangles);
+ //map CPU remap triangle index to GPU remap triangle index
+ for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i)
+ {
+ PX_ASSERT(grbTriIndices[i].mRef[0] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[0]);
+ PX_ASSERT(grbTriIndices[i].mRef[1] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[1]);
+ PX_ASSERT(grbTriIndices[i].mRef[2] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[2]);
+ }
+#endif
+
+ if (orignalToRemap)
+ PX_DELETE_POD(orignalToRemap);
+
+ }
+}
+
+void TriangleMeshBuilder::createEdgeList()
+{
+ Gu::EDGELISTCREATE create;
+ create.NbFaces = mMeshData.mNbTriangles;
+ if(mMeshData.has16BitIndices())
+ {
+ create.DFaces = NULL;
+ create.WFaces = reinterpret_cast<PxU16*>(mMeshData.mTriangles);
+ }
+ else
+ {
+ create.DFaces = reinterpret_cast<PxU32*>(mMeshData.mTriangles);
+ create.WFaces = NULL;
+ }
+ create.FacesToEdges = true;
+ create.EdgesToFaces = true;
+ create.Verts = mMeshData.mVertices;
+ //create.Epsilon = 0.1f;
+ // create.Epsilon = convexEdgeThreshold;
+ edgeList = PX_NEW(Gu::EdgeListBuilder);
+ if(!edgeList->init(create))
+ {
+ PX_DELETE(edgeList);
+ edgeList = 0;
+ }
+}
+
+void TriangleMeshBuilder::releaseEdgeList()
+{
+ PX_DELETE_AND_RESET(edgeList);
+}
+
+//
+// When suppressTriangleMeshRemapTable is true, the face remap table is not created. This saves a significant amount of memory,
+// but the SDK will not be able to provide information about which mesh triangle is hit in collisions, sweeps or raycasts hits.
+//
+// The sequence is as follows:
+
+bool TriangleMeshBuilder::loadFromDesc(const PxTriangleMeshDesc& _desc, PxTriangleMeshCookingResult::Enum* condition, bool validateMesh)
+{
+ const PxU32 originalTriangleCount = _desc.triangles.count;
+ if(!_desc.isValid())
+ {
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh::loadFromDesc: desc.isValid() failed!");
+ return false;
+ }
+
+ // verify the mesh params
+ if(!mParams.midphaseDesc.isValid())
+ {
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh::loadFromDesc: mParams.midphaseDesc.isValid() failed!");
+ return false;
+ }
+
+ // Create a local copy that we can modify
+ PxTriangleMeshDesc desc = _desc;
+
+ // Save simple params
+ {
+ // Handle implicit topology
+ PxU32* topology = NULL;
+ if(!desc.triangles.data)
+ {
+ // We'll create 32-bit indices
+ desc.flags &= ~PxMeshFlag::e16_BIT_INDICES;
+ desc.triangles.stride = sizeof(PxU32)*3;
+
+ {
+ // Non-indexed mesh => create implicit topology
+ desc.triangles.count = desc.points.count/3;
+ // Create default implicit topology
+ topology = PX_NEW_TEMP(PxU32)[desc.points.count];
+ for(PxU32 i=0;i<desc.points.count;i++)
+ topology[i] = i;
+ desc.triangles.data = topology;
+ }
+ }
+ // Continue as usual using our new descriptor
+
+ // Convert and clean the input mesh
+ if (!importMesh(desc, mParams, condition, validateMesh))
+ return false;
+
+ // Cleanup if needed
+ PX_DELETE_POD(topology);
+ }
+
+
+ //copy the original triangle indices to grb triangle indices if buildGRBData is true
+ recordTriangleIndices();
+
+ createMidPhaseStructure();
+
+ // Compute local bounds
+ computeLocalBounds(); // AP scaffold: local bounds are already computed in builder.createRTree efficiently with SIMD
+
+ createSharedEdgeData(mParams.buildTriangleAdjacencies, !(mParams.meshPreprocessParams & PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE));
+
+ createGRBMidPhaseAndData(originalTriangleCount);
+
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool TriangleMeshBuilder::save(PxOutputStream& stream, bool platformMismatch, const PxCookingParams& params) const
+{
+ // Export header
+ if(!writeHeader('M', 'E', 'S', 'H', PX_MESH_VERSION, platformMismatch, stream))
+ return false;
+
+ // Export midphase ID
+ writeDword(getMidphaseID(), platformMismatch, stream);
+
+ // Export serialization flags
+ PxU32 serialFlags = 0;
+ if(mMeshData.mMaterialIndices) serialFlags |= Gu::IMSF_MATERIALS;
+ if(mMeshData.mFaceRemap) serialFlags |= Gu::IMSF_FACE_REMAP;
+ if(mMeshData.mAdjacencies) serialFlags |= Gu::IMSF_ADJACENCIES;
+ if (params.buildGPUData) serialFlags |= Gu::IMSF_GRB_DATA;
+ // Compute serialization flags for indices
+ PxU32 maxIndex=0;
+ const Gu::TriangleT<PxU32>* tris = reinterpret_cast<const Gu::TriangleT<PxU32>*>(mMeshData.mTriangles);
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++)
+ {
+ if(tris[i].v[0]>maxIndex) maxIndex = tris[i].v[0];
+ if(tris[i].v[1]>maxIndex) maxIndex = tris[i].v[1];
+ if(tris[i].v[2]>maxIndex) maxIndex = tris[i].v[2];
+ }
+
+ bool force32 = (params.meshPreprocessParams & PxMeshPreprocessingFlag::eFORCE_32BIT_INDICES);
+ if (maxIndex <= 0xFFFF && !force32)
+ serialFlags |= (maxIndex <= 0xFF ? Gu::IMSF_8BIT_INDICES : Gu::IMSF_16BIT_INDICES);
+ writeDword(serialFlags, platformMismatch, stream);
+
+ // Export mesh
+ writeDword(mMeshData.mNbVertices, platformMismatch, stream);
+ writeDword(mMeshData.mNbTriangles, platformMismatch, stream);
+ writeFloatBuffer(&mMeshData.mVertices->x, mMeshData.mNbVertices*3, platformMismatch, stream);
+ if(serialFlags & Gu::IMSF_8BIT_INDICES)
+ {
+ const PxU32* indices = tris->v;
+ for(PxU32 i=0;i<mMeshData.mNbTriangles*3;i++)
+ {
+ PxI8 data = PxI8(indices[i]);
+ stream.write(&data, sizeof(PxU8));
+ }
+ }
+ else if(serialFlags & Gu::IMSF_16BIT_INDICES)
+ {
+ const PxU32* indices = tris->v;
+ for(PxU32 i=0;i<mMeshData.mNbTriangles*3;i++)
+ writeWord(Ps::to16(indices[i]), platformMismatch, stream);
+ }
+ else
+ writeIntBuffer(tris->v, mMeshData.mNbTriangles*3, platformMismatch, stream);
+
+ if(mMeshData.mMaterialIndices)
+ writeWordBuffer(mMeshData.mMaterialIndices, mMeshData.mNbTriangles, platformMismatch, stream);
+
+ if(mMeshData.mFaceRemap)
+ {
+ PxU32 maxId = computeMaxIndex(mMeshData.mFaceRemap, mMeshData.mNbTriangles);
+ writeDword(maxId, platformMismatch, stream);
+ storeIndices(maxId, mMeshData.mNbTriangles, mMeshData.mFaceRemap, stream, platformMismatch);
+// writeIntBuffer(mMeshData.mFaceRemap, mMeshData.mNbTriangles, platformMismatch, stream);
+ }
+
+ if(mMeshData.mAdjacencies)
+ writeIntBuffer(mMeshData.mAdjacencies, mMeshData.mNbTriangles*3, platformMismatch, stream);
+
+ // Export midphase structure
+ saveMidPhaseStructure(stream);
+
+
+ // Export local bounds
+ writeFloat(mMeshData.mGeomEpsilon, platformMismatch, stream);
+
+ writeFloat(mMeshData.mAABB.minimum.x, platformMismatch, stream);
+ writeFloat(mMeshData.mAABB.minimum.y, platformMismatch, stream);
+ writeFloat(mMeshData.mAABB.minimum.z, platformMismatch, stream);
+ writeFloat(mMeshData.mAABB.maximum.x, platformMismatch, stream);
+ writeFloat(mMeshData.mAABB.maximum.y, platformMismatch, stream);
+ writeFloat(mMeshData.mAABB.maximum.z, platformMismatch, stream);
+
+ if(mMeshData.mExtraTrigData)
+ {
+ writeDword(mMeshData.mNbTriangles, platformMismatch, stream);
+ // No need to convert those bytes
+ stream.write(mMeshData.mExtraTrigData, mMeshData.mNbTriangles*sizeof(PxU8));
+ }
+ else
+ writeDword(0, platformMismatch, stream);
+
+ // GRB write -----------------------------------------------------------------
+ if (params.buildGPUData)
+ {
+ writeDword(mMeshData.mGRB_meshAdjVerticiesTotal, platformMismatch, stream);
+
+ const PxU32* indices = reinterpret_cast<PxU32*>(mMeshData.mGRB_triIndices);
+ if (serialFlags & Gu::IMSF_8BIT_INDICES)
+ {
+ for (PxU32 i = 0; i<mMeshData.mNbTriangles * 3; i++)
+ {
+ PxI8 data = PxI8(indices[i]);
+ stream.write(&data, sizeof(PxU8));
+ }
+ }
+ else if (serialFlags & Gu::IMSF_16BIT_INDICES)
+ {
+ for (PxU32 i = 0; i<mMeshData.mNbTriangles * 3; i++)
+ writeWord(Ps::to16(indices[i]), platformMismatch, stream);
+ }
+ else
+ writeIntBuffer(indices, mMeshData.mNbTriangles * 3, platformMismatch, stream);
+
+
+ //writeIntBuffer(reinterpret_cast<PxU32*>(mMeshData.mGRB_triIndices), , mMeshData.mNbTriangles*3, platformMismatch, stream);
+
+ //writeIntBuffer(reinterpret_cast<PxU32 *>(mMeshData.mGRB_triIndices), mMeshData.mNbTriangles*4, platformMismatch, stream);
+
+ writeIntBuffer(reinterpret_cast<PxU32 *>(mMeshData.mGRB_triAdjacencies), mMeshData.mNbTriangles*4, platformMismatch, stream);
+ writeIntBuffer(mMeshData.mGRB_vertValency, mMeshData.mNbVertices, platformMismatch, stream);
+ writeIntBuffer(mMeshData.mGRB_adjVertStart, mMeshData.mNbVertices, platformMismatch, stream);
+ writeIntBuffer(mMeshData.mGRB_adjVertices, mMeshData.mGRB_meshAdjVerticiesTotal, platformMismatch, stream);
+ writeIntBuffer(mMeshData.mGRB_faceRemap, mMeshData.mNbTriangles, platformMismatch, stream);
+
+ //Export GPU midphase structure
+ BV32Tree* bv32Tree = reinterpret_cast<BV32Tree*>(mMeshData.mGRB_BV32Tree);
+ BV32TriangleMeshBuilder::saveMidPhaseStructure(bv32Tree, stream);
+ }
+
+ // End of GRB write ----------------------------------------------------------
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if PX_VC
+#pragma warning(push)
+#pragma warning(disable:4996) // permitting use of gatherStrided until we have a replacement.
+#endif
+
+bool TriangleMeshBuilder::importMesh(const PxTriangleMeshDesc& desc,const PxCookingParams& params,PxTriangleMeshCookingResult::Enum* condition, bool validate)
+{
+ //convert and clean the input mesh
+ //this is where the mesh data gets copied from user mem to our mem
+
+ PxVec3* verts = mMeshData.allocateVertices(desc.points.count);
+ Gu::TriangleT<PxU32>* tris = reinterpret_cast<Gu::TriangleT<PxU32>*>(mMeshData.allocateTriangles(desc.triangles.count, true, PxU32(params.buildGPUData)));
+
+ //copy, and compact to get rid of strides:
+ Cooking::gatherStrided(desc.points.data, verts, mMeshData.mNbVertices, sizeof(PxVec3), desc.points.stride);
+
+#if PX_CHECKED
+ // PT: check all input vertices are valid
+ for(PxU32 i=0;i<desc.points.count;i++)
+ {
+ const PxVec3& p = verts[i];
+ if(!PxIsFinite(p.x) || !PxIsFinite(p.y) || !PxIsFinite(p.z))
+ {
+ Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "input mesh contains corrupted vertex data");
+ return false;
+ }
+ }
+#endif
+
+ //for trigs index stride conversion and eventual reordering is also needed, I don't think flexicopy can do that for us.
+
+ Gu::TriangleT<PxU32>* dest = tris;
+ const Gu::TriangleT<PxU32>* pastLastDest = tris + mMeshData.mNbTriangles;
+ const PxU8* source = reinterpret_cast<const PxU8*>(desc.triangles.data);
+
+ //4 combos of 16 vs 32 and flip vs no flip
+ PxU32 c = (desc.flags & PxMeshFlag::eFLIPNORMALS)?PxU32(1):0;
+ if (desc.flags & PxMeshFlag::e16_BIT_INDICES)
+ {
+ //index stride conversion is also needed, I don't think flexicopy can do that for us.
+ while (dest < pastLastDest)
+ {
+ const PxU16 * trig16 = reinterpret_cast<const PxU16*>(source);
+ dest->v[0] = trig16[0];
+ dest->v[1] = trig16[1+c];
+ dest->v[2] = trig16[2-c];
+ dest ++;
+ source += desc.triangles.stride;
+ }
+ }
+ else
+ {
+ while (dest < pastLastDest)
+ {
+ const PxU32 * trig32 = reinterpret_cast<const PxU32*>(source);
+ dest->v[0] = trig32[0];
+ dest->v[1] = trig32[1+c];
+ dest->v[2] = trig32[2-c];
+ dest ++;
+ source += desc.triangles.stride;
+ }
+ }
+
+ //copy the material index list if any:
+ if(desc.materialIndices.data)
+ {
+ PxMaterialTableIndex* materials = mMeshData.allocateMaterials();
+ Cooking::gatherStrided(desc.materialIndices.data, materials, mMeshData.mNbTriangles, sizeof(PxMaterialTableIndex), desc.materialIndices.stride);
+
+ // Check material indices
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++) PX_ASSERT(materials[i]!=0xffff);
+ }
+
+ // Clean the mesh using ICE's MeshBuilder
+ // This fixes the bug in ConvexTest06 where the inertia tensor computation fails for a mesh => it works with a clean mesh
+
+ if (!(params.meshPreprocessParams & PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH) || validate)
+ {
+ if(!cleanMesh(validate, condition))
+ {
+ if(!validate)
+ Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "cleaning the mesh failed");
+ return false;
+ }
+ }
+ else
+ {
+ // we need to fill the remap table if no cleaning was done
+ if(params.suppressTriangleMeshRemapTable == false)
+ {
+ PX_ASSERT(mMeshData.mFaceRemap == NULL);
+ mMeshData.mFaceRemap = PX_NEW(PxU32)[mMeshData.mNbTriangles];
+ for (PxU32 i = 0; i < mMeshData.mNbTriangles; i++)
+ mMeshData.mFaceRemap[i] = i;
+ }
+ }
+ return true;
+}
+
+#if PX_VC
+#pragma warning(pop)
+#endif
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//#define PROFILE_BOUNDS
+#ifdef PROFILE_BOUNDS
+ #include <windows.h>
+ #pragma comment(lib, "winmm.lib")
+#endif
+
+void TriangleMeshBuilder::computeLocalBounds()
+{
+#ifdef PROFILE_BOUNDS
+ int time = timeGetTime();
+#endif
+
+ PxBounds3& localBounds = mMeshData.mAABB;
+ computeBoundsAroundVertices(localBounds, mMeshData.mNbVertices, mMeshData.mVertices);
+
+ // Derive a good geometric epsilon from local bounds. We must do this before bounds extrusion for heightfields.
+ //
+ // From Charles Bloom:
+ // "Epsilon must be big enough so that the consistency condition abs(D(Hit))
+ // <= Epsilon is satisfied for all queries. You want the smallest epsilon
+ // you can have that meets that constraint. Normal floats have a 24 bit
+ // mantissa. When you do any float addition, you may have round-off error
+ // that makes the result off by roughly 2^-24 * result. Our result is
+ // scaled by the position values. If our world is strictly required to be
+ // in a box of world size W (each coordinate in -W to W), then the maximum
+ // error is 2^-24 * W. Thus Epsilon must be at least >= 2^-24 * W. If
+ // you're doing coordinate transforms, that may scale your error up by some
+ // amount, so you'll need a bigger epsilon. In general something like
+ // 2^-22*W is reasonable. If you allow scaled transforms, it needs to be
+ // something like 2^-22*W*MAX_SCALE."
+ // PT: TODO: runtime checkings for this
+ PxReal geomEpsilon = 0.0f;
+ for (PxU32 i = 0; i < 3; i++)
+ geomEpsilon = PxMax(geomEpsilon, PxMax(PxAbs(localBounds.maximum[i]), PxAbs(localBounds.minimum[i])));
+ geomEpsilon *= powf(2.0f, -22.0f);
+ mMeshData.mGeomEpsilon = geomEpsilon;
+
+#ifdef PROFILE_BOUNDS
+ int deltaTime = timeGetTime() - time;
+ printf("Bounds time: %f\n", float(deltaTime)*0.001f);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void TriangleMeshBuilder::checkMeshIndicesSize()
+{
+ Gu::TriangleMeshData& m = mMeshData;
+
+ // check if we can change indices from 32bits to 16bits
+ if(m.mNbVertices <= 0xffff && !m.has16BitIndices())
+ {
+ const PxU32 numTriangles = m.mNbTriangles;
+ PxU32* PX_RESTRICT indices32 = reinterpret_cast<PxU32*> (m.mTriangles);
+
+ m.mTriangles = 0; // force a realloc
+ m.allocateTriangles(numTriangles, false);
+ PX_ASSERT(m.has16BitIndices()); // realloc'ing without the force32bit flag changed it.
+
+ PxU16* PX_RESTRICT indices16 = reinterpret_cast<PxU16*> (m.mTriangles);
+ for (PxU32 i = 0; i < numTriangles*3; i++)
+ indices16[i] = Ps::to16(indices32[i]);
+
+ PX_FREE(indices32);
+
+ onMeshIndexFormatChange();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+BV4TriangleMeshBuilder::BV4TriangleMeshBuilder(const PxCookingParams& params) : TriangleMeshBuilder(mData, params)
+{
+}
+
+BV4TriangleMeshBuilder::~BV4TriangleMeshBuilder()
+{
+}
+
+void BV4TriangleMeshBuilder::onMeshIndexFormatChange()
+{
+ IndTri32* triangles32 = NULL;
+ IndTri16* triangles16 = NULL;
+ if(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES)
+ triangles16 = reinterpret_cast<IndTri16*>(mMeshData.mTriangles);
+ else
+ triangles32 = reinterpret_cast<IndTri32*>(mMeshData.mTriangles);
+
+ mData.mMeshInterface.setPointers(triangles32, triangles16, mMeshData.mVertices);
+}
+
+void BV4TriangleMeshBuilder::createMidPhaseStructure()
+{
+ const float gBoxEpsilon = 2e-4f;
+// const float gBoxEpsilon = 0.1f;
+ mData.mMeshInterface.initRemap();
+ mData.mMeshInterface.setNbVertices(mMeshData.mNbVertices);
+ mData.mMeshInterface.setNbTriangles(mMeshData.mNbTriangles);
+
+ IndTri32* triangles32 = NULL;
+ IndTri16* triangles16 = NULL;
+ if (mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES)
+ {
+ triangles16 = reinterpret_cast<IndTri16*>(mMeshData.mTriangles);
+ }
+ else
+ {
+ triangles32 = reinterpret_cast<IndTri32*>(mMeshData.mTriangles);
+ }
+
+ mData.mMeshInterface.setPointers(triangles32, triangles16, mMeshData.mVertices);
+
+ const PxU32 nbTrisPerLeaf = (mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH34) ? mParams.midphaseDesc.mBVH34Desc.numTrisPerLeaf : 4;
+
+ if(!BuildBV4Ex(mData.mBV4Tree, mData.mMeshInterface, gBoxEpsilon, nbTrisPerLeaf))
+ {
+ Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV4 tree failed to build.");
+ return;
+ }
+
+// remapTopology(mData.mMeshInterface);
+
+ const PxU32* order = mData.mMeshInterface.getRemap();
+ if(mMeshData.mMaterialIndices)
+ {
+ PxMaterialTableIndex* newMat = PX_NEW(PxMaterialTableIndex)[mMeshData.mNbTriangles];
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++)
+ newMat[i] = mMeshData.mMaterialIndices[order[i]];
+ PX_DELETE_POD(mMeshData.mMaterialIndices);
+ mMeshData.mMaterialIndices = newMat;
+ }
+
+ if (!mParams.suppressTriangleMeshRemapTable || mParams.buildGPUData)
+ {
+ PxU32* newMap = PX_NEW(PxU32)[mMeshData.mNbTriangles];
+ for(PxU32 i=0;i<mMeshData.mNbTriangles;i++)
+ newMap[i] = mMeshData.mFaceRemap ? mMeshData.mFaceRemap[order[i]] : order[i];
+ PX_DELETE_POD(mMeshData.mFaceRemap);
+ mMeshData.mFaceRemap = newMap;
+ }
+ mData.mMeshInterface.releaseRemap();
+}
+
+void BV4TriangleMeshBuilder::saveMidPhaseStructure(PxOutputStream& stream) const
+{
+ const PxU32 version = 1;
+
+ const bool mismatch = (littleEndian() == 1);
+ writeChunk('B', 'V', '4', ' ', stream);
+ writeDword(version, mismatch, stream);
+
+ writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.x, mismatch, stream);
+ writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.y, mismatch, stream);
+ writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.z, mismatch, stream);
+ writeFloat(mData.mBV4Tree.mLocalBounds.mExtentsMagnitude, mismatch, stream);
+
+ writeDword(mData.mBV4Tree.mInitData, mismatch, stream);
+#ifdef GU_BV4_QUANTIZED_TREE
+ writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.x, mismatch, stream);
+ writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.y, mismatch, stream);
+ writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.z, mismatch, stream);
+ writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.x, mismatch, stream);
+ writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.y, mismatch, stream);
+ writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.z, mismatch, stream);
+#endif
+ writeDword(mData.mBV4Tree.mNbNodes, mismatch, stream);
+ for(PxU32 i=0;i<mData.mBV4Tree.mNbNodes;i++)
+ {
+ const BVDataPacked& node = mData.mBV4Tree.mNodes[i];
+#ifdef GU_BV4_QUANTIZED_TREE
+ writeWordBuffer(&node.mAABB.mData[0].mExtents, 6, mismatch, stream);
+#else
+ writeFloatBuffer(&node.mAABB.mCenter.x, 6, mismatch, stream);
+#endif
+ writeDword(node.mData, mismatch, stream);
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void BV32TriangleMeshBuilder::createMidPhaseStructure(const PxCookingParams& params, Gu::TriangleMeshData& meshData, Gu::BV32Tree& bv32Tree)
+{
+ const float gBoxEpsilon = 2e-4f;
+
+ Gu::SourceMesh meshInterface;
+ // const float gBoxEpsilon = 0.1f;
+ meshInterface.initRemap();
+ meshInterface.setNbVertices(meshData.mNbVertices);
+ meshInterface.setNbTriangles(meshData.mNbTriangles);
+
+ PX_ASSERT(!(meshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES));
+
+ IndTri32* triangles32 = reinterpret_cast<IndTri32*>(meshData.mGRB_triIndices);
+
+ meshInterface.setPointers(triangles32, NULL, meshData.mVertices);
+
+ PxU32 nbTrisPerLeaf = 32;
+
+ if (!BuildBV32Ex(bv32Tree, meshInterface, gBoxEpsilon, nbTrisPerLeaf))
+ {
+ Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV32 tree failed to build.");
+ return;
+ }
+
+ const PxU32* order = meshInterface.getRemap();
+
+ if (!params.suppressTriangleMeshRemapTable || params.buildGPUData)
+ {
+ PxU32* newMap = PX_NEW(PxU32)[meshData.mNbTriangles];
+ for (PxU32 i = 0; i<meshData.mNbTriangles; i++)
+ newMap[i] = meshData.mGRB_faceRemap ? meshData.mGRB_faceRemap[order[i]] : order[i];
+ PX_DELETE_POD(meshData.mGRB_faceRemap);
+ meshData.mGRB_faceRemap = newMap;
+ }
+
+ meshInterface.releaseRemap();
+
+}
+
+void BV32TriangleMeshBuilder::saveMidPhaseStructure(Gu::BV32Tree* bv32Tree, PxOutputStream& stream)
+{
+ const PxU32 version = 1;
+
+ const bool mismatch = (littleEndian() == 1);
+ writeChunk('B', 'V', '3', '2', stream);
+ writeDword(version, mismatch, stream);
+
+ writeFloat(bv32Tree->mLocalBounds.mCenter.x, mismatch, stream);
+ writeFloat(bv32Tree->mLocalBounds.mCenter.y, mismatch, stream);
+ writeFloat(bv32Tree->mLocalBounds.mCenter.z, mismatch, stream);
+ writeFloat(bv32Tree->mLocalBounds.mExtentsMagnitude, mismatch, stream);
+
+ writeDword(bv32Tree->mInitData, mismatch, stream);
+
+ writeDword(bv32Tree->mNbPackedNodes, mismatch, stream);
+
+ PX_ASSERT(bv32Tree->mNbPackedNodes > 0);
+ for (PxU32 i = 0; i < bv32Tree->mNbPackedNodes; ++i)
+ {
+ BV32DataPacked& node = bv32Tree->mPackedNodes[i];
+
+ const PxU32 nbElements = node.mNbNodes * 4;
+ writeDword(node.mNbNodes, mismatch, stream);
+ WriteDwordBuffer(node.mData, node.mNbNodes, mismatch, stream);
+ writeFloatBuffer(&node.mCenter[0].x, nbElements, mismatch, stream);
+ writeFloatBuffer(&node.mExtents[0].x, nbElements, mismatch, stream);
+
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+RTreeTriangleMeshBuilder::RTreeTriangleMeshBuilder(const PxCookingParams& params) : TriangleMeshBuilder(mData, params)
+{
+}
+
+RTreeTriangleMeshBuilder::~RTreeTriangleMeshBuilder()
+{
+}
+
+struct RTreeCookerRemap : RTreeCooker::RemapCallback
+{
+ PxU32 mNbTris;
+ RTreeCookerRemap(PxU32 numTris) : mNbTris(numTris)
+ {
+ }
+
+ virtual void remap(PxU32* val, PxU32 start, PxU32 leafCount)
+ {
+ PX_ASSERT(leafCount > 0);
+ PX_ASSERT(leafCount <= 16); // sanity check
+ PX_ASSERT(start < mNbTris);
+ PX_ASSERT(start+leafCount <= mNbTris);
+ PX_ASSERT(val);
+ LeafTriangles lt;
+ // here we remap from ordered leaf index in the rtree to index in post-remap in triangles
+ // this post-remap will happen later
+ lt.SetData(leafCount, start);
+ *val = lt.Data;
+ }
+};
+
+void RTreeTriangleMeshBuilder::createMidPhaseStructure()
+{
+ const PxReal meshSizePerformanceTradeOff = (mParams.midphaseDesc.getType() == PxMeshMidPhase::eINVALID) ?
+ mParams.meshSizePerformanceTradeOff : mParams.midphaseDesc.mBVH33Desc.meshSizePerformanceTradeOff;
+ const PxMeshCookingHint::Enum meshCookingHint = (mParams.midphaseDesc.getType() == PxMeshMidPhase::eINVALID) ?
+ mParams.meshCookingHint : mParams.midphaseDesc.mBVH33Desc.meshCookingHint;
+
+ Array<PxU32> resultPermute;
+ RTreeCookerRemap rc(mMeshData.mNbTriangles);
+ RTreeCooker::buildFromTriangles(
+ mData.mRTree,
+ mMeshData.mVertices, mMeshData.mNbVertices,
+ (mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? reinterpret_cast<PxU16*>(mMeshData.mTriangles) : NULL,
+ !(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? reinterpret_cast<PxU32*>(mMeshData.mTriangles) : NULL,
+ mMeshData.mNbTriangles, resultPermute, &rc, meshSizePerformanceTradeOff, meshCookingHint);
+
+ PX_ASSERT(resultPermute.size() == mMeshData.mNbTriangles);
+
+ remapTopology(resultPermute.begin());
+}
+
+static void saveRTreeData(PxOutputStream& stream, const RTree& d)
+{
+ // save the RTree root structure followed immediately by RTreePage pages to an output stream
+ const bool mismatch = (littleEndian() == 1);
+ writeChunk('R', 'T', 'R', 'E', stream);
+ writeDword(RTREE_COOK_VERSION, mismatch, stream);
+ writeFloatBuffer(&d.mBoundsMin.x, 4, mismatch, stream);
+ writeFloatBuffer(&d.mBoundsMax.x, 4, mismatch, stream);
+ writeFloatBuffer(&d.mInvDiagonal.x, 4, mismatch, stream);
+ writeFloatBuffer(&d.mDiagonalScaler.x, 4, mismatch, stream);
+ writeDword(d.mPageSize, mismatch, stream);
+ writeDword(d.mNumRootPages, mismatch, stream);
+ writeDword(d.mNumLevels, mismatch, stream);
+ writeDword(d.mTotalNodes, mismatch, stream);
+ writeDword(d.mTotalPages, mismatch, stream);
+ PxU32 unused = 0; writeDword(unused, mismatch, stream); // backwards compatibility
+ for (PxU32 j = 0; j < d.mTotalPages; j++)
+ {
+ writeFloatBuffer(d.mPages[j].minx, RTREE_N, mismatch, stream);
+ writeFloatBuffer(d.mPages[j].miny, RTREE_N, mismatch, stream);
+ writeFloatBuffer(d.mPages[j].minz, RTREE_N, mismatch, stream);
+ writeFloatBuffer(d.mPages[j].maxx, RTREE_N, mismatch, stream);
+ writeFloatBuffer(d.mPages[j].maxy, RTREE_N, mismatch, stream);
+ writeFloatBuffer(d.mPages[j].maxz, RTREE_N, mismatch, stream);
+ WriteDwordBuffer(d.mPages[j].ptrs, RTREE_N, mismatch, stream);
+ }
+}
+
+void RTreeTriangleMeshBuilder::saveMidPhaseStructure(PxOutputStream& stream) const
+{
+ // Export RTree
+ saveRTreeData(stream, mData.mRTree);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}