aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/PhysXCooking/src/convex/ConvexHullUtils.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/convex/ConvexHullUtils.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/convex/ConvexHullUtils.cpp')
-rw-r--r--PhysX_3.4/Source/PhysXCooking/src/convex/ConvexHullUtils.cpp925
1 files changed, 925 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/PhysXCooking/src/convex/ConvexHullUtils.cpp b/PhysX_3.4/Source/PhysXCooking/src/convex/ConvexHullUtils.cpp
new file mode 100644
index 00000000..cf921a16
--- /dev/null
+++ b/PhysX_3.4/Source/PhysXCooking/src/convex/ConvexHullUtils.cpp
@@ -0,0 +1,925 @@
+// 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/PxBounds3.h"
+#include "foundation/PxMathUtils.h"
+
+#include "ConvexHullUtils.h"
+#include "VolumeIntegration.h"
+#include "PsUtilities.h"
+#include "PsVecMath.h"
+#include "GuBox.h"
+#include "GuConvexMeshData.h"
+
+using namespace physx;
+using namespace Ps::aos;
+
+namespace local
+{
+ static const float MIN_ADJACENT_ANGLE = 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other.
+ static const float MAXDOT_MINANG = cosf(Ps::degToRad(MIN_ADJACENT_ANGLE)); // adjacent angle for dot product tests
+
+ //////////////////////////////////////////////////////////////////////////
+ // helper class for ConvexHullCrop
+ class VertFlag
+ {
+ public:
+ PxU8 planetest;
+ PxU8 undermap;
+ PxU8 overmap;
+ };
+
+ //////////////////////////////////////////////////////////////////////////|
+ // helper class for ConvexHullCrop
+ class EdgeFlag
+ {
+ public:
+ PxI16 undermap;
+ };
+
+ //////////////////////////////////////////////////////////////////////////|
+ // helper class for ConvexHullCrop
+ class Coplanar
+ {
+ public:
+ PxU16 ea;
+ PxU8 v0;
+ PxU8 v1;
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+ // plane test
+ enum PlaneTestResult
+ {
+ eCOPLANAR = 0,
+ eUNDER = 1 << 0,
+ eOVER = 1 << 1
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+ // test where vertex lies in respect to the plane
+ static PlaneTestResult planeTest(const PxPlane& p, const PxVec3& v, float epsilon)
+ {
+ const float a = v.dot(p.n) + p.d;
+ PlaneTestResult flag = (a > epsilon) ? eOVER : ((a < -epsilon) ? eUNDER : eCOPLANAR);
+ return flag;
+ }
+
+ // computes the OBB for this set of points relative to this transform matrix. SIMD version
+ void computeOBBSIMD(PxU32 vcount, const Vec4V* points, Vec4V& sides, const QuatV& rot, Vec4V& trans)
+ {
+ PX_ASSERT(vcount);
+
+ Vec4V minV = V4Load(FLT_MAX);
+ Vec4V maxV = V4Load(FLT_MIN);
+ for (PxU32 i = 0; i < vcount; i++)
+ {
+ const Vec4V& vertexV = points[i];
+ const Vec4V t = V4Sub(vertexV, trans);
+ const Vec4V v = Vec4V_From_Vec3V(QuatRotateInv(rot, Vec3V_From_Vec4V(t)));
+
+ minV = V4Min(minV, v);
+ maxV = V4Max(maxV, v);
+ }
+
+ sides = V4Sub(maxV, minV);
+
+ Mat33V tmpMat;
+ QuatGetMat33V(rot, tmpMat.col0, tmpMat.col1, tmpMat.col2);
+ const FloatV coe = FLoad(0.5f);
+
+ const Vec4V deltaVec = V4Sub(maxV, V4Scale(sides, coe));
+
+ const Vec4V t0 = V4Scale(Vec4V_From_Vec3V(tmpMat.col0), V4GetX(deltaVec));
+ trans = V4Add(trans, t0);
+
+ const Vec4V t1 = V4Scale(Vec4V_From_Vec3V(tmpMat.col1), V4GetY(deltaVec));
+ trans = V4Add(trans, t1);
+
+ const Vec4V t2 = V4Scale(Vec4V_From_Vec3V(tmpMat.col2), V4GetZ(deltaVec));
+ trans = V4Add(trans, t2);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// construct the base cube from given min/max
+ConvexHull::ConvexHull(const PxVec3& bmin, const PxVec3& bmax, const Ps::Array<PxPlane>& inPlanes)
+: mInputPlanes(inPlanes)
+{
+ // min max verts of the cube - 8 verts
+ mVertices.pushBack(PxVec3(bmin.x, bmin.y, bmin.z)); // ---
+ mVertices.pushBack(PxVec3(bmin.x, bmin.y, bmax.z)); // --+
+ mVertices.pushBack(PxVec3(bmin.x, bmax.y, bmin.z)); // -+-
+ mVertices.pushBack(PxVec3(bmin.x, bmax.y, bmax.z)); // -++
+ mVertices.pushBack(PxVec3(bmax.x, bmin.y, bmin.z)); // +--
+ mVertices.pushBack(PxVec3(bmax.x, bmin.y, bmax.z)); // +-+
+ mVertices.pushBack(PxVec3(bmax.x, bmax.y, bmin.z)); // ++-
+ mVertices.pushBack(PxVec3(bmax.x, bmax.y, bmax.z)); // +++
+
+ // cube planes - 6 planes
+ mFacets.pushBack(PxPlane(PxVec3(-1.f, 0, 0), bmin.x)); // 0,1,3,2
+ mFacets.pushBack(PxPlane(PxVec3(1.f, 0, 0), -bmax.x)); // 6,7,5,4
+ mFacets.pushBack(PxPlane(PxVec3(0, -1.f, 0), bmin.y)); // 0,4,5,1
+ mFacets.pushBack(PxPlane(PxVec3(0, 1.f, 0), -bmax.y)); // 3,7,6,2
+ mFacets.pushBack(PxPlane(PxVec3(0, 0, -1.f), bmin.z)); // 0,2,6,4
+ mFacets.pushBack(PxPlane(PxVec3(0, 0, 1.f), -bmax.z)); // 1,5,7,3
+
+ // cube edges - 24 edges
+ mEdges.pushBack(HalfEdge(11, 0, 0));
+ mEdges.pushBack(HalfEdge(23, 1, 0));
+ mEdges.pushBack(HalfEdge(15, 3, 0));
+ mEdges.pushBack(HalfEdge(16, 2, 0));
+
+ mEdges.pushBack(HalfEdge(13, 6, 1));
+ mEdges.pushBack(HalfEdge(21, 7, 1));
+ mEdges.pushBack(HalfEdge(9, 5, 1));
+ mEdges.pushBack(HalfEdge(18, 4, 1));
+
+ mEdges.pushBack(HalfEdge(19, 0, 2));
+ mEdges.pushBack(HalfEdge(6, 4, 2));
+ mEdges.pushBack(HalfEdge(20, 5, 2));
+ mEdges.pushBack(HalfEdge(0, 1, 2));
+
+ mEdges.pushBack(HalfEdge(22, 3, 3));
+ mEdges.pushBack(HalfEdge(4, 7, 3));
+ mEdges.pushBack(HalfEdge(17, 6, 3));
+ mEdges.pushBack(HalfEdge(2, 2, 3));
+
+ mEdges.pushBack(HalfEdge(3, 0, 4));
+ mEdges.pushBack(HalfEdge(14, 2, 4));
+ mEdges.pushBack(HalfEdge(7, 6, 4));
+ mEdges.pushBack(HalfEdge(8, 4, 4));
+
+ mEdges.pushBack(HalfEdge(10, 1, 5));
+ mEdges.pushBack(HalfEdge(5, 5, 5));
+ mEdges.pushBack(HalfEdge(12, 7, 5));
+ mEdges.pushBack(HalfEdge(1, 3, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+// create the initial convex hull from given OBB
+ConvexHull::ConvexHull(const PxVec3& extent, const PxTransform& transform, const Ps::Array<PxPlane>& inPlanes)
+ : mInputPlanes(inPlanes)
+{
+ // get the OBB corner points
+ PxVec3 extentPoints[8];
+ PxMat33 rot(transform.q);
+ Gu::computeOBBPoints(extentPoints, transform.p, extent, rot.column0, rot.column1, rot.column2);
+
+ mVertices.pushBack(PxVec3(extentPoints[0].x, extentPoints[0].y, extentPoints[0].z)); // ---
+ mVertices.pushBack(PxVec3(extentPoints[4].x, extentPoints[4].y, extentPoints[4].z)); // --+
+ mVertices.pushBack(PxVec3(extentPoints[3].x, extentPoints[3].y, extentPoints[3].z)); // -+-
+ mVertices.pushBack(PxVec3(extentPoints[7].x, extentPoints[7].y, extentPoints[7].z)); // -++
+ mVertices.pushBack(PxVec3(extentPoints[1].x, extentPoints[1].y, extentPoints[1].z)); // +--
+ mVertices.pushBack(PxVec3(extentPoints[5].x, extentPoints[5].y, extentPoints[5].z)); // +-+
+ mVertices.pushBack(PxVec3(extentPoints[2].x, extentPoints[2].y, extentPoints[2].z)); // ++-
+ mVertices.pushBack(PxVec3(extentPoints[6].x, extentPoints[6].y, extentPoints[6].z)); // +++
+
+ // cube planes - 6 planes
+ PxPlane plane0(extentPoints[0], extentPoints[4], extentPoints[7]); // 0,1,3,2
+ mFacets.pushBack(PxPlane(plane0.n, plane0.d));
+
+ PxPlane plane1(extentPoints[2], extentPoints[6], extentPoints[5]); // 6,7,5,4
+ mFacets.pushBack(PxPlane(plane1.n, plane1.d));
+
+ PxPlane plane2(extentPoints[0], extentPoints[1], extentPoints[5]); // 0,4,5,1
+ mFacets.pushBack(PxPlane(plane2.n, plane2.d));
+
+ PxPlane plane3(extentPoints[7], extentPoints[6], extentPoints[2]); // 3,7,6,2
+ mFacets.pushBack(PxPlane(plane3.n, plane3.d));
+
+ PxPlane plane4(extentPoints[0], extentPoints[3], extentPoints[2]); // 0,2,6,4
+ mFacets.pushBack(PxPlane(plane4.n, plane4.d));
+
+ PxPlane plane5(extentPoints[4], extentPoints[5], extentPoints[6]); // 1,5,7,3
+ mFacets.pushBack(PxPlane(plane5.n, plane5.d));
+
+ // cube edges - 24 edges
+ mEdges.pushBack(HalfEdge(11, 0, 0));
+ mEdges.pushBack(HalfEdge(23, 1, 0));
+ mEdges.pushBack(HalfEdge(15, 3, 0));
+ mEdges.pushBack(HalfEdge(16, 2, 0));
+
+ mEdges.pushBack(HalfEdge(13, 6, 1));
+ mEdges.pushBack(HalfEdge(21, 7, 1));
+ mEdges.pushBack(HalfEdge(9, 5, 1));
+ mEdges.pushBack(HalfEdge(18, 4, 1));
+
+ mEdges.pushBack(HalfEdge(19, 0, 2));
+ mEdges.pushBack(HalfEdge(6, 4, 2));
+ mEdges.pushBack(HalfEdge(20, 5, 2));
+ mEdges.pushBack(HalfEdge(0, 1, 2));
+
+ mEdges.pushBack(HalfEdge(22, 3, 3));
+ mEdges.pushBack(HalfEdge(4, 7, 3));
+ mEdges.pushBack(HalfEdge(17, 6, 3));
+ mEdges.pushBack(HalfEdge(2, 2, 3));
+
+ mEdges.pushBack(HalfEdge(3, 0, 4));
+ mEdges.pushBack(HalfEdge(14, 2, 4));
+ mEdges.pushBack(HalfEdge(7, 6, 4));
+ mEdges.pushBack(HalfEdge(8, 4, 4));
+
+ mEdges.pushBack(HalfEdge(10, 1, 5));
+ mEdges.pushBack(HalfEdge(5, 5, 5));
+ mEdges.pushBack(HalfEdge(12, 7, 5));
+ mEdges.pushBack(HalfEdge(1, 3, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+// finds the candidate plane, returns -1 otherwise
+PxI32 ConvexHull::findCandidatePlane(float planeTestEpsilon, float epsilon) const
+{
+ PxI32 p = -1;
+ float md = 0.0f;
+ PxU32 i, j;
+ for (i = 0; i < mInputPlanes.size(); i++)
+ {
+ float d = 0.0f;
+ float dmax = 0.0f;
+ float dmin = 0.0f;
+ for (j = 0; j < mVertices.size(); j++)
+ {
+ dmax = PxMax(dmax, mVertices[j].dot(mInputPlanes[i].n) + mInputPlanes[i].d);
+ dmin = PxMin(dmin, mVertices[j].dot(mInputPlanes[i].n) + mInputPlanes[i].d);
+ }
+
+ float dr = dmax - dmin;
+ if (dr < planeTestEpsilon)
+ dr = 1.0f; // shouldn't happen.
+ d = dmax / dr;
+ // we have a better candidate try another one
+ if (d <= md)
+ continue;
+ // check if we dont have already that plane or if the normals are nearly the same
+ for (j = 0; j<mFacets.size(); j++)
+ {
+ if (mInputPlanes[i] == mFacets[j])
+ {
+ d = 0.0f;
+ continue;
+ }
+ if (mInputPlanes[i].n.dot(mFacets[j].n)> local::MAXDOT_MINANG)
+ {
+ for (PxU32 k = 0; k < mEdges.size(); k++)
+ {
+ if (mEdges[k].p != j)
+ continue;
+ if (mVertices[mEdges[k].v].dot(mInputPlanes[i].n) + mInputPlanes[i].d < 0)
+ {
+ d = 0; // so this plane wont get selected.
+ break;
+ }
+ }
+ }
+ }
+ if (d>md)
+ {
+ p = PxI32(i);
+ md = d;
+ }
+ }
+ return (md > epsilon) ? p : -1;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// internal hull check
+bool ConvexHull::assertIntact(float epsilon) const
+{
+ PxU32 i;
+ PxU32 estart = 0;
+ for (i = 0; i < mEdges.size(); i++)
+ {
+ if (mEdges[estart].p != mEdges[i].p)
+ {
+ estart = i;
+ }
+ PxU32 inext = i + 1;
+ if (inext >= mEdges.size() || mEdges[inext].p != mEdges[i].p)
+ {
+ inext = estart;
+ }
+ PX_ASSERT(mEdges[inext].p == mEdges[i].p);
+ PxI16 nb = mEdges[i].ea;
+ if (nb == 255 || nb == -1)
+ return false;
+ PX_ASSERT(nb != -1);
+ PX_ASSERT(i == PxU32(mEdges[PxU32(nb)].ea));
+ // Check that the vertex of the next edge is the vertex of the adjacent half edge.
+ // Otherwise the two half edges are not really adjacent and we have a hole.
+ PX_ASSERT(mEdges[PxU32(nb)].v == mEdges[inext].v);
+ if (!(mEdges[PxU32(nb)].v == mEdges[inext].v))
+ return false;
+ }
+
+ for (i = 0; i < mEdges.size(); i++)
+ {
+ PX_ASSERT(local::eCOPLANAR == local::planeTest(mFacets[mEdges[i].p], mVertices[mEdges[i].v], epsilon));
+ if (local::eCOPLANAR != local::planeTest(mFacets[mEdges[i].p], mVertices[mEdges[i].v], epsilon))
+ return false;
+ if (mEdges[estart].p != mEdges[i].p)
+ {
+ estart = i;
+ }
+ PxU32 i1 = i + 1;
+ if (i1 >= mEdges.size() || mEdges[i1].p != mEdges[i].p) {
+ i1 = estart;
+ }
+ PxU32 i2 = i1 + 1;
+ if (i2 >= mEdges.size() || mEdges[i2].p != mEdges[i].p) {
+ i2 = estart;
+ }
+ if (i == i2)
+ continue; // i sliced tangent to an edge and created 2 meaningless edges
+
+ // check the face normal against the triangle from edges
+ PxVec3 localNormal = (mVertices[mEdges[i1].v] - mVertices[mEdges[i].v]).cross(mVertices[mEdges[i2].v] - mVertices[mEdges[i1].v]);
+ const float m = localNormal.magnitude();
+ if (m == 0.0f)
+ localNormal = PxVec3(1.f, 0.0f, 0.0f);
+ localNormal *= (1.0f / m);
+ if (localNormal.dot(mFacets[mEdges[i].p].n) <= 0.0f)
+ return false;
+ }
+ return true;
+}
+
+// returns the maximum number of vertices on a face
+PxU32 ConvexHull::maxNumVertsPerFace() const
+{
+ PxU32 maxVerts = 0;
+ PxU32 currentVerts = 0;
+ PxU32 estart = 0;
+ for (PxU32 i = 0; i < mEdges.size(); i++)
+ {
+ if (mEdges[estart].p != mEdges[i].p)
+ {
+ if(currentVerts > maxVerts)
+ {
+ maxVerts = currentVerts + 1;
+ }
+ currentVerts = 0;
+ estart = i;
+ }
+ else
+ {
+ currentVerts++;
+ }
+ }
+ return maxVerts;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// slice the input convexHull with the slice plane
+ConvexHull* physx::convexHullCrop(const ConvexHull& convex, const PxPlane& slice, float planeTestEpsilon)
+{
+ static const PxU8 invalidIndex = PxU8(-1);
+ PxU32 i;
+ PxU32 vertCountUnder = 0; // Running count of the vertices UNDER the slicing plane.
+
+ PX_ASSERT(convex.getEdges().size() < 480);
+
+ // Arrays of mapping information associated with features in the input convex.
+ // edgeflag[i].undermap - output index of input edge convex->edges[i]
+ // vertflag[i].undermap - output index of input vertex convex->vertices[i]
+ // vertflag[i].planetest - the side-of-plane classification of convex->vertices[i]
+ // (There are other members but they are unused.)
+ local::EdgeFlag edgeFlag[512];
+ local::VertFlag vertFlag[256];
+
+ // Lists of output features. Populated during clipping.
+ // Coplanar edges have one sibling in tmpunderedges and one in coplanaredges.
+ // coplanaredges holds the sibling that belong to the new polygon created from slicing.
+ ConvexHull::HalfEdge tmpUnderEdges[512]; // The output edge list.
+ PxPlane tmpUnderPlanes[128]; // The output plane list.
+ local::Coplanar coplanarEdges[512]; // The coplanar edge list.
+
+ PxU32 coplanarEdgesNum = 0; // Running count of coplanar edges.
+
+ // Created vertices on the slicing plane (stored for output after clipping).
+ Ps::Array<PxVec3> createdVerts;
+
+ // Logical OR of individual vertex flags.
+ PxU32 convexClipFlags = 0;
+
+ // Classify each vertex against the slicing plane as OVER | COPLANAR | UNDER.
+ // OVER - Vertex is over (outside) the slicing plane. Will not be output.
+ // COPLANAR - Vertex is on the slicing plane. A copy will be output.
+ // UNDER - Vertex is under (inside) the slicing plane. Will be output.
+ // We keep an array of information structures for each vertex in the input convex.
+ // vertflag[i].undermap - The (computed) index of convex->vertices[i] in the output.
+ // invalidIndex for OVER vertices - they are not output.
+ // initially invalidIndex for COPLANAR vertices - set later.
+ // vertflag[i].overmap - Unused - we don't care about the over part.
+ // vertflag[i].planetest - The classification (clip flag) of convex->vertices[i].
+ for (i = 0; i < convex.getVertices().size(); i++)
+ {
+ local::PlaneTestResult vertexClipFlag = local::planeTest(slice, convex.getVertices()[i], planeTestEpsilon);
+ switch (vertexClipFlag)
+ {
+ case local::eOVER:
+ case local::eCOPLANAR:
+ vertFlag[i].undermap = invalidIndex; // Initially invalid for COPLANAR
+ vertFlag[i].overmap = invalidIndex;
+ break;
+ case local::eUNDER:
+ vertFlag[i].undermap = Ps::to8(vertCountUnder++);
+ vertFlag[i].overmap = invalidIndex;
+ break;
+ }
+ vertFlag[i].planetest = PxU8(vertexClipFlag);
+ convexClipFlags |= vertexClipFlag;
+ }
+
+ // Check special case: everything UNDER or COPLANAR.
+ // This way we know we wont end up with silly faces / edges later on.
+ if ((convexClipFlags & local::eOVER) == 0)
+ {
+ // Just return a copy of the same convex.
+ ConvexHull* dst = PX_NEW_TEMP(ConvexHull)(convex);
+ return dst;
+ }
+
+ PxU16 underEdgeCount = 0; // Running count of output edges.
+ PxU16 underPlanesCount = 0; // Running count of output planes.
+
+ // Clipping Loop
+ // =============
+ //
+ // for each plane
+ //
+ // for each edge
+ //
+ // if first UNDER & second !UNDER
+ // output current edge -> tmpunderedges
+ // if we have done the sibling
+ // connect current edge to its sibling
+ // set vout = first vertex of sibling
+ // else if second is COPLANAR
+ // if we havent already copied it
+ // copy second -> createdverts
+ // set vout = index of created vertex
+ // else
+ // generate a new vertex -> createdverts
+ // set vout = index of created vertex
+ // if vin is already set and vin != vout (non-trivial edge)
+ // output coplanar edge -> tmpunderedges (one sibling)
+ // set coplanaredge to new edge index (for connecting the other sibling)
+ //
+ // else if first !UNDER & second UNDER
+ // if we have done the sibling
+ // connect current edge to its sibling
+ // set vin = second vertex of sibling (this is a bit of a pain)
+ // else if first is COPLANAR
+ // if we havent already copied it
+ // copy first -> createdverts
+ // set vin = index of created vertex
+ // else
+ // generate a new vertex -> createdverts
+ // set vin = index of created vertex
+ // if vout is already set and vin != vout (non-trivial edge)
+ // output coplanar edge -> tmpunderedges (one sibling)
+ // set coplanaredge to new edge index (for connecting the other sibling)
+ // output current edge -> tmpunderedges
+ //
+ // else if first UNDER & second UNDER
+ // output current edge -> tmpunderedges
+ //
+ // next edge
+ //
+ // if part of current plane was UNDER
+ // output current plane -> tmpunderplanes
+ //
+ // if coplanaredge is set
+ // output coplanar edge -> coplanaredges
+ //
+ // next plane
+ //
+
+ // Indexing is a bit tricky here:
+ //
+ // e0 - index of the current edge
+ // e1 - index of the next edge
+ // estart - index of the first edge in the current plane
+ // currentplane - index of the current plane
+ // enextface - first edge of next plane
+
+ PxU32 e0 = 0;
+
+ for (PxU32 currentplane = 0; currentplane < convex.getFacets().size(); currentplane++)
+ {
+
+ PxU32 eStart = e0;
+ PxU32 eNextFace = 0xffffffff;
+ PxU32 e1 = e0 + 1;
+
+ PxU8 vout = invalidIndex;
+ PxU8 vin = invalidIndex;
+
+ PxU32 coplanarEdge = invalidIndex;
+
+ // Logical OR of individual vertex flags in the current plane.
+ PxU32 planeSide = 0;
+
+ do{
+
+ // Next edge modulo logic
+ if (e1 >= convex.getEdges().size() || convex.getEdges()[e1].p != currentplane)
+ {
+ eNextFace = e1;
+ e1 = eStart;
+ }
+
+ const ConvexHull::HalfEdge& edge0 = convex.getEdges()[e0];
+ const ConvexHull::HalfEdge& edge1 = convex.getEdges()[e1];
+ const ConvexHull::HalfEdge& edgea = convex.getEdges()[PxU32(edge0.ea)];
+
+ planeSide |= vertFlag[edge0.v].planetest;
+
+ if (vertFlag[edge0.v].planetest == local::eUNDER && vertFlag[edge1.v].planetest != local::eUNDER)
+ {
+ // first is UNDER, second is COPLANAR or OVER
+
+ // Output current edge.
+ edgeFlag[e0].undermap = short(underEdgeCount);
+ tmpUnderEdges[underEdgeCount].v = vertFlag[edge0.v].undermap;
+ tmpUnderEdges[underEdgeCount].p = PxU8(underPlanesCount);
+ PX_ASSERT(tmpUnderEdges[underEdgeCount].v != invalidIndex);
+
+ if (PxU32(edge0.ea) < e0)
+ {
+ // We have already done the sibling.
+ // Connect current edge to its sibling.
+ PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex);
+ tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap;
+ tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount);
+ // Set vout = first vertex of (output, clipped) sibling.
+ vout = tmpUnderEdges[edgeFlag[edge0.ea].undermap].v;
+ }
+ else if (vertFlag[edge1.v].planetest == local::eCOPLANAR)
+ {
+ // Boundary case.
+ // We output coplanar vertices once.
+ if (vertFlag[edge1.v].undermap == invalidIndex)
+ {
+ createdVerts.pushBack(convex.getVertices()[edge1.v]);
+ // Remember the index so we don't output it again.
+ vertFlag[edge1.v].undermap = Ps::to8(vertCountUnder++);
+ }
+ vout = vertFlag[edge1.v].undermap;
+ }
+ else
+ {
+ // Add new vertex.
+ const PxPlane& p0 = convex.getFacets()[edge0.p];
+ const PxPlane& pa = convex.getFacets()[edgea.p];
+ createdVerts.pushBack(threePlaneIntersection(p0, pa, slice));
+ vout = Ps::to8(vertCountUnder++);
+ }
+
+ // We added an edge, increment the counter
+ underEdgeCount++;
+
+ if (vin != invalidIndex && vin != vout)
+ {
+ // We already have vin and a non-trivial edge
+ // Output coplanar edge
+ PX_ASSERT(vout != invalidIndex);
+ coplanarEdge = underEdgeCount;
+ tmpUnderEdges[underEdgeCount].v = vout;
+ tmpUnderEdges[underEdgeCount].p = PxU8(underPlanesCount);
+ tmpUnderEdges[underEdgeCount].ea = invalidIndex;
+ underEdgeCount++;
+ }
+ }
+ else if (vertFlag[edge0.v].planetest != local::eUNDER && vertFlag[edge1.v].planetest == local::eUNDER)
+ {
+ // First is OVER or COPLANAR, second is UNDER.
+
+ if (PxU32(edge0.ea) < e0)
+ {
+ // We have already done the sibling.
+ // We need the second vertex of the sibling.
+ // Which is the vertex of the next edge in the adjacent poly.
+ int nea = edgeFlag[edge0.ea].undermap + 1;
+ int p = tmpUnderEdges[edgeFlag[edge0.ea].undermap].p;
+ if (nea >= underEdgeCount || tmpUnderEdges[nea].p != p)
+ {
+ // End of polygon, next edge is first edge
+ nea -= 2;
+ while (nea > 0 && tmpUnderEdges[nea - 1].p == p)
+ nea--;
+ }
+ vin = tmpUnderEdges[nea].v;
+ PX_ASSERT(vin < vertCountUnder);
+ }
+ else if (vertFlag[edge0.v].planetest == local::eCOPLANAR)
+ {
+ // Boundary case.
+ // We output coplanar vertices once.
+ if (vertFlag[edge0.v].undermap == invalidIndex)
+ {
+ createdVerts.pushBack(convex.getVertices()[edge0.v]);
+ // Remember the index so we don't output it again.
+ vertFlag[edge0.v].undermap = Ps::to8(vertCountUnder++);
+ }
+ vin = vertFlag[edge0.v].undermap;
+ }
+ else
+ {
+ // Add new vertex.
+ const PxPlane& p0 = convex.getFacets()[edge0.p];
+ const PxPlane& pa = convex.getFacets()[edgea.p];
+ createdVerts.pushBack(threePlaneIntersection(p0, pa, slice));
+ vin = Ps::to8(vertCountUnder++);
+ }
+
+ if (vout != invalidIndex && vin != vout)
+ {
+ // We have been in and out, Add the coplanar edge
+ coplanarEdge = underEdgeCount;
+ tmpUnderEdges[underEdgeCount].v = vout;
+ tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount);
+ tmpUnderEdges[underEdgeCount].ea = invalidIndex;
+ underEdgeCount++;
+ }
+
+ // Output current edge.
+ tmpUnderEdges[underEdgeCount].v = vin;
+ tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount);
+ edgeFlag[e0].undermap = short(underEdgeCount);
+
+ if (PxU32(edge0.ea) < e0)
+ {
+ // We have already done the sibling.
+ // Connect current edge to its sibling.
+ PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex);
+ tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap;
+ tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount);
+ }
+
+ PX_ASSERT(edgeFlag[e0].undermap == underEdgeCount);
+ underEdgeCount++;
+ }
+ else if (vertFlag[edge0.v].planetest == local::eUNDER && vertFlag[edge1.v].planetest == local::eUNDER)
+ {
+ // Both UNDER
+
+ // Output current edge.
+ edgeFlag[e0].undermap = short(underEdgeCount);
+ tmpUnderEdges[underEdgeCount].v = vertFlag[edge0.v].undermap;
+ tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount);
+ if (PxU32(edge0.ea) < e0)
+ {
+ // We have already done the sibling.
+ // Connect current edge to its sibling.
+ PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex);
+ tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap;
+ tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount);
+ }
+ underEdgeCount++;
+ }
+
+ e0 = e1;
+ e1++; // do the modulo at the beginning of the loop
+
+ } while (e0 != eStart);
+
+ e0 = eNextFace;
+
+ if (planeSide & local::eUNDER)
+ {
+ // At least part of current plane is UNDER.
+ // Output current plane.
+ tmpUnderPlanes[underPlanesCount] = convex.getFacets()[currentplane];
+ underPlanesCount++;
+ }
+
+ if (coplanarEdge != invalidIndex)
+ {
+ // We have a coplanar edge.
+ // Add to coplanaredges for later processing.
+ // (One sibling is in place but one is missing)
+ PX_ASSERT(vin != invalidIndex);
+ PX_ASSERT(vout != invalidIndex);
+ PX_ASSERT(coplanarEdge != 511);
+ coplanarEdges[coplanarEdgesNum].ea = PxU8(coplanarEdge);
+ coplanarEdges[coplanarEdgesNum].v0 = vin;
+ coplanarEdges[coplanarEdgesNum].v1 = vout;
+ coplanarEdgesNum++;
+ }
+
+ // Reset coplanar edge infos for next poly
+ vin = invalidIndex;
+ vout = invalidIndex;
+ coplanarEdge = invalidIndex;
+ }
+
+ // Add the new plane to the mix:
+ if (coplanarEdgesNum > 0)
+ {
+ tmpUnderPlanes[underPlanesCount++] = slice;
+ }
+
+ // Sort the coplanar edges in winding order.
+ for (i = 0; i < coplanarEdgesNum - 1; i++)
+ {
+ if (coplanarEdges[i].v1 != coplanarEdges[i + 1].v0)
+ {
+ PxU32 j = 0;
+ for (j = i + 2; j < coplanarEdgesNum; j++)
+ {
+ if (coplanarEdges[i].v1 == coplanarEdges[j].v0)
+ {
+ local::Coplanar tmp = coplanarEdges[i + 1];
+ coplanarEdges[i + 1] = coplanarEdges[j];
+ coplanarEdges[j] = tmp;
+ break;
+ }
+ }
+ if (j >= coplanarEdgesNum)
+ {
+ // PX_ASSERT(j<coplanaredges_num);
+ return NULL;
+ }
+ }
+ }
+
+ // PT: added this line to fix DE2904
+ if (!vertCountUnder)
+ return NULL;
+
+ // Create the output convex.
+ ConvexHull* punder = PX_NEW_TEMP(ConvexHull)(convex.getInputPlanes());
+ ConvexHull& under = *punder;
+
+ // Copy UNDER vertices
+ PxU32 k = 0;
+ for (i = 0; i < convex.getVertices().size(); i++)
+ {
+ if (vertFlag[i].planetest == local::eUNDER)
+ {
+ under.getVertices().pushBack(convex.getVertices()[i]);
+ k++;
+ }
+ }
+
+ // Copy created vertices
+ i = 0;
+ while (k < vertCountUnder)
+ {
+ under.getVertices().pushBack(createdVerts[i++]);
+ k++;
+ }
+
+ PX_ASSERT(i == createdVerts.size());
+
+ // Copy the output edges and output planes.
+ under.getEdges().resize(underEdgeCount + coplanarEdgesNum);
+ under.getFacets().resize(underPlanesCount);
+
+ // Add the coplanar edge siblings that belong to the new polygon (coplanaredges).
+ for (i = 0; i < coplanarEdgesNum; i++)
+ {
+ under.getEdges()[underEdgeCount + i].p = PxU8(underPlanesCount - 1);
+ under.getEdges()[underEdgeCount + i].ea = short(coplanarEdges[i].ea);
+ tmpUnderEdges[coplanarEdges[i].ea].ea = PxI16(underEdgeCount + i);
+ under.getEdges()[underEdgeCount + i].v = coplanarEdges[i].v0;
+ }
+
+ PxMemCopy(under.getEdges().begin(), tmpUnderEdges, sizeof(ConvexHull::HalfEdge)*underEdgeCount);
+ PxMemCopy(under.getFacets().begin(), tmpUnderPlanes, sizeof(PxPlane)*underPlanesCount);
+ return punder;
+}
+
+bool physx::computeOBBFromConvex(const PxConvexMeshDesc& desc, PxVec3& sides, PxTransform& matrix)
+{
+ PxIntegrals integrals;
+ // using the centroid of the convex for the volume integration solved accuracy issues in cases where the inertia tensor
+ // ended up close to not being positive definite and after a few further transforms the diagonalized inertia tensor ended
+ // up with negative values.
+
+ const PxVec3* verts = (reinterpret_cast<const PxVec3*>(desc.points.data));
+ const PxU32* ind = (reinterpret_cast<const PxU32*>(desc.indices.data));
+ const PxHullPolygon* polygons = (reinterpret_cast<const PxHullPolygon*>(desc.polygons.data));
+ PxVec3 mean(0.0f);
+ for (PxU32 i = 0; i < desc.points.count; i++)
+ mean += verts[i];
+ mean *= (1.0f / desc.points.count);
+
+ PxU8* indices = reinterpret_cast<PxU8*> (PX_ALLOC_TEMP(sizeof(PxU8)*desc.indices.count, "PxU8"));
+ for (PxU32 i = 0; i < desc.indices.count; i++)
+ {
+ indices[i] = Ps::to8(ind[i]);
+ }
+ // we need to move the polygon data to internal format
+ Gu::HullPolygonData* polygonData = reinterpret_cast<Gu::HullPolygonData*> (PX_ALLOC_TEMP(sizeof(Gu::HullPolygonData)*desc.polygons.count, "Gu::HullPolygonData"));
+ for (PxU32 i = 0; i < desc.polygons.count; i++)
+ {
+ polygonData[i].mPlane = PxPlane(polygons[i].mPlane[0], polygons[i].mPlane[1], polygons[i].mPlane[2], polygons[i].mPlane[3]);
+ polygonData[i].mNbVerts = Ps::to8(polygons[i].mNbVerts);
+ polygonData[i].mVRef8 = polygons[i].mIndexBase;
+ }
+
+ PxConvexMeshDesc inDesc;
+ inDesc.points.data = desc.points.data;
+ inDesc.points.count = desc.points.count;
+
+ inDesc.polygons.data = polygonData;
+ inDesc.polygons.count = desc.polygons.count;
+
+ inDesc.indices.data = indices;
+ inDesc.indices.count = desc.indices.count;
+
+ // compute volume integrals to get basis axis
+ bool status = (desc.flags & PxConvexFlag::eFAST_INERTIA_COMPUTATION) ?
+ computeVolumeIntegralsEberlySIMD(inDesc, 1.0f, integrals, mean) : computeVolumeIntegralsEberly(inDesc, 1.0f, integrals, mean);
+ if (status)
+ {
+ Vec4V* pointsV = reinterpret_cast<Vec4V*> (PX_ALLOC_TEMP(sizeof(Vec4V)*desc.points.count, "Vec4V"));
+ for (PxU32 i = 0; i < desc.points.count; i++)
+ {
+ // safe to V4 load, same as volume integration - we allocate one more vector
+ pointsV[i] = V4LoadU(&verts[i].x);
+ }
+
+ PxMat33 inertia;
+ integrals.getOriginInertia(inertia);
+ PxQuat inertiaQuat;
+ PxDiagonalize(inertia, inertiaQuat);
+ PxMat33 baseAxis(inertiaQuat);
+ Vec4V center = V4LoadU(&integrals.COM.x);
+
+ const PxU32 numSteps = 20;
+ const float subStep = Ps::degToRad(float(360/numSteps));
+
+ float bestVolume = 1e9;
+
+ for (PxU32 axis = 0; axis < 3; axis++)
+ {
+ for (PxU32 iStep = 0; iStep < numSteps; iStep++)
+ {
+ PxQuat quat(iStep*subStep, baseAxis[axis]);
+
+ Vec4V transV = center;
+ Vec4V psidesV;
+
+ const QuatV rotV = QuatVLoadU(&quat.x);
+ local::computeOBBSIMD(desc.points.count, pointsV, psidesV, rotV, transV);
+
+ PxVec3 psides;
+ V3StoreU(Vec3V_From_Vec4V(psidesV), psides);
+
+ const float volume = psides[0] * psides[1] * psides[2]; // the volume of the cube
+
+ if (volume <= bestVolume)
+ {
+ bestVolume = volume;
+ sides = psides;
+
+ V4StoreU(rotV, &matrix.q.x);
+ V3StoreU(Vec3V_From_Vec4V(transV), matrix.p);
+ }
+ }
+ }
+
+ PX_FREE_AND_RESET(pointsV);
+ }
+ else
+ {
+ PX_FREE_AND_RESET(indices);
+ PX_FREE_AND_RESET(polygonData);
+ return false;
+ }
+
+ PX_FREE_AND_RESET(indices);
+ PX_FREE_AND_RESET(polygonData);
+ return true;
+}