// // 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) 2018 NVIDIA Corporation. All rights reserved. #include "Hull2MeshEdges.h" #include "PsUserAllocated.h" #include "PxVec4.h" #include "PxVec3.h" #include "PxPlane.h" #include "PxMath.h" #include "PxTransform.h" #include "PsArray.h" #define FLT_EPS 0.000000001f PX_INLINE bool intersectPlanes(physx::PxVec3& pos, physx::PxVec3& dir, const physx::PxPlane& plane0, const physx::PxPlane& plane1) { const physx::PxVec3 n0 = plane0.n; const physx::PxVec3 n1 = plane1.n; dir = n0.cross(n1); if(dir.normalize() < FLT_EPS) { return false; } pos = physx::PxVec3(0.0f); for (int iter = 3; iter--;) { // Project onto plane0: pos = plane0.project(pos); // Raycast to plane1: const physx::PxVec3 b = dir.cross(n0); //pos = pos - (pos.dot(plane1.n)/b.dot(plane1.n))*b; pos = pos - (plane1.distance(pos) / b.dot(plane1.n))*b; } return true; } void calculatePolygonEdges(physx::shdfnd::Array& edges,uint32_t planeCount,const physx::PxPlane *planes) { for (uint32_t i = 0; i FLT_EPS) { const float s = num/den; if (den > 0.0f) { maxS = physx::PxMin(maxS, s); } else { minS = physx::PxMax(minS, s); } if (maxS <= minS) { intersectionFound = false; break; } } else if (num < -10*FLT_EPS) { intersectionFound = false; break; } } if (intersectionFound && minS != -FLT_MAX && maxS != FLT_MIN) { HullEdge& s = edges.insert(); s.e0 = orig + minS * edgeDir; s.e1 = orig + maxS * edgeDir; } } } } } struct ConvexMeshBuilder { ConvexMeshBuilder(void) : mVertices("ConvexMeshBuilder::mVertice"), mIndices("ConvexMeshBuilder::mIndices") {} void buildMesh(uint16_t numPlanes,const physx::PxVec4 *planes); physx::shdfnd::Array mVertices; physx::shdfnd::Array mIndices; }; static float det(physx::PxVec4 v0, physx::PxVec4 v1, physx::PxVec4 v2, physx::PxVec4 v3) { const physx::PxVec3& d0 = reinterpret_cast(v0); const physx::PxVec3& d1 = reinterpret_cast(v1); const physx::PxVec3& d2 = reinterpret_cast(v2); const physx::PxVec3& d3 = reinterpret_cast(v3); return v0.w * d1.cross(d2).dot(d3) - v1.w * d0.cross(d2).dot(d3) + v2.w * d0.cross(d1).dot(d3) - v3.w * d0.cross(d1).dot(d2); } static physx::PxVec3 intersect(physx::PxVec4 p0, physx::PxVec4 p1, physx::PxVec4 p2) { const physx::PxVec3& d0 = reinterpret_cast(p0); const physx::PxVec3& d1 = reinterpret_cast(p1); const physx::PxVec3& d2 = reinterpret_cast(p2); return (p0.w * d1.cross(d2) + p1.w * d2.cross(d0) + p2.w * d0.cross(d1)) / d0.dot(d2.cross(d1)); } static const uint16_t sInvalid = uint16_t(-1); // restriction: only supports a single patch per vertex. struct HalfedgeMesh { struct Halfedge { Halfedge(uint16_t vertex = sInvalid, uint16_t face = sInvalid, uint16_t next = sInvalid, uint16_t prev = sInvalid) : mVertex(vertex), mFace(face), mNext(next), mPrev(prev) {} uint16_t mVertex; // to uint16_t mFace; // left uint16_t mNext; // ccw uint16_t mPrev; // cw }; HalfedgeMesh() : mNumTriangles(0), mHalfedges("mHalfedges"),mVertices("mVertices"),mFaces("mFaces"),mPoints("mPoints") {} uint16_t findHalfedge(uint16_t v0, uint16_t v1) { uint16_t h = mVertices[v0], start = h; while(h != sInvalid && mHalfedges[h].mVertex != v1) { h = mHalfedges[h ^ 1u].mNext; if(h == start) return sInvalid; } return h; } void connect(uint16_t h0, uint16_t h1) { mHalfedges[h0].mNext = h1; mHalfedges[h1].mPrev = h0; } void addTriangle(uint16_t v0, uint16_t v1, uint16_t v2) { // add new vertices uint16_t n = physx::PxMax(v0, physx::PxMax(v1, v2))+1u; if(mVertices.size() < n) mVertices.resize(n, sInvalid); // collect halfedges, prev and next of triangle uint16_t verts[] = { v0, v1, v2 }; uint16_t handles[3], prev[3], next[3]; for(uint16_t i=0; i<3; ++i) { uint16_t j = (i+1u)%3; uint16_t h = findHalfedge(verts[i], verts[j]); if(h == sInvalid) { // add new edge h = uint16_t(mHalfedges.size()); mHalfedges.pushBack(Halfedge(verts[j])); mHalfedges.pushBack(Halfedge(verts[i])); } handles[i] = h; prev[i] = mHalfedges[h].mPrev; next[i] = mHalfedges[h].mNext; } // patch connectivity for(uint16_t i=0; i<3; ++i) { uint16_t j = (i+1u)%3; mHalfedges[handles[i]].mFace = uint16_t(mFaces.size()); // connect prev and next connect(handles[i], handles[j]); if(next[j] == sInvalid) // new next edge, connect opposite connect(handles[j]^1u, next[i]!=sInvalid ? next[i] : handles[i]^1u); if(prev[i] == sInvalid) // new prev edge, connect opposite connect(prev[j]!=sInvalid ? prev[j] : handles[j]^1u, handles[i]^1u); // prev is boundary, update middle vertex if(mHalfedges[handles[i]^1u].mFace == sInvalid) mVertices[verts[j]] = handles[i]^1u; } PX_ASSERT(mNumTriangles < 0xffff); mFaces.pushBack(handles[2]); ++mNumTriangles; } uint16_t removeTriangle(uint16_t f) { uint16_t result = sInvalid; for(uint16_t i=0, h = mFaces[f]; i<3; ++i) { uint16_t v0 = mHalfedges[h^1u].mVertex; uint16_t v1 = mHalfedges[h].mVertex; mHalfedges[h].mFace = sInvalid; if(mHalfedges[h^1u].mFace == sInvalid) // was boundary edge, remove { uint16_t v0Prev = mHalfedges[h ].mPrev; uint16_t v0Next = mHalfedges[h^1u].mNext; uint16_t v1Prev = mHalfedges[h^1u].mPrev; uint16_t v1Next = mHalfedges[h ].mNext; // update halfedge connectivity connect(v0Prev, v0Next); connect(v1Prev, v1Next); // update vertex boundary or delete mVertices[v0] = (v0Prev^1) == v0Next ? sInvalid : v0Next; mVertices[v1] = (v1Prev^1) == v1Next ? sInvalid : v1Next; } else { mVertices[v0] = h; // update vertex boundary result = v1; } h = mHalfedges[h].mNext; } mFaces[f] = sInvalid; --mNumTriangles; return result; } // true if vertex v is in front of face f bool visible(uint16_t v, uint16_t f) { uint16_t h = mFaces[f]; if(h == sInvalid) return false; uint16_t v0 = mHalfedges[h].mVertex; h = mHalfedges[h].mNext; uint16_t v1 = mHalfedges[h].mVertex; h = mHalfedges[h].mNext; uint16_t v2 = mHalfedges[h].mVertex; h = mHalfedges[h].mNext; return det(mPoints[v], mPoints[v0], mPoints[v1], mPoints[v2]) < -1e-5f; } uint16_t mNumTriangles; physx::shdfnd::Array mHalfedges; physx::shdfnd::Array mVertices; // vertex -> (boundary) halfedge physx::shdfnd::Array mFaces; // face -> halfedge physx::shdfnd::Array mPoints; }; void ConvexMeshBuilder::buildMesh(uint16_t numPlanes,const physx::PxVec4 *planes) { if(numPlanes < 4) return; // todo: handle degenerate cases HalfedgeMesh mesh; // gather points (planes, that is) mesh.mPoints.reserve(numPlanes); for (uint32_t i=0; i face2Vertex(mesh.mFaces.size(), sInvalid); for(uint32_t i=0; i(planes)); m.mVertexCount = mConvexMeshBuilder.mVertices.size(); if ( m.mVertexCount ) { ret = true; m.mTriCount = mConvexMeshBuilder.mIndices.size()/3; m.mVertices = &mConvexMeshBuilder.mVertices[0]; m.mIndices = &mConvexMeshBuilder.mIndices[0]; } return ret; } virtual const HullEdge *getHullEdges(uint32_t planeCount,const physx::PxPlane *planes,uint32_t &edgeCount) { const HullEdge *ret = NULL; mEdges.clear(); calculatePolygonEdges(mEdges,planeCount,planes); if ( !mEdges.empty() ) { ret = &mEdges[0]; edgeCount = mEdges.size(); } return ret; } virtual void release(void) { delete this; } physx::shdfnd::Array mEdges; ConvexMeshBuilder mConvexMeshBuilder; }; Hull2MeshEdges *createHull2MeshEdges(void) { Hull2MeshEdgesImpl *ret = PX_NEW(Hull2MeshEdgesImpl); return static_cast< Hull2MeshEdges *>(ret); }