Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp
21212 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Collision/NarrowPhaseQuery.h>7#include <Jolt/Physics/Collision/CollisionDispatch.h>8#include <Jolt/Physics/Collision/RayCast.h>9#include <Jolt/Physics/Collision/AABoxCast.h>10#include <Jolt/Physics/Collision/ShapeCast.h>11#include <Jolt/Physics/Collision/CollideShape.h>12#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>13#include <Jolt/Physics/Collision/CastResult.h>14#include <Jolt/Physics/Collision/InternalEdgeRemovingCollector.h>1516JPH_NAMESPACE_BEGIN1718bool NarrowPhaseQuery::CastRay(const RRayCast &inRay, RayCastResult &ioHit, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter) const19{20JPH_PROFILE_FUNCTION();2122class MyCollector : public RayCastBodyCollector23{24public:25MyCollector(const RRayCast &inRay, RayCastResult &ioHit, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter) :26mRay(inRay),27mHit(ioHit),28mBodyLockInterface(inBodyLockInterface),29mBodyFilter(inBodyFilter)30{31ResetEarlyOutFraction(ioHit.mFraction);32}3334virtual void AddHit(const ResultType &inResult) override35{36JPH_ASSERT(inResult.mFraction < mHit.mFraction, "This hit should not have been passed on to the collector");3738// Only test shape if it passes the body filter39if (mBodyFilter.ShouldCollide(inResult.mBodyID))40{41// Lock the body42BodyLockRead lock(mBodyLockInterface, inResult.mBodyID);43if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks44{45const Body &body = lock.GetBody();4647// Check body filter again now that we've locked the body48if (mBodyFilter.ShouldCollideLocked(body))49{50// Collect the transformed shape51TransformedShape ts = body.GetTransformedShape();5253// Release the lock now, we have all the info we need in the transformed shape54lock.ReleaseLock();5556// Do narrow phase collision check57if (ts.CastRay(mRay, mHit))58{59// Test that we didn't find a further hit by accident60JPH_ASSERT(mHit.mFraction >= 0.0f && mHit.mFraction < GetEarlyOutFraction());6162// Update early out fraction based on narrow phase collector63UpdateEarlyOutFraction(mHit.mFraction);64}65}66}67}68}6970RRayCast mRay;71RayCastResult & mHit;72const BodyLockInterface & mBodyLockInterface;73const BodyFilter & mBodyFilter;74};7576// Do broadphase test, note that the broadphase uses floats so we drop precision here77MyCollector collector(inRay, ioHit, *mBodyLockInterface, inBodyFilter);78mBroadPhaseQuery->CastRay(RayCast(inRay), collector, inBroadPhaseLayerFilter, inObjectLayerFilter);79return ioHit.mFraction <= 1.0f;80}8182void NarrowPhaseQuery::CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const83{84JPH_PROFILE_FUNCTION();8586class MyCollector : public RayCastBodyCollector87{88public:89MyCollector(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :90RayCastBodyCollector(ioCollector),91mRay(inRay),92mRayCastSettings(inRayCastSettings),93mCollector(ioCollector),94mBodyLockInterface(inBodyLockInterface),95mBodyFilter(inBodyFilter),96mShapeFilter(inShapeFilter)97{98}99100virtual void AddHit(const ResultType &inResult) override101{102JPH_ASSERT(inResult.mFraction < mCollector.GetEarlyOutFraction(), "This hit should not have been passed on to the collector");103104// Only test shape if it passes the body filter105if (mBodyFilter.ShouldCollide(inResult.mBodyID))106{107// Lock the body108BodyLockRead lock(mBodyLockInterface, inResult.mBodyID);109if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks110{111const Body &body = lock.GetBody();112113// Check body filter again now that we've locked the body114if (mBodyFilter.ShouldCollideLocked(body))115{116// Collect the transformed shape117TransformedShape ts = body.GetTransformedShape();118119// Notify collector of new body120mCollector.OnBody(body);121122// Release the lock now, we have all the info we need in the transformed shape123lock.ReleaseLock();124125// Do narrow phase collision check126ts.CastRay(mRay, mRayCastSettings, mCollector, mShapeFilter);127128// Notify collector of the end of this body129// We do this before updating the early out fraction so that the collector can still modify it130mCollector.OnBodyEnd();131132// Update early out fraction based on narrow phase collector133UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());134}135}136}137}138139RRayCast mRay;140RayCastSettings mRayCastSettings;141CastRayCollector & mCollector;142const BodyLockInterface & mBodyLockInterface;143const BodyFilter & mBodyFilter;144const ShapeFilter & mShapeFilter;145};146147// Do broadphase test, note that the broadphase uses floats so we drop precision here148MyCollector collector(inRay, inRayCastSettings, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);149mBroadPhaseQuery->CastRay(RayCast(inRay), collector, inBroadPhaseLayerFilter, inObjectLayerFilter);150}151152void NarrowPhaseQuery::CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const153{154JPH_PROFILE_FUNCTION();155156class MyCollector : public CollideShapeBodyCollector157{158public:159MyCollector(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :160CollideShapeBodyCollector(ioCollector),161mPoint(inPoint),162mCollector(ioCollector),163mBodyLockInterface(inBodyLockInterface),164mBodyFilter(inBodyFilter),165mShapeFilter(inShapeFilter)166{167}168169virtual void AddHit(const ResultType &inResult) override170{171// Only test shape if it passes the body filter172if (mBodyFilter.ShouldCollide(inResult))173{174// Lock the body175BodyLockRead lock(mBodyLockInterface, inResult);176if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks177{178const Body &body = lock.GetBody();179180// Check body filter again now that we've locked the body181if (mBodyFilter.ShouldCollideLocked(body))182{183// Collect the transformed shape184TransformedShape ts = body.GetTransformedShape();185186// Notify collector of new body187mCollector.OnBody(body);188189// Release the lock now, we have all the info we need in the transformed shape190lock.ReleaseLock();191192// Do narrow phase collision check193ts.CollidePoint(mPoint, mCollector, mShapeFilter);194195// Notify collector of the end of this body196// We do this before updating the early out fraction so that the collector can still modify it197mCollector.OnBodyEnd();198199// Update early out fraction based on narrow phase collector200UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());201}202}203}204}205206RVec3 mPoint;207CollidePointCollector & mCollector;208const BodyLockInterface & mBodyLockInterface;209const BodyFilter & mBodyFilter;210const ShapeFilter & mShapeFilter;211};212213// Do broadphase test (note: truncates double to single precision since the broadphase uses single precision)214MyCollector collector(inPoint, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);215mBroadPhaseQuery->CollidePoint(Vec3(inPoint), collector, inBroadPhaseLayerFilter, inObjectLayerFilter);216}217218void NarrowPhaseQuery::CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const219{220JPH_PROFILE_FUNCTION();221222class MyCollector : public CollideShapeBodyCollector223{224public:225MyCollector(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :226CollideShapeBodyCollector(ioCollector),227mShape(inShape),228mShapeScale(inShapeScale),229mCenterOfMassTransform(inCenterOfMassTransform),230mCollideShapeSettings(inCollideShapeSettings),231mBaseOffset(inBaseOffset),232mCollector(ioCollector),233mBodyLockInterface(inBodyLockInterface),234mBodyFilter(inBodyFilter),235mShapeFilter(inShapeFilter)236{237}238239virtual void AddHit(const ResultType &inResult) override240{241// Only test shape if it passes the body filter242if (mBodyFilter.ShouldCollide(inResult))243{244// Lock the body245BodyLockRead lock(mBodyLockInterface, inResult);246if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks247{248const Body &body = lock.GetBody();249250// Check body filter again now that we've locked the body251if (mBodyFilter.ShouldCollideLocked(body))252{253// Collect the transformed shape254TransformedShape ts = body.GetTransformedShape();255256// Notify collector of new body257mCollector.OnBody(body);258259// Release the lock now, we have all the info we need in the transformed shape260lock.ReleaseLock();261262// Do narrow phase collision check263ts.CollideShape(mShape, mShapeScale, mCenterOfMassTransform, mCollideShapeSettings, mBaseOffset, mCollector, mShapeFilter);264265// Notify collector of the end of this body266// We do this before updating the early out fraction so that the collector can still modify it267mCollector.OnBodyEnd();268269// Update early out fraction based on narrow phase collector270UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());271}272}273}274}275276const Shape * mShape;277Vec3 mShapeScale;278RMat44 mCenterOfMassTransform;279const CollideShapeSettings & mCollideShapeSettings;280RVec3 mBaseOffset;281CollideShapeCollector & mCollector;282const BodyLockInterface & mBodyLockInterface;283const BodyFilter & mBodyFilter;284const ShapeFilter & mShapeFilter;285};286287// Calculate bounds for shape and expand by max separation distance288AABox bounds = inShape->GetWorldSpaceBounds(inCenterOfMassTransform, inShapeScale);289bounds.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance));290291// Do broadphase test292MyCollector collector(inShape, inShapeScale, inCenterOfMassTransform, inCollideShapeSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);293mBroadPhaseQuery->CollideAABox(bounds, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);294}295296void NarrowPhaseQuery::CollideShapeWithInternalEdgeRemoval(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const297{298// We require these settings for internal edge removal to work299CollideShapeSettings settings = inCollideShapeSettings;300settings.mActiveEdgeMode = EActiveEdgeMode::CollideWithAll;301settings.mCollectFacesMode = ECollectFacesMode::CollectFaces;302303InternalEdgeRemovingCollector wrapper(ioCollector304#ifdef JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG305, inBaseOffset306#endif // JPH_INTERNAL_EDGE_REMOVING_COLLECTOR_DEBUG307);308CollideShape(inShape, inShapeScale, inCenterOfMassTransform, settings, inBaseOffset, wrapper, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);309}310311void NarrowPhaseQuery::CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const312{313JPH_PROFILE_FUNCTION();314315class MyCollector : public CastShapeBodyCollector316{317public:318MyCollector(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :319CastShapeBodyCollector(ioCollector),320mShapeCast(inShapeCast),321mShapeCastSettings(inShapeCastSettings),322mBaseOffset(inBaseOffset),323mCollector(ioCollector),324mBodyLockInterface(inBodyLockInterface),325mBodyFilter(inBodyFilter),326mShapeFilter(inShapeFilter)327{328}329330virtual void AddHit(const ResultType &inResult) override331{332JPH_ASSERT(inResult.mFraction <= max(0.0f, mCollector.GetEarlyOutFraction()), "This hit should not have been passed on to the collector");333334// Only test shape if it passes the body filter335if (mBodyFilter.ShouldCollide(inResult.mBodyID))336{337// Lock the body338BodyLockRead lock(mBodyLockInterface, inResult.mBodyID);339if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks340{341const Body &body = lock.GetBody();342343// Check body filter again now that we've locked the body344if (mBodyFilter.ShouldCollideLocked(body))345{346// Collect the transformed shape347TransformedShape ts = body.GetTransformedShape();348349// Notify collector of new body350mCollector.OnBody(body);351352// Release the lock now, we have all the info we need in the transformed shape353lock.ReleaseLock();354355// Do narrow phase collision check356ts.CastShape(mShapeCast, mShapeCastSettings, mBaseOffset, mCollector, mShapeFilter);357358// Notify collector of the end of this body359// We do this before updating the early out fraction so that the collector can still modify it360mCollector.OnBodyEnd();361362// Update early out fraction based on narrow phase collector363UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());364}365}366}367}368369RShapeCast mShapeCast;370const ShapeCastSettings & mShapeCastSettings;371RVec3 mBaseOffset;372CastShapeCollector & mCollector;373const BodyLockInterface & mBodyLockInterface;374const BodyFilter & mBodyFilter;375const ShapeFilter & mShapeFilter;376};377378// Do broadphase test379MyCollector collector(inShapeCast, inShapeCastSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);380mBroadPhaseQuery->CastAABox({ inShapeCast.mShapeWorldBounds, inShapeCast.mDirection }, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);381}382383void NarrowPhaseQuery::CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const384{385class MyCollector : public CollideShapeBodyCollector386{387public:388MyCollector(const AABox &inBox, TransformedShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :389CollideShapeBodyCollector(ioCollector),390mBox(inBox),391mCollector(ioCollector),392mBodyLockInterface(inBodyLockInterface),393mBodyFilter(inBodyFilter),394mShapeFilter(inShapeFilter)395{396}397398virtual void AddHit(const ResultType &inResult) override399{400// Only test shape if it passes the body filter401if (mBodyFilter.ShouldCollide(inResult))402{403// Lock the body404BodyLockRead lock(mBodyLockInterface, inResult);405if (lock.SucceededAndIsInBroadPhase()) // Race condition: body could have been removed since it has been found in the broadphase, ensures body is in the broadphase while we call the callbacks406{407const Body &body = lock.GetBody();408409// Check body filter again now that we've locked the body410if (mBodyFilter.ShouldCollideLocked(body))411{412// Collect the transformed shape413TransformedShape ts = body.GetTransformedShape();414415// Notify collector of new body416mCollector.OnBody(body);417418// Release the lock now, we have all the info we need in the transformed shape419lock.ReleaseLock();420421// Do narrow phase collision check422ts.CollectTransformedShapes(mBox, mCollector, mShapeFilter);423424// Notify collector of the end of this body425// We do this before updating the early out fraction so that the collector can still modify it426mCollector.OnBodyEnd();427428// Update early out fraction based on narrow phase collector429UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());430}431}432}433}434435const AABox & mBox;436TransformedShapeCollector & mCollector;437const BodyLockInterface & mBodyLockInterface;438const BodyFilter & mBodyFilter;439const ShapeFilter & mShapeFilter;440};441442// Do broadphase test443MyCollector collector(inBox, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);444mBroadPhaseQuery->CollideAABox(inBox, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);445}446447JPH_NAMESPACE_END448449450