// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2019 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 "geometry/PxTriangle.h" #include "EdgeList.h" #include "CmRadixSortBuffered.h" #include "GuSerialize.h" #include "PsMathUtils.h" #include "PsFoundation.h" using namespace physx; using namespace Gu; Gu::EdgeList::EdgeList() { mData.mNbEdges = 0; mData.mEdgeFaces = NULL; mData.mEdges = NULL; mData.mEdgeToTriangles = NULL; mData.mFacesByEdges = NULL; } Gu::EdgeList::~EdgeList() { PX_FREE_AND_RESET(mData.mFacesByEdges); PX_FREE_AND_RESET(mData.mEdgeToTriangles); PX_FREE_AND_RESET(mData.mEdges); PX_DELETE_POD(mData.mEdgeFaces); } bool Gu::EdgeList::load(PxInputStream& stream) { // Import header PxU32 Version; bool Mismatch; if(!ReadHeader('E', 'D', 'G', 'E', Version, Mismatch, stream)) return false; // Import edges mData.mNbEdges = readDword(Mismatch, stream); //mEdges = ICE_NEW_MEM(Edge[mNbEdges],Edge); mData.mEdges = reinterpret_cast(PX_ALLOC(sizeof(EdgeData)*mData.mNbEdges, "EdgeData")); stream.read(mData.mEdges, sizeof(EdgeData)*mData.mNbEdges); mData.mNbFaces = readDword(Mismatch, stream); //mEdgeFaces = ICE_NEW_MEM(EdgeTriangle[mNbFaces],EdgeTriangle); mData.mEdgeFaces = reinterpret_cast(PX_ALLOC(sizeof(EdgeTriangleData)*mData.mNbFaces, "EdgeTriangleData")); stream.read(mData.mEdgeFaces, sizeof(EdgeTriangleData)*mData.mNbFaces); //mEdgeToTriangles = ICE_NEW_MEM(EdgeDesc[mNbEdges],EdgeDesc); mData.mEdgeToTriangles = reinterpret_cast(PX_ALLOC(sizeof(EdgeDescData)*mData.mNbEdges, "EdgeDescData")); stream.read(mData.mEdgeToTriangles, sizeof(EdgeDescData)*mData.mNbEdges); PxU32 LastOffset = mData.mEdgeToTriangles[mData.mNbEdges-1].Offset + mData.mEdgeToTriangles[mData.mNbEdges-1].Count; mData.mFacesByEdges = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*LastOffset, "EdgeList FacesByEdges")); stream.read(mData.mFacesByEdges, sizeof(PxU32)*LastOffset); return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Initializes the edge-list. * \param create [in] edge-list creation structure * \return true if success. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Gu::EdgeListBuilder::init(const EDGELISTCREATE& create) { bool FacesToEdges = create.Verts ? true : create.FacesToEdges; bool EdgesToFaces = create.Verts ? true : create.EdgesToFaces; // "FacesToEdges" maps each face to three edges. if(FacesToEdges && !createFacesToEdges(create.NbFaces, create.DFaces, create.WFaces)) return false; // "EdgesToFaces" maps each edge to the set of faces sharing this edge if(EdgesToFaces && !createEdgesToFaces(create.NbFaces, create.DFaces, create.WFaces)) return false; // Create active edges if(create.Verts && !computeActiveEdges(create.NbFaces, create.DFaces, create.WFaces, create.Verts, create.Epsilon)) return false; // Get rid of useless data if(!create.FacesToEdges) { PX_FREE_AND_RESET(mData.mEdgeFaces); } if(!create.EdgesToFaces) { PX_FREE_AND_RESET(mData.mEdgeToTriangles); PX_FREE_AND_RESET(mData.mFacesByEdges); } return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Computes FacesToEdges. * After the call: * - mNbEdges is updated with the number of non-redundant edges * - mEdges is a list of mNbEdges edges (one edge is 2 vertex-references) * - mEdgesRef is a list of nbfaces structures with 3 indexes in mEdges for each face * * \param nb_faces [in] a number of triangles * \param dfaces [in] list of triangles with PxU32 vertex references (or NULL) * \param wfaces [in] list of triangles with PxU16 vertex references (or NULL) * \return true if success. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Gu::EdgeListBuilder::createFacesToEdges(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces) { // Checkings if(!nb_faces || (!dfaces && !wfaces)) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "EdgeList::CreateFacesToEdges: NULL parameter!"); return false; } if(mData.mEdgeFaces) return true; // Already computed! // 1) Get some bytes: I need one EdgesRefs for each face, and some temp buffers mData.mEdgeFaces = PX_NEW(EdgeTriangleData)[nb_faces]; // Link faces to edges PxU32* VRefs0 = PX_NEW_TEMP(PxU32)[nb_faces*3]; // Temp storage PxU32* VRefs1 = PX_NEW_TEMP(PxU32)[nb_faces*3]; // Temp storage EdgeData* Buffer = PX_NEW_TEMP(EdgeData)[nb_faces*3]; // Temp storage // 2) Create a full redundant list of 3 edges / face. for(PxU32 i=0;i stored in temp buffer Buffer[mData.mNbEdges].Ref0 = SortedRef0; Buffer[mData.mNbEdges].Ref1 = SortedRef1; mData.mNbEdges++; } PreviousRef0 = SortedRef0; PreviousRef1 = SortedRef1; // Create mEdgesRef on the fly mData.mEdgeFaces[Face/3].mLink[ID] = mData.mNbEdges-1; } // 5) Here, mNbEdges==#non redundant edges mData.mEdges = reinterpret_cast(PX_ALLOC(sizeof(EdgeData)*mData.mNbEdges, "EdgeData")); // Create real edges-list. PxMemCopy(mData.mEdges, Buffer, mData.mNbEdges*sizeof(EdgeData)); // 6) Free ram and exit PX_DELETE_POD(Buffer); PX_DELETE_POD(VRefs1); PX_DELETE_POD(VRefs0); return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Computes EdgesToFaces. * After the call: * - mEdgeToTriangles is created * - mFacesByEdges is created * * \param nb_faces [in] a number of triangles * \param dfaces [in] list of triangles with PxU32 vertex references (or NULL) * \param wfaces [in] list of triangles with PxU16 vertex references (or NULL) * \return true if success. */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Gu::EdgeListBuilder::createEdgesToFaces(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces) { // 1) I need FacesToEdges ! if(!createFacesToEdges(nb_faces, dfaces, wfaces)) return false; // 2) Get some bytes: one Pair structure / edge mData.mEdgeToTriangles = reinterpret_cast(PX_ALLOC(sizeof(EdgeDescData)*mData.mNbEdges, "EdgeDescData")); PxMemZero(mData.mEdgeToTriangles, sizeof(EdgeDescData)*mData.mNbEdges); // 3) Create Counters, ie compute the #faces sharing each edge for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU32)*LastOffset, "EdgeListBuilder FacesByEdges")); // 5) Create mFacesByEdges for(PxU32 i=0;i(PX_ALLOC_TEMP(sizeof(bool)*NbEdges, "bool")); // Loop through edges and look for convex ones bool* CurrentMark = ActiveEdges; while(NbEdges--) { // Get number of triangles sharing current edge PxU32 Count = ED->Count; // Boundary edges are active => keep them (actually they're silhouette edges directly) // Internal edges can be active => test them // Singular edges ? => discard them bool Active = false; if(Count==1) { Active = true; } else if(Count==2) { PxU32 FaceIndex0 = FBE[ED->Offset+0]*3; PxU32 FaceIndex1 = FBE[ED->Offset+1]*3; PxU32 VRef00, VRef01, VRef02; PxU32 VRef10, VRef11, VRef12; if(dfaces) { VRef00 = dfaces[FaceIndex0+0]; VRef01 = dfaces[FaceIndex0+1]; VRef02 = dfaces[FaceIndex0+2]; VRef10 = dfaces[FaceIndex1+0]; VRef11 = dfaces[FaceIndex1+1]; VRef12 = dfaces[FaceIndex1+2]; } else //if(wfaces) { PX_ASSERT(wfaces); VRef00 = wfaces[FaceIndex0+0]; VRef01 = wfaces[FaceIndex0+1]; VRef02 = wfaces[FaceIndex0+2]; VRef10 = wfaces[FaceIndex1+0]; VRef11 = wfaces[FaceIndex1+1]; VRef12 = wfaces[FaceIndex1+2]; } { // We first check the opposite vertex against the plane PxU32 Op = OppositeVertex(VRef00, VRef01, VRef02, Edges->Ref0, Edges->Ref1); PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges { PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); PxVec3 N0, N1; T0.normal(N0); T1.normal(N1); const float a = Ps::angle(N0, N1); if(fabsf(a)>epsilon) Active = true; } else { PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); PxVec3 N0, N1; T0.normal(N0); T1.normal(N1); if(N0.dot(N1) < -0.999f) { Active = true; } } } } else { //Connected to more than 2 //We need to loop through the triangles and count the number of unique triangles (considering back-face triangles as non-unique). If we end up with more than 2 unique triangles, //then by definition this is an inactive edge. However, if we end up with 2 unique triangles (say like a double-sided tesselated surface), then it depends on the same rules as above PxU32 FaceInd0 = FBE[ED->Offset]*3; PxU32 VRef00, VRef01, VRef02; PxU32 VRef10=0, VRef11=0, VRef12=0; if(dfaces) { VRef00 = dfaces[FaceInd0+0]; VRef01 = dfaces[FaceInd0+1]; VRef02 = dfaces[FaceInd0+2]; } else //if(wfaces) { PX_ASSERT(wfaces); VRef00 = wfaces[FaceInd0+0]; VRef01 = wfaces[FaceInd0+1]; VRef02 = wfaces[FaceInd0+2]; } PxU32 numUniqueTriangles = 1; bool doubleSided0 = false; bool doubleSided1 = 0; for(PxU32 a = 1; a < Count; ++a) { PxU32 FaceInd = FBE[ED->Offset+a]*3; PxU32 VRef0, VRef1, VRef2; if(dfaces) { VRef0 = dfaces[FaceInd+0]; VRef1 = dfaces[FaceInd+1]; VRef2 = dfaces[FaceInd+2]; } else //if(wfaces) { PX_ASSERT(wfaces); VRef0 = wfaces[FaceInd+0]; VRef1 = wfaces[FaceInd+1]; VRef2 = wfaces[FaceInd+2]; } if(((VRef0 != VRef00) && (VRef0 != VRef01) && (VRef0 != VRef02)) || ((VRef1 != VRef00) && (VRef1 != VRef01) && (VRef1 != VRef02)) || ((VRef2 != VRef00) && (VRef2 != VRef01) && (VRef2 != VRef02))) { //Not the same as trig 0 if(numUniqueTriangles == 2) { if(((VRef0 != VRef10) && (VRef0 != VRef11) && (VRef0 != VRef12)) || ((VRef1 != VRef10) && (VRef1 != VRef11) && (VRef1 != VRef12)) || ((VRef2 != VRef10) && (VRef2 != VRef11) && (VRef2 != VRef12))) { //Too many unique triangles - terminate and mark as inactive numUniqueTriangles++; break; } else { PxTriangle T0(verts[VRef10], verts[VRef11], verts[VRef12]); PxTriangle T1(verts[VRef0], verts[VRef1], verts[VRef2]); PxVec3 N0, N1; T0.normal(N0); T1.normal(N1); if(N0.dot(N1) < -0.999f) doubleSided1 = true; } } else { VRef10 = VRef0; VRef11 = VRef1; VRef12 = VRef2; numUniqueTriangles++; } } else { //Check for double sided... PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); PxTriangle T1(verts[VRef0], verts[VRef1], verts[VRef2]); PxVec3 N0, N1; T0.normal(N0); T1.normal(N1); if(N0.dot(N1) < -0.999f) doubleSided0 = true; } } if(numUniqueTriangles == 1) Active = true; if(numUniqueTriangles == 2) { //Potentially active. Let's check the angles between the surfaces... if(doubleSided0 || doubleSided1) { // Plane PL1 = faces[FBE[ED->Offset+1]].PlaneEquation(verts); PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); // if(PL1.Distance(verts[Op])<-epsilon) Active = true; //if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges //KS - can't test signed distance for concave edges. This is a double-sided poly { PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); PxVec3 N0, N1; T0.normal(N0); T1.normal(N1); const float a = Ps::angle(N0, N1); if(fabsf(a)>epsilon) Active = true; } } else { //Not double sided...must have had a bunch of duplicate triangles!!!! //Treat as normal PxU32 Op = OppositeVertex(VRef00, VRef01, VRef02, Edges->Ref0, Edges->Ref1); // Plane PL1 = faces[FBE[ED->Offset+1]].PlaneEquation(verts); PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); // if(PL1.Distance(verts[Op])<-epsilon) Active = true; if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges { PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); PxVec3 N0, N1; T0.normal(N0); T1.normal(N1); const float a = Ps::angle(N0, N1); if(fabsf(a)>epsilon) Active = true; } } } else { //Lots of triangles all smooshed together. Just activate the edge in this case Active = true; } } *CurrentMark++ = Active; ED++; Edges++; } // Now copy bits back into already existing edge structures // - first in edge triangles for(PxU32 i=0;iMaxIndex) MaxIndex = VRef0; if(VRef1>MaxIndex) MaxIndex = VRef1; if(VRef2>MaxIndex) MaxIndex = VRef2; } MaxIndex++; bool* ActiveVerts = reinterpret_cast(PX_ALLOC_TEMP(sizeof(bool)*MaxIndex, "bool")); PxMemZero(ActiveVerts, MaxIndex*sizeof(bool)); PX_ASSERT(dfaces || wfaces); for(PxU32 i=0;i mark edge vertices as active PxU32 r0, r1; if(j==0) { r0=0; r1=1; } else if(j==1) { r0=1; r1=2; } else /*if(j==2)*/ { PX_ASSERT(j==2); r0=0; r1=2; } ActiveVerts[VRef[r0]] = ActiveVerts[VRef[r1]] = true; } } } /* for(PxU32 i=0;i mark edge vertices as inactive PxU32 r0, r1; if(j==0) { r0=0; r1=1; } if(j==1) { r0=1; r1=2; } if(j==2) { r0=0; r1=2; } ActiveVerts[VRef[r0]] = ActiveVerts[VRef[r1]] = false; } } }*/ // Now stuff this into the structure for(PxU32 i=0;i