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/GuContactCapsuleMesh.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/GuContactCapsuleMesh.cpp')
| -rw-r--r-- | PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp new file mode 100644 index 00000000..da282865 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp @@ -0,0 +1,636 @@ +// 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 "GuIntersectionEdgeEdge.h" +#include "GuDistanceSegmentTriangle.h" +#include "GuIntersectionRayTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuInternal.h" +#include "GuContactMethodImpl.h" +#include "GuFeatureCode.h" +#include "GuContactBuffer.h" +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuConvexEdgeFlags.h" +#include "GuGeometryUnion.h" +#include "GuSIMDHelpers.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; + +#define DEBUG_RENDER_MESHCONTACTS 0 + +#if DEBUG_RENDER_MESHCONTACTS +#include "PxPhysics.h" +#include "PxScene.h" +#endif + +#define USE_AABB_TRI_CULLING + +//#define USE_CAPSULE_TRI_PROJ_CULLING +//#define USE_CAPSULE_TRI_SAT_CULLING +#define VISUALIZE_TOUCHED_TRIS 0 +#define VISUALIZE_CULLING_BOX 0 + +#if VISUALIZE_TOUCHED_TRIS +#include "CmRenderOutput.h" +#include "PxsContactManager.h" +#include "PxsContext.h" +static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) +{ + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::LINES << a << b; +} +static void gVisualizeTri(const PxVec3& a, const PxVec3& b, const PxVec3& c, PxcNpThreadContext& context, PxU32 color=0xffffff) +{ + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::TRIANGLES << a << b << c; +} + +static PxU32 gColors[8] = { 0xff0000ff, 0xff00ff00, 0xffff0000, + 0xff00ffff, 0xffff00ff, 0xffffff00, + 0xff000080, 0xff008000}; +#endif + +static const float fatBoxEdgeCoeff = 0.01f; + +static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, + const PxVec3* PX_RESTRICT triVerts, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project triangle + float Min1, Max1; + { + Min1 = Max1 = triVerts[0].dot(axis); + const PxReal dp1 = triVerts[1].dot(axis); + Min1 = physx::intrinsics::selectMin(Min1, dp1); + Max1 = physx::intrinsics::selectMax(Max1, dp1); + const PxReal dp2 = triVerts[2].dot(axis); + Min1 = physx::intrinsics::selectMin(Min1, dp2); + Max1 = physx::intrinsics::selectMax(Max1, dp2); + } + + // Test projections + if(max0<Min1 || Max1<min0) + return false; + + const PxReal d0 = max0 - Min1; + PX_ASSERT(d0>=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +PX_FORCE_INLINE static PxVec3 PxcComputeTriangleNormal(const PxVec3* PX_RESTRICT triVerts) +{ + return ((triVerts[0]-triVerts[1]).cross(triVerts[0]-triVerts[2])).getNormalized(); +} + +PX_FORCE_INLINE static PxVec3 PxcComputeTriangleCenter(const PxVec3* PX_RESTRICT triVerts) +{ + static const PxReal inv3 = 1.0f / 3.0f; + return (triVerts[0] + triVerts[1] + triVerts[2]) * inv3; +} + +static bool PxcCapsuleTriOverlap3(PxU8 edgeFlags, const Segment& segment, PxReal radius, const PxVec3* PX_RESTRICT triVerts, + PxReal* PX_RESTRICT t=NULL, PxVec3* PX_RESTRICT pp=NULL) +{ + PxReal penDepth = PX_MAX_REAL; + + // Test normal + PxVec3 sep = PxcComputeTriangleNormal(triVerts); + if(!PxcTestAxis(sep, segment, radius, triVerts, penDepth)) + return false; + + // Test edges + // ML:: use the active edge flag instead of the concave flag + const PxU32 activeEdgeFlag[] = {ETD_CONVEX_EDGE_01, ETD_CONVEX_EDGE_12, ETD_CONVEX_EDGE_20}; + const PxVec3 capsuleAxis = (segment.p1 - segment.p0).getNormalized(); + for(PxU32 i=0;i<3;i++) + { + //bool active =((edgeFlags & ignoreEdgeFlag[i]) == 0); + + if(edgeFlags & activeEdgeFlag[i]) + { + + const PxVec3 e0 = triVerts[i]; +// const PxVec3 e1 = triVerts[(i+1)%3]; + const PxVec3 e1 = triVerts[Ps::getNextIndex3(i)]; + const PxVec3 edge = e0 - e1; + + PxVec3 cross = capsuleAxis.cross(edge); + if(!Ps::isAlmostZero(cross)) + { + cross = cross.getNormalized(); + PxReal d; + if(!PxcTestAxis(cross, segment, radius, triVerts, d)) + return false; + if(d<penDepth) + { + penDepth = d; + sep = cross; + } + } + } + } + + const PxVec3 capsuleCenter = segment.computeCenter(); + const PxVec3 triCenter = PxcComputeTriangleCenter(triVerts); + const PxVec3 witness = capsuleCenter - triCenter; + + if(sep.dot(witness) < 0.0f) + sep = -sep; + + if(t) *t = penDepth; + if(pp) *pp = sep; + + return true; +} + +static void PxcGenerateVFContacts( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, + const PxReal radius, const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, + PxU32 triangleIndex, PxReal contactDistance) +{ + const PxVec3* PX_RESTRICT Ptr = &segment.p0; + for(PxU32 i=0;i<2;i++) + { + const PxVec3& Pos = Ptr[i]; + PxReal t,u,v; + if(intersectRayTriangleCulling(Pos, -normal, triVerts[0], triVerts[1], triVerts[2], t, u, v, 1e-3f) && t < radius + contactDistance) + { + const PxVec3 Hit = meshAbsPose.transform(Pos - t * normal); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(Hit, wn, t-radius, triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << Hit << (Hit + wn * 10.0f); + #endif + } + } +} + +// PT: PxcGenerateEEContacts2 uses a segment-triangle distance function, which breaks when the segment +// intersects the triangle, in which case you need to switch to a penetration-depth computation. +// If you don't do this thin capsules don't work. +static void PxcGenerateEEContacts( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex) +{ + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + for(PxU32 i=0;i<3;i++) + { + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], -normal, s0, s1, dist, ip)) + { + ip = meshAbsPose.transform(ip); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(ip, wn, - (radius + dist), triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << ip << (ip + wn * 10.0f); + #endif + } + } +} + +static void PxcGenerateEEContacts2( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex, PxReal contactDistance) +{ + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + for(PxU32 i=0;i<3;i++) + { + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], normal, s0, s1, dist, ip) && dist < radius+contactDistance) + { + ip = meshAbsPose.transform(ip); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(ip, wn, dist - radius, triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << ip << (ip + wn * 10.0f); + #endif + } + } +} + +namespace +{ +struct CapsuleMeshContactGeneration +{ + ContactBuffer& mContactBuffer; + const Cm::Matrix34 mMeshAbsPose; + const Segment& mMeshCapsule; +#ifdef USE_AABB_TRI_CULLING + Vec3p mBC; + Vec3p mBE; +#endif + PxReal mInflatedRadius; + PxReal mContactDistance; + PxReal mShapeCapsuleRadius; + + CapsuleMeshContactGeneration(ContactBuffer& contactBuffer, const PxTransform& transform1, const Segment& meshCapsule, PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius) : + mContactBuffer (contactBuffer), + mMeshAbsPose (Cm::Matrix34(transform1)), + mMeshCapsule (meshCapsule), + mInflatedRadius (inflatedRadius), + mContactDistance (contactDistance), + mShapeCapsuleRadius (shapeCapsuleRadius) + { + PX_ASSERT(contactBuffer.count==0); +#ifdef USE_AABB_TRI_CULLING + mBC = (meshCapsule.p0 + meshCapsule.p1)*0.5f; + const Vec3p be = (meshCapsule.p0 - meshCapsule.p1)*0.5f; + mBE.x = fabsf(be.x) + inflatedRadius; + mBE.y = fabsf(be.y) + inflatedRadius; + mBE.z = fabsf(be.z) + inflatedRadius; +#endif + } + + void processTriangle(PxU32 triangleIndex, const TrianglePadded& tri, PxU8 extraData/*, const PxU32* vertInds*/) + { +#ifdef USE_AABB_TRI_CULLING + #if VISUALIZE_CULLING_BOX + { + Cm::RenderOutput& out = context.mRenderOutput; + PxTransform idt = PxTransform(PxIdentity); + out << idt; + out << 0xffffffff; + out << Cm::DebugBox(mBC, mBE, true); + } + #endif +#endif + const PxVec3& p0 = tri.verts[0]; + const PxVec3& p1 = tri.verts[1]; + const PxVec3& p2 = tri.verts[2]; + +#ifdef USE_AABB_TRI_CULLING + // PT: this one is safe because triangle class is padded + // PT: TODO: is this test really needed? Not done in midphase already? + if(!intersectTriangleBox_Unsafe(mBC, mBE, p0, p1, p2)) + return; +#endif + +#ifdef USE_CAPSULE_TRI_PROJ_CULLING + PxVec3 triCenter = (p0 + p1 + p2)*0.33333333f; + PxVec3 delta = mBC - triCenter; + + PxReal depth; + if(!PxcTestAxis(delta, mMeshCapsule, mInflatedRadius, tri.verts, depth)) + return; +#endif + +#if VISUALIZE_TOUCHED_TRIS + gVisualizeTri(p0, p1, p2, context, PxDebugColor::eARGB_RED); +#endif + +#ifdef USE_CAPSULE_TRI_SAT_CULLING + PxVec3 SepAxis; + if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis)) + return; +#endif + + PxReal t,u,v; + const PxVec3 p1_p0 = p1 - p0; + const PxVec3 p2_p0 = p2 - p0; + const PxReal squareDist = distanceSegmentTriangleSquared(mMeshCapsule, p0, p1_p0, p2_p0, &t, &u, &v); + + // PT: do cheaper test first! + if(squareDist >= mInflatedRadius*mInflatedRadius) + return; + + // PT: backface culling without the normalize + // PT: TODO: consider doing before the segment-triangle distance test if it's cheaper + const PxVec3 planeNormal = p1_p0.cross(p2_p0); + const PxF32 planeD = planeNormal.dot(p0); // PT: actually -d compared to PxcPlane + if(planeNormal.dot(mBC) < planeD) + return; + + if(squareDist > 0.001f*0.001f) + { + // Contact information + PxVec3 normal; + if(selectNormal(extraData, u, v)) + { + normal = planeNormal.getNormalized(); + } + else + { + const PxVec3 pointOnTriangle = Ps::computeBarycentricPoint(p0, p1, p2, u, v); + + const PxVec3 pointOnSegment = mMeshCapsule.getPointAt(t); + normal = pointOnSegment - pointOnTriangle; + const PxReal l = normal.magnitude(); + if(l == 0.0f) + return; + normal = normal / l; + } + + PxcGenerateEEContacts2(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance); + PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance); + } + else + { + PxVec3 SepAxis; + if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis)) + return; + + PxcGenerateEEContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex); + PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex, mContactDistance); + } + } + +private: + CapsuleMeshContactGeneration& operator=(const CapsuleMeshContactGeneration&); +}; + +struct CapsuleMeshContactGenerationCallback_NoScale : MeshHitCallback<PxRaycastHit> +{ + CapsuleMeshContactGeneration mGeneration; + const TriangleMesh* mMeshData; + + CapsuleMeshContactGenerationCallback_NoScale( + ContactBuffer& contactBuffer, + const PxTransform& transform1, const Segment& meshCapsule, + PxReal inflatedRadius, PxReal contactDistance, + PxReal shapeCapsuleRadius, const TriangleMesh* meshData + ) : + MeshHitCallback<PxRaycastHit> (CallbackMode::eMULTIPLE), + mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius), + mMeshData (meshData) + { + PX_ASSERT(contactBuffer.count==0); + } + + PX_FORCE_INLINE PxAgain processTriangle(const PxRaycastHit& hit, const TrianglePadded& tri) + { + const PxU32 triangleIndex = hit.faceIndex; + + //ML::set all the edges to be active, if the mExtraTrigData exist, we overwrite this flag + const PxU8 extraData = getConvexEdgeFlags(mMeshData->getExtraTrigData(), triangleIndex); + mGeneration.processTriangle(triangleIndex, tri, extraData); + return true; + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/) + { + TrianglePadded tri; + // PT: TODO: revisit this, avoid the copy + tri.verts[0] = v0; + tri.verts[1] = v1; + tri.verts[2] = v2; + return processTriangle(hit, tri); + } + +private: + CapsuleMeshContactGenerationCallback_NoScale& operator=(const CapsuleMeshContactGenerationCallback_NoScale&); +}; + +struct CapsuleMeshContactGenerationCallback_Scale : CapsuleMeshContactGenerationCallback_NoScale +{ + const Cm::FastVertex2ShapeScaling& mScaling; + + CapsuleMeshContactGenerationCallback_Scale( + ContactBuffer& contactBuffer, + const PxTransform& transform1, const Segment& meshCapsule, + PxReal inflatedRadius, const Cm::FastVertex2ShapeScaling& scaling, PxReal contactDistance, + PxReal shapeCapsuleRadius, const TriangleMesh* meshData + ) : + CapsuleMeshContactGenerationCallback_NoScale(contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius, meshData), + mScaling (scaling) + { + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/) + { + TrianglePadded tri; + getScaledVertices(tri.verts, v0, v1, v2, false, mScaling); + return processTriangle(hit, tri); + } + +private: + CapsuleMeshContactGenerationCallback_Scale& operator=(const CapsuleMeshContactGenerationCallback_Scale&); +}; + +} + +// PT: computes local capsule without going to world-space +static PX_FORCE_INLINE Segment computeLocalCapsule(const PxTransform& transform0, const PxTransform& transform1, const PxCapsuleGeometry& shapeCapsule) +{ + const PxVec3 halfHeight = getCapsuleHalfHeightVector(transform0, shapeCapsule); + const PxVec3 delta = transform1.p - transform0.p; + return Segment( + transform1.rotateInv(halfHeight - delta), + transform1.rotateInv(-halfHeight - delta)); +} + +bool Gu::contactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate! + const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule); + + const TriangleMesh* meshData = shapeMesh.meshData; + + //bound the capsule in shape space by an OBB: + Box queryBox; + { + const Capsule queryCapsule(meshCapsule, inflatedRadius); + queryBox.create(queryCapsule); + } + + if(shapeMesh.scale.isIdentity()) + { + CapsuleMeshContactGenerationCallback_NoScale callback(contactBuffer, transform1, meshCapsule, + inflatedRadius, params.mContactDistance, shapeCapsule.radius, meshData); + + // PT: TODO: switch to capsule query here + Midphase::intersectOBB(meshData, queryBox, callback, true); + } + else + { + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + CapsuleMeshContactGenerationCallback_Scale callback(contactBuffer, transform1, meshCapsule, + inflatedRadius, meshScaling, params.mContactDistance, shapeCapsule.radius, meshData); + + //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region: + + //apply the skew transform to the box: + meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot); + + Midphase::intersectOBB(meshData, queryBox, callback, true); + } + return contactBuffer.count > 0; +} + +namespace +{ +struct CapsuleHeightfieldContactGenerationCallback : EntityReport<PxU32> +{ + CapsuleMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + const PxTransform& mTransform1; + + CapsuleHeightfieldContactGenerationCallback( + ContactBuffer& contactBuffer, + const PxTransform& transform1, HeightFieldUtil& hfUtil, const Segment& meshCapsule, + PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius + ) : + mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius), + mHfUtil (hfUtil), + mTransform1 (transform1) + { + PX_ASSERT(contactBuffer.count==0); + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + const PxU8 nextInd[] = {2,0,1}; + + while(nb--) + { + const PxU32 triangleIndex = *indices++; + + PxU32 vertIndices[3]; + TrianglePadded currentTriangle; // in world space + PxU32 adjInds[3]; + mHfUtil.getTriangle(mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + if(adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + mHfUtil.getTriangle(mTransform1, adjTri, NULL, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + if(projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if(proj < 0.999f) + { + triFlags |= 1 << (a+3); + } + } + } + else + { + triFlags |= 1 << (a+3); + } + } + + mGeneration.processTriangle(triangleIndex, currentTriangle, triFlags); + } + return true; + } + +private: + CapsuleHeightfieldContactGenerationCallback& operator=(const CapsuleHeightfieldContactGenerationCallback&); +}; +} + +bool Gu::contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>(); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate! + const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule); + + // We must be in local space to use the cache + + const HeightField& hf = *static_cast<HeightField*>(shapeMesh.heightField); + HeightFieldUtil hfUtil(shapeMesh, hf); + + CapsuleHeightfieldContactGenerationCallback callback( + contactBuffer, transform1, hfUtil, meshCapsule, inflatedRadius, params.mContactDistance, shapeCapsule.radius); + + //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region: + + //bound the capsule in shape space by an OBB: + + PxBounds3 bounds; + bounds.maximum = PxVec3(shapeCapsule.halfHeight + inflatedRadius, inflatedRadius, inflatedRadius); + bounds.minimum = -bounds.maximum; + + bounds = PxBounds3::transformFast(transform1.transformInv(transform0), bounds); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &callback); + + return contactBuffer.count > 0; +} |