Revision control
++ // Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
++ // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
++ // SPDX-FileCopyrightText: Copyright (c) 2008-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
++ // SPDX-License-Identifier: Apache-2.0
++
++ #include "geomutils/PxContactBuffer.h"
++ #include "GuContactMethodImpl.h"
++
++ using namespace physx;
++
++ //This version is ported 1:1 from novodex
++ static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin,
++ PxReal sphereRadius,
++ const PxVec3& boxExtents,
++ // const PxcCachedTransforms& boxCacheTransform,
++ const PxTransform32& boxTransform,
++ PxVec3& point,
++ PxVec3& normal,
++ PxReal& separation,
++ PxReal contactDistance)
++ {
++ // const PxTransform& boxTransform = boxCacheTransform.getShapeToWorld();
++
++ //returns true on contact
++ const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center;
++ PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords.
++
++ //check if delta is outside ABB - and clip the vector to the ABB.
++ bool outside = false;
++
++ if (dRot.x < -boxExtents.x)
++ {
++ outside = true;
++ dRot.x = -boxExtents.x;
++ }
++ else if (dRot.x > boxExtents.x)
++ {
++ outside = true;
++ dRot.x = boxExtents.x;
++ }
++
++ if (dRot.y < -boxExtents.y)
++ {
++ outside = true;
++ dRot.y = -boxExtents.y;
++ }
++ else if (dRot.y > boxExtents.y)
++ {
++ outside = true;
++ dRot.y = boxExtents.y;
++ }
++
++ if (dRot.z < -boxExtents.z)
++ {
++ outside = true;
++ dRot.z =-boxExtents.z;
++ }
++ else if (dRot.z > boxExtents.z)
++ {
++ outside = true;
++ dRot.z = boxExtents.z;
++ }
++
++ if (outside) //if clipping was done, sphere center is outside of box.
++ {
++ point = boxTransform.rotate(dRot); //get clipped delta back in world coords.
++ normal = delta - point; //what we clipped away.
++ const PxReal lenSquared = normal.magnitudeSquared();
++ const PxReal inflatedDist = sphereRadius + contactDistance;
++ if (lenSquared > inflatedDist * inflatedDist)
++ return false; //disjoint
++
++ //normalize to make it into the normal:
++ separation = PxRecipSqrt(lenSquared);
++ normal *= separation;
++ separation *= lenSquared;
++ //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal.
++ //we could also use point here, which has same direction.
++ //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish.
++ //We'll just use vertex face for now, this info isn't really being used anyway.
++ //contact point is point on surface of cube closest to sphere center.
++ point += boxTransform.p;
++ separation -= sphereRadius;
++ return true;
++ }
++ else
++ {
++ //center is in box, we definitely have a contact.
++ PxVec3 locNorm; //local coords contact normal
++
++ /*const*/ PxVec3 absdRot;
++ absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z));
++ /*const*/ PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions.
++
++ //find smallest element of distToSurface
++ if (distToSurface.y < distToSurface.x)
++ {
++ if (distToSurface.y < distToSurface.z)
++ {
++ //y
++ locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f);
++ separation = -distToSurface.y;
++ }
++ else
++ {
++ //z
++ locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f);
++ separation = -distToSurface.z;
++ }
++ }
++ else
++ {
++ if (distToSurface.x < distToSurface.z)
++ {
++ //x
++ locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f);
++ separation = -distToSurface.x;
++ }
++ else
++ {
++ //z
++ locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f);
++ separation = -distToSurface.z;
++ }
++ }
++ //separation so far is just the embedding of the center point; we still have to push out all of the radius.
++ point = sphereOrigin;
++ normal = boxTransform.rotate(locNorm);
++ separation -= sphereRadius;
++ return true;
++ }
++ }
++
++ bool Gu::contactSphereBox(GU_CONTACT_METHOD_ARGS)
++ {
++ PX_UNUSED(renderOutput);
++ PX_UNUSED(cache);
++
++ const PxSphereGeometry& sphereGeom = checkedCast<PxSphereGeometry>(shape0);
++ const PxBoxGeometry& boxGeom = checkedCast<PxBoxGeometry>(shape1);
++
++ PxVec3 normal;
++ PxVec3 point;
++ PxReal separation;
++ if(!ContactSphereBox(transform0.p, sphereGeom.radius, boxGeom.halfExtents, transform1, point, normal, separation, params.mContactDistance))
++ return false;
++
++ contactBuffer.contact(point, normal, separation);
++ return true;
++ }