Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/NarrowPhaseQuery.cpp
9913 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(ioCollector);304CollideShape(inShape, inShapeScale, inCenterOfMassTransform, settings, inBaseOffset, wrapper, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);305}306307void NarrowPhaseQuery::CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const308{309JPH_PROFILE_FUNCTION();310311class MyCollector : public CastShapeBodyCollector312{313public:314MyCollector(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :315CastShapeBodyCollector(ioCollector),316mShapeCast(inShapeCast),317mShapeCastSettings(inShapeCastSettings),318mBaseOffset(inBaseOffset),319mCollector(ioCollector),320mBodyLockInterface(inBodyLockInterface),321mBodyFilter(inBodyFilter),322mShapeFilter(inShapeFilter)323{324}325326virtual void AddHit(const ResultType &inResult) override327{328JPH_ASSERT(inResult.mFraction <= max(0.0f, mCollector.GetEarlyOutFraction()), "This hit should not have been passed on to the collector");329330// Only test shape if it passes the body filter331if (mBodyFilter.ShouldCollide(inResult.mBodyID))332{333// Lock the body334BodyLockRead lock(mBodyLockInterface, inResult.mBodyID);335if (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 callbacks336{337const Body &body = lock.GetBody();338339// Check body filter again now that we've locked the body340if (mBodyFilter.ShouldCollideLocked(body))341{342// Collect the transformed shape343TransformedShape ts = body.GetTransformedShape();344345// Notify collector of new body346mCollector.OnBody(body);347348// Release the lock now, we have all the info we need in the transformed shape349lock.ReleaseLock();350351// Do narrow phase collision check352ts.CastShape(mShapeCast, mShapeCastSettings, mBaseOffset, mCollector, mShapeFilter);353354// Notify collector of the end of this body355// We do this before updating the early out fraction so that the collector can still modify it356mCollector.OnBodyEnd();357358// Update early out fraction based on narrow phase collector359UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());360}361}362}363}364365RShapeCast mShapeCast;366const ShapeCastSettings & mShapeCastSettings;367RVec3 mBaseOffset;368CastShapeCollector & mCollector;369const BodyLockInterface & mBodyLockInterface;370const BodyFilter & mBodyFilter;371const ShapeFilter & mShapeFilter;372};373374// Do broadphase test375MyCollector collector(inShapeCast, inShapeCastSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);376mBroadPhaseQuery->CastAABox({ inShapeCast.mShapeWorldBounds, inShapeCast.mDirection }, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);377}378379void NarrowPhaseQuery::CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const380{381class MyCollector : public CollideShapeBodyCollector382{383public:384MyCollector(const AABox &inBox, TransformedShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :385CollideShapeBodyCollector(ioCollector),386mBox(inBox),387mCollector(ioCollector),388mBodyLockInterface(inBodyLockInterface),389mBodyFilter(inBodyFilter),390mShapeFilter(inShapeFilter)391{392}393394virtual void AddHit(const ResultType &inResult) override395{396// Only test shape if it passes the body filter397if (mBodyFilter.ShouldCollide(inResult))398{399// Lock the body400BodyLockRead lock(mBodyLockInterface, inResult);401if (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 callbacks402{403const Body &body = lock.GetBody();404405// Check body filter again now that we've locked the body406if (mBodyFilter.ShouldCollideLocked(body))407{408// Collect the transformed shape409TransformedShape ts = body.GetTransformedShape();410411// Notify collector of new body412mCollector.OnBody(body);413414// Release the lock now, we have all the info we need in the transformed shape415lock.ReleaseLock();416417// Do narrow phase collision check418ts.CollectTransformedShapes(mBox, mCollector, mShapeFilter);419420// Notify collector of the end of this body421// We do this before updating the early out fraction so that the collector can still modify it422mCollector.OnBodyEnd();423424// Update early out fraction based on narrow phase collector425UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());426}427}428}429}430431const AABox & mBox;432TransformedShapeCollector & mCollector;433const BodyLockInterface & mBodyLockInterface;434const BodyFilter & mBodyFilter;435const ShapeFilter & mShapeFilter;436};437438// Do broadphase test439MyCollector collector(inBox, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);440mBroadPhaseQuery->CollideAABox(inBox, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);441}442443JPH_NAMESPACE_END444445446