aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/PhysX/src/NpSceneQueries.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/PhysX/src/NpSceneQueries.cpp
downloadphysx-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/PhysX/src/NpSceneQueries.cpp')
-rw-r--r--PhysX_3.4/Source/PhysX/src/NpSceneQueries.cpp836
1 files changed, 836 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/PhysX/src/NpSceneQueries.cpp b/PhysX_3.4/Source/PhysX/src/NpSceneQueries.cpp
new file mode 100644
index 00000000..afadf740
--- /dev/null
+++ b/PhysX_3.4/Source/PhysX/src/NpSceneQueries.cpp
@@ -0,0 +1,836 @@
+// 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 "GuIntersectionRayBox.h"
+#include "PxGeometryQuery.h"
+#include "NpRigidDynamic.h"
+#include "NpQueryShared.h"
+#include "SqPruner.h"
+#include "GuBounds.h"
+#include "GuIntersectionRay.h"
+
+// Synchronous scene queries
+
+using namespace physx;
+using namespace Sq;
+using namespace Gu;
+
+#if PX_SUPPORT_PVD
+#include "NpPvdSceneQueryCollector.h"
+#endif
+
+namespace local
+{
+ // helper class to encapsulate Scb::Actor and Shape together with PxActorShape
+ struct ActorShape : PxActorShape
+ {
+ const Scb::Shape* scbShape;
+ const Scb::Actor* scbActor;
+
+ ActorShape() : PxActorShape() {}
+
+ ActorShape(PxRigidActor* eaActor, PxShape* eaShape, Scb::Shape* sShape, Scb::Actor* sActor) : PxActorShape(eaActor, eaShape)
+ {
+ scbShape = sShape;
+ scbActor = sActor;
+ }
+ };
+
+ // fill the helper actor shape
+ static PX_FORCE_INLINE void populate(const PrunerPayload& payload, ActorShape& as)
+ {
+ Scb::Shape* localShape = reinterpret_cast<Scb::Shape*>(payload.data[0]);
+ Scb::Actor* localActor = reinterpret_cast<Scb::Actor*>(payload.data[1]);
+
+ as.scbShape = localShape;
+ as.scbActor = localActor;
+
+ as.actor = static_cast<PxRigidActor*>(static_cast<const Sc::RigidCore&>(localActor->getActorCore()).getPxActor());
+ as.shape = localShape->getScShape().getPxShape();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool NpSceneQueries::raycast(
+ const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
+ PxHitCallback<PxRaycastHit>& hits, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
+ const PxQueryCache* cache) const
+{
+ PX_PROFILE_ZONE("SceneQuery.raycast", getContextId());
+ NP_READ_CHECK(this);
+ PX_SIMD_GUARD;
+
+ MultiQueryInput input(origin, unitDir, distance);
+ return multiQuery<PxRaycastHit>(input, hits, hitFlags, cache, filterData, filterCall, NULL);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool NpSceneQueries::overlap(
+ const PxGeometry& geometry, const PxTransform& pose, PxOverlapCallback& hits,
+ const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const
+{
+ PX_PROFILE_ZONE("SceneQuery.overlap", getContextId());
+ NP_READ_CHECK(this);
+ PX_SIMD_GUARD;
+
+ MultiQueryInput input(&geometry, &pose);
+ // we are not supporting cache for overlaps for some reason
+ return multiQuery<PxOverlapHit>(input, hits, PxHitFlags(), NULL, filterData, filterCall, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool NpSceneQueries::sweep(
+ const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance,
+ PxHitCallback<PxSweepHit>& hits, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
+ const PxQueryCache* cache, const PxReal inflation) const
+{
+ PX_PROFILE_ZONE("SceneQuery.sweep", getContextId());
+ NP_READ_CHECK(this);
+ PX_SIMD_GUARD;
+
+#if PX_CHECKED
+ if(!PxGeometryQuery::isValid(geometry))
+ {
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry is not valid");
+ return false;
+ }
+#endif // PX_CHECKED
+
+
+ if((hitFlags & PxHitFlag::ePRECISE_SWEEP) && (hitFlags & PxHitFlag::eMTD))
+ {
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support MTD. Perform MTD with default sweep");
+ hitFlags &= ~PxHitFlag::ePRECISE_SWEEP;
+ }
+
+ if((hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP) && (hitFlags & PxHitFlag::eMTD))
+ {
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " eMTD cannot be used in conjunction with eASSUME_NO_INITIAL_OVERLAP. eASSUME_NO_INITIAL_OVERLAP will be ignored");
+ hitFlags &= ~PxHitFlag::eASSUME_NO_INITIAL_OVERLAP;
+ }
+
+ PxReal realInflation = inflation;
+ if((hitFlags & PxHitFlag::ePRECISE_SWEEP)&& inflation > 0.f)
+ {
+ realInflation = 0.f;
+ Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support inflation, inflation will be overwritten to be zero");
+ }
+ MultiQueryInput input(&geometry, &pose, unitDir, distance, realInflation);
+ return multiQuery<PxSweepHit>(input, hits, hitFlags, cache, filterData, filterCall, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//========================================================================================================================
+
+static PX_FORCE_INLINE bool applyAllPreFiltersSQ(
+ const local::ActorShape* as, PxQueryHitType::Enum& hitType, const PxQueryFlags& inFilterFlags,
+ const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
+ const NpSceneQueries& scene, BatchQueryFilterData* bfd, PxHitFlags& queryFlags, PxU32 /*maxNbTouches*/)
+{
+ if(!applyClientFilter(as->scbActor, filterData, scene))
+ return false;
+
+ // AP: the !bfd clause is here because there's no other way to pass data to BQ pre/post filter shaders
+ // For normal query the data can be passed with inherited callback instance
+ // So if for BQ SPU filter shader the user tries to pass data via FD, the equation will always cut it out
+ // AP scaffold TODO: once SPU is officially phased out we can remove the !bfd clause, fix broken UTs (that are wrong)
+ // and also remove support for filter shaders
+ if(!bfd && !applyFilterEquation(*as->scbShape, filterData.data))
+ return false;
+
+ if((inFilterFlags & PxQueryFlag::ePREFILTER) && (filterCall || bfd))
+ {
+ PxHitFlags outQueryFlags = queryFlags;
+
+ if(filterCall)
+ hitType = filterCall->preFilter(filterData.data, as->shape, as->actor, outQueryFlags);
+ else if(bfd->preFilterShader)
+ hitType = bfd->preFilterShader(
+ filterData.data, as->scbShape->getScShape().getQueryFilterData(),
+ bfd->filterShaderData, bfd->filterShaderDataSize, outQueryFlags);
+
+ // AP: at this point the callback might return eTOUCH but the touch buffer can be empty, the hit will be discarded
+ //PX_CHECK_MSG(hitType == PxQueryHitType::eTOUCH ? maxNbTouches > 0 : true,
+ // "SceneQuery: preFilter returned eTOUCH but empty touch buffer was provided, hit discarded.");
+
+ queryFlags = (queryFlags & ~PxHitFlag::eMODIFIABLE_FLAGS) | (outQueryFlags & PxHitFlag::eMODIFIABLE_FLAGS);
+ }
+ // test passed, continue to return as;
+ return true;
+}
+
+//========================================================================================================================
+// performs a single geometry query for any HitType (PxSweepHit, PxOverlapHit, PxRaycastHit)
+template<typename HitType>
+struct GeomQueryAny
+{
+ static PX_FORCE_INLINE PxU32 geomHit(
+ const NpSceneQueries& sceneQueries, const MultiQueryInput& input, const ShapeData& sd,
+ const PxGeometry& sceneGeom, const PxTransform& pose, PxHitFlags hitFlags,
+ PxU32 maxHits, HitType* hits, const PxReal shrunkMaxDistance, PxBounds3* precomputedBounds)
+ {
+ const PxGeometry& geom0 = *input.geometry;
+ const PxTransform& pose0 = *input.pose;
+ const PxGeometry& geom1 = sceneGeom;
+ const PxTransform& pose1 = pose;
+
+ // Handle raycasts
+ if(HitTypeSupport<HitType>::IsRaycast)
+ {
+ // the test for mesh AABB is archived in //sw/physx/dev/apokrovsky/graveyard/sqMeshAABBTest.cpp
+ // TODO: investigate performance impact (see US12801)
+ PX_CHECK_AND_RETURN_VAL(input.getDir().isFinite(), "PxScene::raycast(): rayDir is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL(input.getOrigin().isFinite(), "PxScene::raycast(): rayOrigin is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxScene::raycast(): pose is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL(shrunkMaxDistance >= 0.0f, "PxScene::raycast(): maxDist is negative.", 0);
+ PX_CHECK_AND_RETURN_VAL(PxIsFinite(shrunkMaxDistance), "PxScene::raycast(): maxDist is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL(PxAbs(input.getDir().magnitudeSquared()-1)<1e-4f,
+ "PxScene::raycast(): ray direction must be unit vector.", 0);
+
+ // PT: TODO: investigate perf difference
+ const RaycastFunc func = sceneQueries.mCachedRaycastFuncs[geom1.getType()];
+ return func(geom1, pose1, input.getOrigin(), input.getDir(), shrunkMaxDistance,
+ hitFlags, maxHits, reinterpret_cast<PxRaycastHit*>(hits));
+ }
+ // Handle sweeps
+ else if(HitTypeSupport<HitType>::IsSweep)
+ {
+ PX_ASSERT(precomputedBounds != NULL);
+ // b0 = query shape bounds
+ // b1 = scene shape bounds
+ // AP: Here we clip the sweep to bounds with sum of extents. This is needed for GJK stability.
+ // because sweep is equivalent to a raycast vs a scene shape with inflated bounds.
+ // This also may (or may not) provide an optimization for meshes because top level of rtree has multiple boxes
+ // and there is no bounds test for the whole mesh elsewhere
+ PxBounds3 b0 = *precomputedBounds, b1;
+ // compute the scene geometry bounds
+ Gu::computeBounds(b1, sceneGeom, pose, 0.0f, NULL, 1.0f, false);
+ const PxVec3 combExt = (b0.getExtents() + b1.getExtents())*1.01f;
+
+ PxF32 tnear, tfar;
+ if(!intersectRayAABB2(-combExt, combExt, b0.getCenter() - b1.getCenter(), input.getDir(), shrunkMaxDistance, tnear, tfar)) // returns (tnear<tfar)
+ if(tnear>tfar) // this second test is needed because shrunkMaxDistance can be 0 for 0 length sweep
+ return 0;
+ PX_ASSERT(input.getDir().isNormalized());
+ // tfar is now the t where the ray exits the AABB. input.getDir() is normalized
+
+ const PxVec3& unitDir = input.getDir();
+ PxSweepHit& sweepHit = reinterpret_cast<PxSweepHit&>(hits[0]);
+
+ // if we don't start inside the AABB box, offset the start pos, because of precision issues with large maxDist
+ const bool offsetPos = (tnear > GU_RAY_SURFACE_OFFSET);
+ const PxReal offset = offsetPos ? (tnear - GU_RAY_SURFACE_OFFSET) : 0.0f;
+ const PxVec3 offsetVec(offsetPos ? (unitDir*offset) : PxVec3(0.0f));
+ // we move the geometry we sweep against, so that we avoid the Gu::Capsule/Box recomputation
+ const PxTransform pose1Offset(pose1.p - offsetVec, pose1.q);
+
+ const PxReal distance = PxMin(tfar, shrunkMaxDistance) - offset;
+ const PxReal inflation = input.inflation;
+ PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxScene::sweep(): pose0 is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL(pose1Offset.isValid(), "PxScene::sweep(): pose1 is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxScene::sweep(): unitDir is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL(PxIsFinite(distance), "PxScene::sweep(): distance is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL((distance >= 0.0f && !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) || distance > 0.0f,
+ "PxScene::sweep(): sweep distance must be >=0 or >0 with eASSUME_NO_INITIAL_OVERLAP.", 0);
+
+ PxU32 retVal = 0;
+ const GeomSweepFuncs& sf = sceneQueries.mCachedSweepFuncs;
+ switch(geom0.getType())
+ {
+ case PxGeometryType::eSPHERE:
+ {
+ const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0);
+ // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false)
+ const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f);
+ const Capsule worldCapsule(pose0.p, pose0.p, sphereGeom.radius); // AP: precompute?
+ const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP;
+ const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()];
+ retVal = PxU32(func(geom1, pose1Offset, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation));
+ }
+ break;
+
+ case PxGeometryType::eCAPSULE:
+ {
+ const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP;
+ const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()];
+ retVal = PxU32(func(geom1, pose1Offset, static_cast<const PxCapsuleGeometry&>(geom0), pose0, sd.getGuCapsule(), unitDir, distance, sweepHit, hitFlags, inflation));
+ }
+ break;
+
+ case PxGeometryType::eBOX:
+ {
+ const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP;
+ const SweepBoxFunc func = precise ? sf.preciseBoxMap[geom1.getType()] : sf.boxMap[geom1.getType()];
+ retVal = PxU32(func(geom1, pose1Offset, static_cast<const PxBoxGeometry&>(geom0), pose0, sd.getGuBox(), unitDir, distance, sweepHit, hitFlags, inflation));
+ }
+ break;
+
+ case PxGeometryType::eCONVEXMESH:
+ {
+ const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0);
+ const SweepConvexFunc func = sf.convexMap[geom1.getType()];
+ retVal = PxU32(func(geom1, pose1Offset, convexGeom, pose0, unitDir, distance, sweepHit, hitFlags, inflation));
+ }
+ break;
+ case PxGeometryType::ePLANE:
+ case PxGeometryType::eTRIANGLEMESH:
+ case PxGeometryType::eHEIGHTFIELD:
+ case PxGeometryType::eGEOMETRY_COUNT:
+ case PxGeometryType::eINVALID:
+ physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__,
+ "PxScene::sweep(): first geometry object parameter must be sphere, capsule, box or convex geometry.");
+ break;
+ }
+ if (retVal)
+ {
+ // we need to offset the distance back
+ sweepHit.distance += offset;
+ // we need to offset the hit position back as we moved the geometry we sweep against
+ sweepHit.position += offsetVec;
+ }
+ return retVal;
+ }
+ // Handle overlaps
+ else if(HitTypeSupport<HitType>::IsOverlap)
+ {
+ const GeomOverlapTable* overlapFuncs = sceneQueries.mCachedOverlapFuncs;
+ return PxU32(Gu::overlap(geom0, pose0, geom1, pose1, overlapFuncs));
+ }
+ else
+ {
+ PX_ALWAYS_ASSERT_MESSAGE("Unexpected template expansion in GeomQueryAny::geomHit");
+ return 0;
+ }
+ }
+};
+
+// struct to access protected data members in the public PxHitCallback API
+template<typename HitType>
+struct MultiQueryCallback : public PrunerCallback
+{
+ const NpSceneQueries& mScene;
+ const MultiQueryInput& mInput;
+ PxHitCallback<HitType>& mHitCall;
+ const PxHitFlags mHitFlags;
+ const PxQueryFilterData& mFilterData;
+ PxQueryFilterCallback* mFilterCall;
+ PxReal mShrunkDistance;
+ BatchQueryFilterData* mBfd; // only not NULL for batch queries
+ const PxHitFlags mMeshAnyHitFlags;
+ bool mReportTouchesAgain;
+ bool mFarBlockFound; // this is to prevent repeated searches for far block
+ bool mNoBlock;
+ const bool mAnyHit;
+ bool mIsCached; // is this call coming as a callback from the pruner or a single item cached callback?
+
+ // The reason we need these bounds is because we need to know combined(inflated shape) bounds to clip the sweep path
+ // to be tolerable by GJK precision issues. This test is done for (queryShape vs touchedShapes)
+ // So it makes sense to cache the bounds for sweep query shape, otherwise we'd have to recompute them every time
+ // Currently only used for sweeps.
+ PxBounds3 mQueryShapeBounds;
+ bool mQueryShapeBoundsValid;
+ const ShapeData* mShapeData;
+
+ MultiQueryCallback(
+ const NpSceneQueries& scene, const MultiQueryInput& input, bool anyHit, PxHitCallback<HitType>& hitCall, PxHitFlags hitFlags,
+ const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, PxReal shrunkDistance, BatchQueryFilterData* aBfd) :
+ mScene (scene),
+ mInput (input),
+ mHitCall (hitCall),
+ mHitFlags (hitFlags),
+ mFilterData (filterData),
+ mFilterCall (filterCall),
+ mShrunkDistance (shrunkDistance),
+ mBfd (aBfd),
+ mMeshAnyHitFlags ((hitFlags.isSet(PxHitFlag::eMESH_ANY) || anyHit) ? PxHitFlag::eMESH_ANY : PxHitFlag::eDISTANCE),
+ mReportTouchesAgain (true),
+ mFarBlockFound (filterData.flags & PxQueryFlag::eNO_BLOCK),
+ mNoBlock (filterData.flags & PxQueryFlag::eNO_BLOCK),
+ mAnyHit (anyHit),
+ mIsCached (false),
+ mQueryShapeBoundsValid (false),
+ mShapeData (NULL)
+ {
+ }
+
+ virtual PxAgain invoke(PxReal& aDist, const PrunerPayload& aPayload)
+ {
+ const PxU32 tempCount = 1;
+ HitType tempBuf[tempCount];
+
+ // PT: TODO: do we need actorShape.actor/actorShape.shape immediately?
+ local::ActorShape actorShape;
+ local::populate(aPayload, actorShape);
+
+ const PxQueryFlags filterFlags = mFilterData.flags;
+
+ // for no filter callback, default to eTOUCH for MULTIPLE, eBLOCK otherwise
+ // also always treat as eBLOCK if currently tested shape is cached
+ // Using eRESERVED flag as a special condition to default to eTOUCH hits while only looking for a single blocking hit
+ // from a nested query (see other comments containing #LABEL1)
+ PxQueryHitType::Enum shapeHitType =
+ ((mHitCall.maxNbTouches || (mFilterData.flags & PxQueryFlag::eRESERVED)) && !mIsCached)
+ ? PxQueryHitType::eTOUCH
+ : PxQueryHitType::eBLOCK;
+
+ // apply pre-filter
+ PxHitFlags filteredHitFlags = mHitFlags;
+ if(!mIsCached) // don't run filters on single item cache
+ if(!applyAllPreFiltersSQ(&actorShape, shapeHitType/*in&out*/, filterFlags, mFilterData, mFilterCall,
+ mScene, mBfd, filteredHitFlags, mHitCall.maxNbTouches))
+ return true; // skip this shape from reporting if prefilter said to do so
+ if(shapeHitType == PxQueryHitType::eNONE)
+ return true;
+
+ PX_ASSERT(actorShape.actor && actorShape.shape);
+ const Scb::Shape* shape = actorShape.scbShape;
+ const Scb::Actor* actor = actorShape.scbActor;
+
+ // compute the global pose for the cached shape and actor
+ PX_ALIGN(16, PxTransform) globalPose;
+ NpActor::getGlobalPose(globalPose, *shape, *actor);
+
+ const PxGeometry& shapeGeom = shape->getGeometry();
+
+ // Here we decide whether to use the user provided buffer in place or a local stack buffer
+ // see if we have more room left in the callback results buffer than in the parent stack buffer
+ // if so get subHits in-place in the hit buffer instead of the parent stack buffer
+ // nbTouches is the number of accumulated touch hits so far
+ // maxNbTouches is the size of the user buffer
+ PxU32 maxSubHits1 = mHitCall.maxNbTouches - mHitCall.nbTouches; // how much room is left in the user buffer
+ HitType* subHits1 = mHitCall.touches + mHitCall.nbTouches; // pointer to the first free hit in the user buffer
+ if(mHitCall.nbTouches >= mHitCall.maxNbTouches)
+ // if there's no room left in the user buffer, use a stack buffer
+ {
+ // tried using 64 here - causes check stack code to get generated on xbox, perhaps because of guard page
+ // need this buffer in case the input buffer is full but we still want to correctly merge results from later hits
+ maxSubHits1 = tempCount;
+ subHits1 = reinterpret_cast<HitType*>(tempBuf);
+ }
+
+ // limit number of hits to 1 for meshes if eMESH_MULTIPLE wasn't specified. this tells geomQuery to only look for a closest hit
+ if(shapeGeom.getType() == PxGeometryType::eTRIANGLEMESH && !(filteredHitFlags & PxHitFlag::eMESH_MULTIPLE))
+ maxSubHits1 = 1; // required to only receive 1 hit to pass UTs
+ // call the geometry specific intersection template
+ PxU32 nbSubHits = GeomQueryAny<HitType>::geomHit(
+ mScene, mInput, *mShapeData, shapeGeom, globalPose,
+ filteredHitFlags | mMeshAnyHitFlags,
+ maxSubHits1, subHits1, mShrunkDistance, mQueryShapeBoundsValid ? &mQueryShapeBounds : NULL);
+
+ // ------------------------- iterate over geometry subhits -----------------------------------
+ for (PxU32 iSubHit = 0; iSubHit < nbSubHits; iSubHit++)
+ {
+ HitType& hit = subHits1[iSubHit];
+ hit.actor = actorShape.actor;
+ hit.shape = actorShape.shape;
+
+ // some additional processing only for sweep hits with initial overlap
+ if(HitTypeSupport<HitType>::IsSweep && HITDIST(hit) == 0.0f && !(filteredHitFlags & PxHitFlag::eMTD))
+ // PT: necessary as some leaf routines are called with reversed params, thus writing +unitDir there.
+ // AP: apparently still necessary to also do in Gu because Gu can be used standalone (without SQ)
+ reinterpret_cast<PxSweepHit&>(hit).normal = -mInput.getDir();
+
+ // start out with hitType for this cached shape set to a pre-filtered hit type
+ PxQueryHitType::Enum hitType = shapeHitType;
+
+ // run the post-filter if specified in filterFlags and filterCall is non-NULL
+ if(!mIsCached && (mFilterCall || mBfd) && (filterFlags & PxQueryFlag::ePOSTFILTER))
+ {
+ if(mFilterCall)
+ hitType = mFilterCall->postFilter(mFilterData.data, hit);
+ else if(mBfd->postFilterShader)
+ hitType = mBfd->postFilterShader(
+ mFilterData.data, actorShape.scbShape->getScShape().getQueryFilterData(),
+ mBfd->filterShaderData, mBfd->filterShaderDataSize, hit);
+ }
+
+ // early out on any hit if eANY_HIT was specified, regardless of hit type
+ if(mAnyHit && hitType != PxQueryHitType::eNONE)
+ {
+ // block or touch qualifies for qType=ANY type hit => return it as blocking according to spec. Ignore eNONE.
+ mHitCall.block = hit;
+ mHitCall.hasBlock = true;
+ return false; // found a hit for ANY qType, can early exit now
+ }
+
+ if(mNoBlock)
+ hitType = PxQueryHitType::eTOUCH;
+
+ PX_WARN_ONCE_IF(HitTypeSupport<HitType>::IsOverlap && hitType == PxQueryHitType::eBLOCK,
+ "eBLOCK returned from user filter for overlap() query. This may cause undesired behavior. "
+ "Consider using PxQueryFlag::eNO_BLOCK for overlap queries.");
+
+ if(hitType == PxQueryHitType::eTOUCH)
+ {
+ // -------------------------- handle eTOUCH hits ---------------------------------
+ // for qType=multiple, store the hit. For other qTypes ignore it.
+ // <= is important for initially overlapping sweeps
+ #if PX_CHECKED
+ if(mHitCall.maxNbTouches == 0 && !mBfd && !mFilterData.flags.isSet(PxQueryFlag::eRESERVED))
+ // issue a warning if eTOUCH was returned by the prefilter, we have 0 touch buffer and not a batch query
+ // not doing for BQ because the touches buffer can be overflown and thats ok by spec
+ // eRESERVED to avoid a warning from nested callback (closest blocking hit recursive search)
+ Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__,
+ "User filter returned PxQueryHitType::eTOUCH but the touches buffer was empty. Hit was discarded.");
+ #endif
+
+ if(mHitCall.maxNbTouches && mReportTouchesAgain && HITDIST(hit) <= mShrunkDistance)
+ {
+ // Buffer full: need to find the closest blocking hit, clip touch hits and flush the buffer
+ if(mHitCall.nbTouches == mHitCall.maxNbTouches)
+ {
+ // issue a second nested query just looking for the closest blocking hit
+ // could do better perf-wise by saving traversal state (start looking for blocking from this point)
+ // but this is not a perf critical case because users can provide a bigger buffer
+ // that covers non-degenerate cases
+ // far block search doesn't apply to overlaps because overlaps don't work with blocking hits
+ if(HitTypeSupport<HitType>::IsOverlap == 0)
+ {
+ // AP: the use of eRESERVED is a bit tricky, see other comments containing #LABEL1
+ PxQueryFilterData fd1 = mFilterData; fd1.flags |= PxQueryFlag::eRESERVED;
+ PxHitBuffer<HitType> buf1; // create a temp callback buffer for a single blocking hit
+ if(!mFarBlockFound && mHitCall.maxNbTouches > 0 && mScene.NpSceneQueries::multiQuery<HitType>(
+ mInput, buf1, mHitFlags, NULL, fd1, mFilterCall, mBfd))
+ {
+ mHitCall.block = buf1.block;
+ mHitCall.hasBlock = true;
+ mHitCall.nbTouches =
+ clipHitsToNewMaxDist<HitType>(mHitCall.touches, mHitCall.nbTouches, HITDIST(buf1.block));
+ mShrunkDistance = HITDIST(buf1.block);
+ aDist = mShrunkDistance;
+ }
+ mFarBlockFound = true;
+ }
+ if(mHitCall.nbTouches == mHitCall.maxNbTouches)
+ {
+ mReportTouchesAgain = mHitCall.processTouches(mHitCall.touches, mHitCall.nbTouches);
+ if(!mReportTouchesAgain)
+ return false; // optimization - buffer is full
+ else
+ mHitCall.nbTouches = 0; // reset nbTouches so we can continue accumulating again
+ }
+ }
+
+ //if(hitCall.nbTouches < hitCall.maxNbTouches) // can be true if maxNbTouches is 0
+ mHitCall.touches[mHitCall.nbTouches++] = hit;
+ } // if(hitCall.maxNbTouches && reportTouchesAgain && HITDIST(hit) <= shrunkDistance)
+ } // if(hitType == PxQueryHitType::eTOUCH)
+ else if(hitType == PxQueryHitType::eBLOCK)
+ {
+ // -------------------------- handle eBLOCK hits ----------------------------------
+ // only eBLOCK qualifies as a closest hit candidate => compare against best distance and store
+ // <= is needed for eTOUCH hits to be recorded correctly vs same eBLOCK distance for overlaps
+ if(HITDIST(hit) <= mShrunkDistance)
+ {
+ if(HitTypeSupport<HitType>::IsOverlap == 0)
+ {
+ mShrunkDistance = HITDIST(hit);
+ aDist = mShrunkDistance;
+ }
+ mHitCall.block = hit;
+ mHitCall.hasBlock = true;
+ }
+ } // if(hitType == eBLOCK)
+ else {
+ PX_ASSERT(hitType == PxQueryHitType::eNONE);
+ }
+ } // for iSubHit
+ return true;
+ }
+
+private:
+ MultiQueryCallback<HitType>& operator=(const MultiQueryCallback<HitType>&);
+};
+
+//========================================================================================================================
+#if PX_SUPPORT_PVD
+template<typename HitType>
+struct CapturePvdOnReturn : public PxHitCallback<HitType>
+{
+ // copy the arguments of multiQuery into a struct, this is strictly for PVD recording
+ const NpSceneQueries* mSQ;
+ const MultiQueryInput& mInput;
+ PxHitFlags mHitFlags; // PT: TODO: this is not used!
+ const PxQueryCache* mCache; // PT: TODO: this is not used!
+ const PxQueryFilterData& mFilterData;
+ PxQueryFilterCallback* mFilterCall; // PT: TODO: this is not used!
+ BatchQueryFilterData* mBFD; // PT: TODO: check if this is sometimes not NULL
+ Ps::Array<HitType> mAllHits;
+ PxHitCallback<HitType>& mParentCallback;
+
+ CapturePvdOnReturn(
+ const NpSceneQueries* sq, const MultiQueryInput& input, PxHitFlags hitFlags,
+ const PxQueryCache* cache, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
+ BatchQueryFilterData* bfd, PxHitCallback<HitType>& parentCallback) :
+ PxHitCallback<HitType> (parentCallback.touches, parentCallback.maxNbTouches),
+ mSQ (sq),
+ mInput (input),
+ mHitFlags (hitFlags),
+ mCache (cache),
+ mFilterData (filterData),
+ mFilterCall (filterCall),
+ mBFD (bfd),
+ mParentCallback (parentCallback)
+ {}
+
+ virtual PxAgain processTouches(const HitType* hits, PxU32 nbHits)
+ {
+ const PxAgain again = mParentCallback.processTouches(hits, nbHits);
+ for(PxU32 i=0; i<nbHits; i++)
+ mAllHits.pushBack(hits[i]);
+ return again;
+ }
+
+ ~CapturePvdOnReturn()
+ {
+ const physx::Vd::ScbScenePvdClient& pvdClient = mSQ->getScene().getScenePvdClient();
+ if(!(pvdClient.isConnected() && (pvdClient.getScenePvdFlags() & PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES)))
+ return;
+
+ physx::Vd::PvdSceneQueryCollector& collector = mBFD ? mSQ->getBatchedSqCollector() : mSQ->getSingleSqCollector();
+
+ if(mParentCallback.nbTouches)
+ {
+ for(PxU32 i = 0; i < mParentCallback.nbTouches; i++)
+ mAllHits.pushBack(mParentCallback.touches[i]);
+ }
+
+ if(mParentCallback.hasBlock)
+ mAllHits.pushBack(mParentCallback.block);
+
+ // PT: TODO: why do we need reinterpret_casts below?
+ if(HitTypeSupport<HitType>::IsRaycast)
+ collector.raycast (mInput.getOrigin(), mInput.getDir(), mInput.maxDistance, reinterpret_cast<PxRaycastHit*>(mAllHits.begin()), mAllHits.size(), mFilterData, this->maxNbTouches!=0);
+ else if(HitTypeSupport<HitType>::IsOverlap)
+ collector.overlapMultiple (*mInput.geometry, *mInput.pose, reinterpret_cast<PxOverlapHit*>(mAllHits.begin()), mAllHits.size(), mFilterData);
+ else if(HitTypeSupport<HitType>::IsSweep)
+ collector.sweep (*mInput.geometry, *mInput.pose, mInput.getDir(), mInput.maxDistance, reinterpret_cast<PxSweepHit*>(mAllHits.begin()), mAllHits.size(), mFilterData, this->maxNbTouches!=0);
+ }
+
+private:
+ CapturePvdOnReturn<HitType>& operator=(const CapturePvdOnReturn<HitType>&);
+};
+#endif // PX_SUPPORT_PVD
+
+//========================================================================================================================
+template<typename HitType>
+struct IssueCallbacksOnReturn
+{
+ PxHitCallback<HitType>& hits;
+ PxAgain again; // query was stopped by previous processTouches. This means that nbTouches is still non-zero
+ // but we don't need to issue processTouches again
+ PX_FORCE_INLINE IssueCallbacksOnReturn(PxHitCallback<HitType>& aHits) : hits(aHits)
+ {
+ again = true;
+ }
+
+ ~IssueCallbacksOnReturn()
+ {
+ if(again)
+ // only issue processTouches if query wasn't stopped
+ // this is because nbTouches doesn't get reset to 0 in this case (according to spec)
+ // and the touches in touches array were already processed by the callback
+ {
+ if(hits.hasBlock && hits.nbTouches)
+ hits.nbTouches = clipHitsToNewMaxDist<HitType>(hits.touches, hits.nbTouches, HITDIST(hits.block));
+ if(hits.nbTouches)
+ {
+ bool again_ = hits.processTouches(hits.touches, hits.nbTouches);
+ if(again_)
+ hits.nbTouches = 0;
+ }
+ }
+ hits.finalizeQuery();
+ }
+
+private:
+ IssueCallbacksOnReturn<HitType>& operator=(const IssueCallbacksOnReturn<HitType>&);
+};
+
+#undef HITDIST
+
+//========================================================================================================================
+template<typename HitType>
+bool NpSceneQueries::multiQuery(
+ const MultiQueryInput& input, PxHitCallback<HitType>& hits, PxHitFlags hitFlags, const PxQueryCache* cache,
+ const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, BatchQueryFilterData* bfd) const
+{
+ const bool anyHit = (filterData.flags & PxQueryFlag::eANY_HIT) == PxQueryFlag::eANY_HIT;
+
+ PxI32 retval = 0; PX_UNUSED(retval);
+
+ if(HitTypeSupport<HitType>::IsRaycast == 0)
+ {
+ PX_CHECK_AND_RETURN_VAL(input.pose != NULL, "NpSceneQueries::overlap/sweep pose is NULL.", 0);
+ PX_CHECK_AND_RETURN_VAL(input.pose->isValid(), "NpSceneQueries::overlap/sweep pose is not valid.", 0);
+ }
+ else
+ {
+ PX_CHECK_AND_RETURN_VAL(input.getOrigin().isFinite(), "NpSceneQueries::raycast pose is not valid.", 0);
+ }
+
+ if(HitTypeSupport<HitType>::IsOverlap == 0)
+ {
+ PX_CHECK_AND_RETURN_VAL(input.getDir().isFinite(), "NpSceneQueries multiQuery input check: unitDir is not valid.", 0);
+ PX_CHECK_AND_RETURN_VAL(input.getDir().isNormalized(), "NpSceneQueries multiQuery input check: direction must be normalized", 0);
+ }
+
+ if(HitTypeSupport<HitType>::IsRaycast)
+ {
+ PX_CHECK_AND_RETURN_VAL(input.maxDistance > 0.0f, "NpSceneQueries::multiQuery input check: distance cannot be negative or zero", 0);
+ }
+
+ if(HitTypeSupport<HitType>::IsOverlap && !anyHit)
+ {
+ PX_CHECK_AND_RETURN_VAL(hits.maxNbTouches > 0, "PxScene::overlap() and PxBatchQuery::overlap() calls without eANY_HIT flag require a touch hit buffer for return results.", 0);
+ }
+
+ if(HitTypeSupport<HitType>::IsSweep)
+ {
+ PX_CHECK_AND_RETURN_VAL(input.maxDistance >= 0.0f, "NpSceneQueries multiQuery input check: distance cannot be negative", 0);
+ PX_CHECK_AND_RETURN_VAL(input.maxDistance != 0.0f || !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP),
+ "NpSceneQueries multiQuery input check: zero-length sweep only valid without the PxHitFlag::eASSUME_NO_INITIAL_OVERLAP flag", 0);
+ }
+
+ PX_CHECK_MSG(!cache || (cache && cache->shape && cache->actor), "Raycast cache specified but shape or actor pointer is NULL!");
+ const PrunerData cacheData = cache ? NpActor::getShapeManager(*cache->actor)->findSceneQueryData(*static_cast<NpShape*>(cache->shape)) : SQ_INVALID_PRUNER_DATA;
+
+ // this function is logically const for the SDK user, as flushUpdates() will not have an API-visible effect on this object
+ // internally however, flushUpdates() changes the states of the Pruners in mSQManager
+ // because here is the only place we need this, const_cast instead of making SQM mutable
+ const_cast<NpSceneQueries*>(this)->mSQManager.flushUpdates();
+
+#if PX_SUPPORT_PVD
+ CapturePvdOnReturn<HitType> pvdCapture(this, input, hitFlags, cache, filterData, filterCall, bfd, hits);
+#endif
+
+ IssueCallbacksOnReturn<HitType> cbr(hits); // destructor will execute callbacks on return from this function
+ hits.hasBlock = false;
+ hits.nbTouches = 0;
+
+ PxReal shrunkDistance = HitTypeSupport<HitType>::IsOverlap ? PX_MAX_REAL : input.maxDistance; // can be progressively shrunk as we go over the list of shapes
+ if(HitTypeSupport<HitType>::IsSweep)
+ shrunkDistance = PxMin(shrunkDistance, PX_MAX_SWEEP_DISTANCE);
+ MultiQueryCallback<HitType> pcb(*this, input, anyHit, hits, hitFlags, filterData, filterCall, shrunkDistance, bfd);
+
+ if(cacheData!=SQ_INVALID_PRUNER_DATA && hits.maxNbTouches == 0) // don't use cache for queries that can return touch hits
+ {
+ // this block is only executed for single shape cache
+ const PrunerPayload& cachedPayload = mSQManager.getPayload(cacheData);
+ pcb.mIsCached = true;
+ PxReal dummyDist;
+
+ PxAgain againAfterCache;
+ if(HitTypeSupport<HitType>::IsSweep)
+ {
+ // AP: for sweeps we cache the bounds because we need to know them for the test to clip the sweep to bounds
+ // otherwise GJK becomes unstable. The bounds can be used multiple times so this is an optimization.
+ const ShapeData sd(*input.geometry, *input.pose, input.inflation);
+ pcb.mQueryShapeBounds = sd.getPrunerInflatedWorldAABB();
+ pcb.mQueryShapeBoundsValid = true;
+ pcb.mShapeData = &sd;
+ againAfterCache = pcb.invoke(dummyDist, cachedPayload);
+ pcb.mShapeData = NULL;
+ } else
+ againAfterCache = pcb.invoke(dummyDist, cachedPayload);
+ pcb.mIsCached = false;
+ if(!againAfterCache) // if PxAgain result for cached shape was false (abort query), return here
+ return hits.hasAnyHits();
+ }
+
+ const Pruner* staticPruner = mSQManager.get(PruningIndex::eSTATIC).pruner();
+ const Pruner* dynamicPruner = mSQManager.get(PruningIndex::eDYNAMIC).pruner();
+
+ const PxU32 doStatics = filterData.flags & PxQueryFlag::eSTATIC;
+ const PxU32 doDynamics = filterData.flags & PxQueryFlag::eDYNAMIC;
+
+ if(HitTypeSupport<HitType>::IsRaycast)
+ {
+ bool again = doStatics ? staticPruner->raycast(input.getOrigin(), input.getDir(), pcb.mShrunkDistance, pcb) : true;
+ if(!again)
+ return hits.hasAnyHits();
+
+ if(doDynamics)
+ again = dynamicPruner->raycast(input.getOrigin(), input.getDir(), pcb.mShrunkDistance, pcb);
+
+ cbr.again = again; // update the status to avoid duplicate processTouches()
+ return hits.hasAnyHits();
+ }
+ else if(HitTypeSupport<HitType>::IsOverlap)
+ {
+ PX_ASSERT(input.geometry);
+
+ const ShapeData sd(*input.geometry, *input.pose, input.inflation);
+ pcb.mShapeData = &sd;
+ PxAgain again = doStatics ? staticPruner->overlap(sd, pcb) : true;
+ if(!again) // && (filterData.flags & PxQueryFlag::eANY_HIT))
+ return hits.hasAnyHits();
+
+ if(doDynamics)
+ again = dynamicPruner->overlap(sd, pcb);
+
+ cbr.again = again; // update the status to avoid duplicate processTouches()
+ return hits.hasAnyHits();
+ }
+ else
+ {
+ PX_ASSERT(HitTypeSupport<HitType>::IsSweep);
+ PX_ASSERT(input.geometry);
+
+ const ShapeData sd(*input.geometry, *input.pose, input.inflation);
+ pcb.mQueryShapeBounds = sd.getPrunerInflatedWorldAABB();
+ pcb.mQueryShapeBoundsValid = true;
+ pcb.mShapeData = &sd;
+ PxAgain again = doStatics ? staticPruner->sweep(sd, input.getDir(), pcb.mShrunkDistance, pcb) : true;
+ if(!again)
+ return hits.hasAnyHits();
+
+ if(doDynamics)
+ again = dynamicPruner->sweep(sd, input.getDir(), pcb.mShrunkDistance, pcb);
+
+ cbr.again = again; // update the status to avoid duplicate processTouches()
+ return hits.hasAnyHits();
+ }
+}
+
+
+// explicit instantiations for multiQuery to fix link errors on android
+#if !PX_WINDOWS_FAMILY
+#define TMQ(hittype) \
+ template bool NpSceneQueries::multiQuery<hittype>( \
+ const MultiQueryInput& input, PxHitCallback<hittype>& hits, PxHitFlags hitFlags, \
+ const PxQueryCache* cache, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, \
+ BatchQueryFilterData* bfd) const;
+
+TMQ(PxRaycastHit)
+TMQ(PxOverlapHit)
+TMQ(PxSweepHit)
+
+#undef TMQ
+#endif