diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp')
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp new file mode 100644 index 00000000..582b2df9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp @@ -0,0 +1,615 @@ +// 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 "GuDistancePointTriangle.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "GuFeatureCode.h" +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuBox.h" +#include "PsSort.h" + +#include "CmRenderOutput.h" + +#define DEBUG_RENDER_MESHCONTACTS 0 + +using namespace physx; +using namespace Gu; + +static const bool gDrawTouchedTriangles = false; + +static void outputErrorMessage() +{ +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Dropping contacts in sphere vs mesh: exceeded limit of 64 "); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: a customized version that also returns the feature code +static PxVec3 closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t, FeatureCode& fc) +{ + // Check if P in vertex region outside A + const PxVec3 ab = b - a; + const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + { + s = 0.0f; + t = 0.0f; + fc = FC_VERTEX0; + return a; // Barycentric coords 1,0,0 + } + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + { + s = 1.0f; + t = 0.0f; + fc = FC_VERTEX1; + return b; // Barycentric coords 0,1,0 + } + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + s = v; + t = 0.0f; + fc = FC_EDGE01; + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + { + s = 0.0f; + t = 1.0f; + fc = FC_VERTEX2; + return c; // Barycentric coords 0,0,1 + } + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + s = 0.0f; + t = w; + fc = FC_EDGE20; + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + s = 1.0f-w; + t = w; + fc = FC_EDGE12; + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + s = v; + t = w; + fc = FC_FACE; + return a + ab*v + ac*w; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: we use a separate structure to make sorting faster +struct SortKey +{ + float mSquareDist; + PxU32 mIndex; + + PX_FORCE_INLINE bool operator < (const SortKey& data) const + { + return mSquareDist < data.mSquareDist; + } +}; + +struct TriangleData +{ + PxVec3 mDelta; + FeatureCode mFC; + PxU32 mTriangleIndex; + PxU32 mVRef[3]; +}; + +struct CachedTriangleIndices +{ + PxU32 mVRef[3]; +}; + +static PX_FORCE_INLINE bool validateSquareDist(PxReal squareDist) +{ + return squareDist>0.0001f; +} + +static bool validateEdge(PxU32 vref0, PxU32 vref1, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris) +{ + while(nbCachedTris--) + { + const CachedTriangleIndices& inds = *cachedTris++; + const PxU32 vi0 = inds.mVRef[0]; + const PxU32 vi1 = inds.mVRef[1]; + const PxU32 vi2 = inds.mVRef[2]; + + if(vi0==vref0) + { + if(vi1==vref1 || vi2==vref1) + return false; + } + else if(vi1==vref0) + { + if(vi0==vref1 || vi2==vref1) + return false; + } + else if(vi2==vref0) + { + if(vi1==vref1 || vi0==vref1) + return false; + } + } + return true; +} + +static bool validateVertex(PxU32 vref, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris) +{ + while(nbCachedTris--) + { + const CachedTriangleIndices& inds = *cachedTris++; + if(inds.mVRef[0]==vref || inds.mVRef[1]==vref || inds.mVRef[2]==vref) + return false; + } + return true; +} + +namespace +{ + class NullAllocator + { + public: + PX_FORCE_INLINE NullAllocator() { } + PX_FORCE_INLINE void* allocate(size_t, const char*, int) { return NULL; } + PX_FORCE_INLINE void deallocate(void*) { } + }; + +struct SphereMeshContactGeneration +{ + const PxSphereGeometry& mShapeSphere; + const PxTransform& mTransform0; + const PxTransform& mTransform1; + ContactBuffer& mContactBuffer; + const PxVec3& mSphereCenterShape1Space; + PxF32 mInflatedRadius2; + PxU32 mNbDelayed; + TriangleData mSavedData[ContactBuffer::MAX_CONTACTS]; + SortKey mSortKey[ContactBuffer::MAX_CONTACTS]; + PxU32 mNbCachedTris; + CachedTriangleIndices mCachedTris[ContactBuffer::MAX_CONTACTS]; + Cm::RenderOutput* mRenderOutput; + + SphereMeshContactGeneration(const PxSphereGeometry& shapeSphere, const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, + Cm::RenderOutput* renderOutput) : + mShapeSphere (shapeSphere), + mTransform0 (transform0), + mTransform1 (transform1), + mContactBuffer (contactBuffer), + mSphereCenterShape1Space (sphereCenterShape1Space), + mInflatedRadius2 (inflatedRadius*inflatedRadius), + mNbDelayed (0), + mNbCachedTris (0), + mRenderOutput (renderOutput) + { + } + + PX_FORCE_INLINE void cacheTriangle(PxU32 ref0, PxU32 ref1, PxU32 ref2) + { + const PxU32 nb = mNbCachedTris++; + mCachedTris[nb].mVRef[0] = ref0; + mCachedTris[nb].mVRef[1] = ref1; + mCachedTris[nb].mVRef[2] = ref2; + } + + PX_FORCE_INLINE void addContact(const PxVec3& d, PxReal squareDist, PxU32 triangleIndex) + { + float dist; + PxVec3 delta; + if(validateSquareDist(squareDist)) + { + // PT: regular contact. Normalize 'delta'. + dist = PxSqrt(squareDist); + delta = d / dist; + } + else + { + // PT: singular contact: 'd' is the non-unit triangle's normal in this case. + dist = 0.0f; + delta = -d.getNormalized(); + } + + const PxVec3 worldNormal = -mTransform1.rotate(delta); + + const PxVec3 localHit = mSphereCenterShape1Space + mShapeSphere.radius*delta; + const PxVec3 hit = mTransform1.transform(localHit); + + if(!mContactBuffer.contact(hit, worldNormal, dist - mShapeSphere.radius, triangleIndex)) + outputErrorMessage(); + } + + void processTriangle(PxU32 triangleIndex, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, const PxU32* vertInds) + { + // PT: compute closest point between sphere center and triangle + PxReal u, v; + FeatureCode fc; + const PxVec3 cp = closestPtPointTriangle(mSphereCenterShape1Space, v0, v1, v2, u, v, fc); + + // PT: compute 'delta' vector between closest point and sphere center + const PxVec3 delta = cp - mSphereCenterShape1Space; + const PxReal squareDist = delta.magnitudeSquared(); + if(squareDist >= mInflatedRadius2) + return; + + // PT: backface culling without the normalize + // PT: TODO: consider doing before the pt-triangle distance test if it's cheaper + // PT: TODO: e0/e1 already computed in closestPtPointTriangle + const PxVec3 e0 = v1 - v0; + const PxVec3 e1 = v2 - v0; + const PxVec3 planeNormal = e0.cross(e1); + const PxF32 planeD = planeNormal.dot(v0); // PT: actually -d compared to PxcPlane + if(planeNormal.dot(mSphereCenterShape1Space) < planeD) + return; + + // PT: for a regular contact, 'delta' is non-zero (and so is 'squareDist'). However when the sphere's center exactly touches + // the triangle, then both 'delta' and 'squareDist' become zero. This needs to be handled as a special case to avoid dividing + // by zero. We will use the triangle's normal as a contact normal in this special case. + // + // 'validateSquareDist' is called twice because there are conflicting goals here. We could call it once now and already + // compute the proper data for generating the contact. But this would mean doing a square-root and a division right here, + // even when the contact is not actually needed in the end. We could also call it only once in "addContact', but the plane's + // normal would not always be available (in case of delayed contacts), and thus it would need to be either recomputed (slower) + // or stored within 'TriangleData' (using more memory). Calling 'validateSquareDist' twice is a better option overall. + PxVec3 d; + if(validateSquareDist(squareDist)) + d = delta; + else + d = planeNormal; + + if(fc==FC_FACE) + { + addContact(d, squareDist, triangleIndex); + + if(mNbCachedTris<ContactBuffer::MAX_CONTACTS) + cacheTriangle(vertInds[0], vertInds[1], vertInds[2]); + } + else + { + if(mNbDelayed<ContactBuffer::MAX_CONTACTS) + { + const PxU32 index = mNbDelayed++; + mSortKey[index].mSquareDist = squareDist; + mSortKey[index].mIndex = index; + + TriangleData* saved = mSavedData + index; + saved->mDelta = d; + saved->mVRef[0] = vertInds[0]; + saved->mVRef[1] = vertInds[1]; + saved->mVRef[2] = vertInds[2]; + saved->mFC = fc; + saved->mTriangleIndex = triangleIndex; + } + else outputErrorMessage(); + } + } + + void generateLastContacts() + { + const PxU32 count = mNbDelayed; + if(!count) + return; + + Ps::sort(mSortKey, count, Ps::Less<SortKey>(), NullAllocator(), ContactBuffer::MAX_CONTACTS); + + TriangleData* touchedTris = mSavedData; + for(PxU32 i=0;i<count;i++) + { + const TriangleData& data = touchedTris[mSortKey[i].mIndex]; + + const PxU32 ref0 = data.mVRef[0]; + const PxU32 ref1 = data.mVRef[1]; + const PxU32 ref2 = data.mVRef[2]; + + bool generateContact = false; + + switch(data.mFC) + { + case FC_VERTEX0: + generateContact = ::validateVertex(ref0, mCachedTris, mNbCachedTris); + break; + + case FC_VERTEX1: + generateContact = ::validateVertex(ref1, mCachedTris, mNbCachedTris); + break; + + case FC_VERTEX2: + generateContact = ::validateVertex(ref2, mCachedTris, mNbCachedTris); + break; + + case FC_EDGE01: + generateContact = ::validateEdge(ref0, ref1, mCachedTris, mNbCachedTris); + break; + + case FC_EDGE12: + generateContact = ::validateEdge(ref1, ref2, mCachedTris, mNbCachedTris); + break; + + case FC_EDGE20: + generateContact = ::validateEdge(ref0, ref2, mCachedTris, mNbCachedTris); + break; + + case FC_FACE: + case FC_UNDEFINED: + PX_ASSERT(0); // PT: should not be possible + break; + }; + + if(generateContact) + addContact(data.mDelta, mSortKey[i].mSquareDist, data.mTriangleIndex); + + if(mNbCachedTris<ContactBuffer::MAX_CONTACTS) + cacheTriangle(ref0, ref1, ref2); + else + outputErrorMessage(); + } + } + +private: + SphereMeshContactGeneration& operator=(const SphereMeshContactGeneration&); +}; + +struct SphereMeshContactGenerationCallback_NoScale : MeshHitCallback<PxRaycastHit> +{ + SphereMeshContactGeneration mGeneration; + const TriangleMesh& mMeshData; + + SphereMeshContactGenerationCallback_NoScale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer, + const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput + ) : MeshHitCallback<PxRaycastHit> (CallbackMode::eMULTIPLE), + mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput), + mMeshData (meshData) + { + } + + virtual ~SphereMeshContactGenerationCallback_NoScale() + { + mGeneration.generateLastContacts(); + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + if(gDrawTouchedTriangles) + { + (*mGeneration.mRenderOutput) << 0xffffffff; + (*mGeneration.mRenderOutput) << PxMat44(PxIdentity); + const PxVec3 wp0 = mGeneration.mTransform1.transform(v0); + const PxVec3 wp1 = mGeneration.mTransform1.transform(v1); + const PxVec3 wp2 = mGeneration.mTransform1.transform(v2); + mGeneration.mRenderOutput->outputSegment(wp0, wp1); + mGeneration.mRenderOutput->outputSegment(wp1, wp2); + mGeneration.mRenderOutput->outputSegment(wp2, wp0); + } + + mGeneration.processTriangle(hit.faceIndex, v0, v1, v2, vinds); + return true; + } + +protected: + SphereMeshContactGenerationCallback_NoScale &operator=(const SphereMeshContactGenerationCallback_NoScale &); +}; + +struct SphereMeshContactGenerationCallback_Scale : SphereMeshContactGenerationCallback_NoScale +{ + const Cm::FastVertex2ShapeScaling& mMeshScaling; + + SphereMeshContactGenerationCallback_Scale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, const PxTransform& transform1, const Cm::FastVertex2ShapeScaling& meshScaling, + ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput + ) : SphereMeshContactGenerationCallback_NoScale(meshData, shapeSphere, + transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput), + mMeshScaling (meshScaling) + { + } + + virtual ~SphereMeshContactGenerationCallback_Scale() {} + + virtual PxAgain processHit(const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + PxVec3 verts[3]; + getScaledVertices(verts, v0, v1, v2, false, mMeshScaling); + + if(gDrawTouchedTriangles) + { + (*mGeneration.mRenderOutput) << 0xffffffff; + (*mGeneration.mRenderOutput) << PxMat44(PxIdentity); + const PxVec3 wp0 = mGeneration.mTransform1.transform(verts[0]); + const PxVec3 wp1 = mGeneration.mTransform1.transform(verts[1]); + const PxVec3 wp2 = mGeneration.mTransform1.transform(verts[2]); + mGeneration.mRenderOutput->outputSegment(wp0, wp1); + mGeneration.mRenderOutput->outputSegment(wp1, wp2); + mGeneration.mRenderOutput->outputSegment(wp2, wp0); + } + + mGeneration.processTriangle(hit.faceIndex, verts[0], verts[1], verts[2], vinds); + return true; + } +protected: + SphereMeshContactGenerationCallback_Scale &operator=(const SphereMeshContactGenerationCallback_Scale &); +}; + +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Gu::contactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + // We must be in local space to use the cache + const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p); + const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + const TriangleMesh* meshData = shapeMesh.meshData; + + // mesh scale is not baked into cached verts + if(shapeMesh.scale.isIdentity()) + { + SphereMeshContactGenerationCallback_NoScale callback( + *meshData, shapeSphere, transform0, transform1, + contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + // PT: TODO: switch to sphere query here + const Box obb(sphereCenterInMeshSpace, PxVec3(inflatedRadius), PxMat33(PxIdentity)); + Midphase::intersectOBB(meshData, obb, callback, true); + } + else + { + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + SphereMeshContactGenerationCallback_Scale callback( + *meshData, shapeSphere, transform0, transform1, + meshScaling, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + PxVec3 obbCenter = sphereCenterInMeshSpace; + PxVec3 obbExtents = PxVec3(inflatedRadius); + PxMat33 obbRot(PxIdentity); + meshScaling.transformQueryBounds(obbCenter, obbExtents, obbRot); + + const Box obb(obbCenter, obbExtents, obbRot); + + Midphase::intersectOBB(meshData, obb, callback, true); + } + return contactBuffer.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace +{ +struct SphereHeightfieldContactGenerationCallback : EntityReport<PxU32> +{ + SphereMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + + SphereHeightfieldContactGenerationCallback( + HeightFieldUtil& hfUtil, + const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, + const PxTransform& transform1, + ContactBuffer& contactBuffer, + const PxVec3& sphereCenterInMeshSpace, + PxF32 inflatedRadius, + Cm::RenderOutput* renderOutput + ) : + mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput), + mHfUtil (hfUtil) + { + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + while(nb--) + { + const PxU32 triangleIndex = *indices++; + PxU32 vertIndices[3]; + PxTriangle currentTriangle; + mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, NULL, triangleIndex, false, false); + + mGeneration.processTriangle(triangleIndex, currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], vertIndices); + } + return true; + } +protected: + SphereHeightfieldContactGenerationCallback &operator=(const SphereHeightfieldContactGenerationCallback &); +}; +} + +bool Gu::contactSphereHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>(); + const HeightField& hf = *static_cast<HeightField*>(shapeMesh.heightField); + + HeightFieldUtil hfUtil(shapeMesh, hf); + + const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p); + const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + const PxVec3 inflatedRV3(inflatedRadius); + + const PxBounds3 bounds(sphereCenterInMeshSpace - inflatedRV3, sphereCenterInMeshSpace + inflatedRV3); + + SphereHeightfieldContactGenerationCallback blockCallback(hfUtil, shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + + return contactBuffer.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// |