// // 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-2018 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 "GuContactBuffer.h" #include "GuIntersectionRayBox.h" #include "GuDistanceSegmentBox.h" #include "GuInternal.h" #include "GuContactMethodImpl.h" #include "PsMathUtils.h" #include "PsUtilities.h" #include "GuGeometryUnion.h" #include "GuBoxConversion.h" using namespace physx; using namespace Gu; /*namespace Gu { const PxU8* getBoxEdges(); }*/ ///////// /*#include "CmRenderOutput.h" #include "PxsContext.h" static void gVisualizeBox(const Box& box, PxcNpThreadContext& context, PxU32 color=0xffffff) { PxMat33 rot(box.base.column0, box.base.column1, box.base.column2); PxMat44 m(rot, box.origin); DebugBox db(box.extent); Cm::RenderOutput& out = context.mRenderOutput; out << color << m; out << db; } 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 const PxReal fatBoxEdgeCoeff = 0.01f; static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip) { // if colliding edge (p3,p4) does not cross plane return no collision // same as if p3 and p4 on same side of plane return 0 // // Derivation: // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. // if d3 and d4 have the same sign, they're on the same side of the plane => no collision // We test both sides at the same time by only testing Sign(d3 * d4). // ### put that in the Plane class // ### also check that code in the triangle class that might be similar const PxReal d3 = plane.distance(p3); PxReal temp = d3 * plane.distance(p4); if(temp>0.0f) return false; // if colliding edge (p3,p4) and plane are parallel return no collision PxVec3 v2 = p4 - p3; temp = plane.n.dot(v2); if(temp==0.0f) return false; // ### epsilon would be better // compute intersection point of plane and colliding edge (p3,p4) ip = p3-v2*(d3/temp); // compute distance of intersection from line (ip, -dir) to line (p1,p2) dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))*coeff; if(dist<0.0f) return false; // compute intersection point on edge (p1,p2) line ip -= dist*dir; // check if intersection point (ip) is between edge (p1,p2) vertices temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); if(temp<0.0f) return true; // collision found return false; // no collision } static bool GuTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, 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 box PxReal Min1, Max1; { const PxReal BoxCen = box.center.dot(axis); const PxReal BoxExt = PxAbs(box.rot.column0.dot(axis)) * box.extents.x + PxAbs(box.rot.column1.dot(axis)) * box.extents.y + PxAbs(box.rot.column2.dot(axis)) * box.extents.z; Min1 = BoxCen - BoxExt; Max1 = BoxCen + BoxExt; } // Test projections if(max0=0.0f); const PxReal d1 = Max1 - min0; PX_ASSERT(d1>=0.0f); depth = physx::intrinsics::selectMin(d0, d1); return true; } static bool GuCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL) { PxVec3 Sep(PxReal(0)); PxReal PenDepth = PX_MAX_REAL; // Test normals for(PxU32 i=0;i<3;i++) { PxReal d; if(!GuTestAxis(box.rot[i], segment, radius, box, d)) return false; if(d(); const PxBoxGeometry& shapeBox = shape1.get(); // PT: TODO: move computations to local space // Capsule data Segment worldSegment; getCapsuleSegment(transform0, shapeCapsule, worldSegment); const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; // Box data Box worldBox; buildFrom(worldBox, transform1.p, shapeBox.halfExtents, transform1.q); // Collision detection PxReal t; PxVec3 onBox; const PxReal squareDist = distanceSegmentBoxSquared(worldSegment.p0, worldSegment.p1, worldBox.center, worldBox.extents, worldBox.rot, &t, &onBox); if(squareDist >= inflatedRadius*inflatedRadius) return false; PX_ASSERT(contactBuffer.count==0); if(squareDist != 0.0f) { // PT: the capsule segment doesn't intersect the box => distance-based version const PxVec3 onSegment = worldSegment.getPointAt(t); onBox = worldBox.center + worldBox.rot.transform(onBox); PxVec3 normal = onSegment - onBox; PxReal normalLen = normal.magnitude(); if(normalLen > 0.0f) { normal *= 1.0f/normalLen; // PT: generate VF contacts for segment's vertices vs box GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance); // PT: early exit if we already have 2 stable contacts if(contactBuffer.count==2) return true; // PT: else generate slower EE contacts GuGenerateEEContacts2(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance); // PT: run VF case for box-vertex-vs-capsule only if we don't have any contact yet if(!contactBuffer.count) contactBuffer.contact(onBox, normal, sqrtf(squareDist) - shapeCapsule.radius); } else { // On linux we encountered the following: // For a case where a segment endpoint lies on the surface of a box, the squared distance between segment and box was tiny but still larger than 0. // However, the computation of the normal length was exactly 0. In that case we should have switched to the penetration based version so we do it now // instead. goto PenetrationBasedCode; } } else { PenetrationBasedCode: // PT: the capsule segment intersects the box => penetration-based version // PT: compute penetration vector (MTD) PxVec3 sepAxis; PxReal depth; if(!GuCapsuleOBBOverlap3(worldSegment, shapeCapsule.radius, worldBox, &depth, &sepAxis)) return false; // PT: generate VF contacts for segment's vertices vs box GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis, params.mContactDistance); // PT: early exit if we already have 2 stable contacts if(contactBuffer.count==2) return true; // PT: else generate slower EE contacts GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis); if(!contactBuffer.count) { contactBuffer.contact(worldSegment.computeCenter(), sepAxis, -(shapeCapsule.radius + depth)); return true; } } return true; } }//Gu }//physx