// 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-2025 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. // **************************************************************************** // This snippet demonstrates all queries supported by the low-level query system. // Please get yourself familiar with SnippetStandaloneQuerySystem first. // **************************************************************************** #include #include "PxPhysicsAPI.h" #include "GuQuerySystem.h" #include "GuFactory.h" #include "foundation/PxArray.h" #include "../snippetcommon/SnippetPrint.h" #include "../snippetcommon/SnippetPVD.h" #include "../snippetutils/SnippetUtils.h" #ifdef RENDER_SNIPPET #include "../snippetrender/SnippetCamera.h" #include "../snippetrender/SnippetRender.h" #endif using namespace physx; using namespace Gu; #ifdef RENDER_SNIPPET using namespace Snippets; #endif static PxDefaultAllocator gAllocator; static PxDefaultErrorCallback gErrorCallback; static PxFoundation* gFoundation = NULL; static PxConvexMesh* gConvexMesh = NULL; static PxTriangleMesh* gTriangleMesh = NULL; static const bool gManualBoundsComputation = false; static const bool gUseDelayedUpdates = true; static const float gBoundsInflation = 0.001f; static bool gPause = false; static bool gOneFrame = false; enum QueryScenario { RAYCAST_CLOSEST, RAYCAST_ANY, RAYCAST_MULTIPLE, SWEEP_CLOSEST, SWEEP_ANY, SWEEP_MULTIPLE, OVERLAP_ANY, OVERLAP_MULTIPLE, NB_SCENES }; static QueryScenario gSceneIndex = RAYCAST_CLOSEST; // Tweak number of created objects per scene static const PxU32 gFactor[NB_SCENES] = { 2, 1, 2, 2, 1, 2, 1, 2 }; // Tweak amplitude of created objects per scene static const float gAmplitude[NB_SCENES] = { 4.0f, 4.0f, 4.0f, 4.0f, 8.0f, 4.0f, 4.0f, 4.0f }; static const PxVec3 gCamPos[NB_SCENES] = { PxVec3(-2.199769f, 3.448516f, 10.943871f), PxVec3(-2.199769f, 3.448516f, 10.943871f), PxVec3(-2.199769f, 3.448516f, 10.943871f), PxVec3(-2.199769f, 3.448516f, 10.943871f), PxVec3(-3.404853f, 4.865191f, 17.692263f), PxVec3(-2.199769f, 3.448516f, 10.943871f), PxVec3(-2.199769f, 3.448516f, 10.943871f), PxVec3(-2.199769f, 3.448516f, 10.943871f), }; static const PxVec3 gCamDir[NB_SCENES] = { PxVec3(0.172155f, -0.202382f, -0.964056f), PxVec3(0.172155f, -0.202382f, -0.964056f), PxVec3(0.172155f, -0.202382f, -0.964056f), PxVec3(0.172155f, -0.202382f, -0.964056f), PxVec3(0.172155f, -0.202382f, -0.964056f), PxVec3(0.172155f, -0.202382f, -0.964056f), PxVec3(0.172155f, -0.202382f, -0.964056f), PxVec3(0.172155f, -0.202382f, -0.964056f), }; #define MAX_NB_OBJECTS 32 /////////////////////////////////////////////////////////////////////////////// // The following functions determine how we use the pruner payloads in this snippet static PX_FORCE_INLINE void setupPayload(PrunerPayload& payload, PxU32 objectIndex, const PxGeometryHolder* gh) { payload.data[0] = objectIndex; payload.data[1] = size_t(gh); } static PX_FORCE_INLINE PxU32 getObjectIndexFromPayload(const PrunerPayload& payload) { return PxU32(payload.data[0]); } static PX_FORCE_INLINE const PxGeometry& getGeometryFromPayload(const PrunerPayload& payload) { const PxGeometryHolder* gh = reinterpret_cast(payload.data[1]); return gh->any(); } /////////////////////////////////////////////////////////////////////////////// namespace { struct CustomRaycastHit : PxGeomRaycastHit { PxU32 mObjectIndex; }; struct CustomSweepHit : PxGeomSweepHit { PxU32 mObjectIndex; }; class CustomScene : public Adapter { public: CustomScene(); ~CustomScene() {} // Adapter virtual const PxGeometry& getGeometry(const PrunerPayload& payload) const; //~Adapter void release(); void addGeom(const PxGeometry& geom, const PxTransform& pose, bool isDynamic); void render(); void updateObjects(); void runQueries(); bool raycastClosest(const PxVec3& origin, const PxVec3& unitDir, float maxDist, CustomRaycastHit& hit) const; bool raycastAny(const PxVec3& origin, const PxVec3& unitDir, float maxDist) const; bool raycastMultiple(const PxVec3& origin, const PxVec3& unitDir, float maxDist, PxArray& hits) const; bool sweepClosest(const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist, CustomSweepHit& hit) const; bool sweepAny(const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist) const; bool sweepMultiple(const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist, PxArray& hits) const; bool overlapAny(const PxGeometry& geom, const PxTransform& pose) const; bool overlapMultiple(const PxGeometry& geom, const PxTransform& pose, PxArray& hits) const; struct Object { PxGeometryHolder mGeom; ActorShapeData mData; PxVec3 mTouchedColor; bool mTouched; }; PxU32 mNbObjects; Object mObjects[MAX_NB_OBJECTS]; QuerySystem* mQuerySystem; PxU32 mPrunerIndex; }; const PxGeometry& CustomScene::getGeometry(const PrunerPayload& payload) const { PX_ASSERT(!gManualBoundsComputation); return getGeometryFromPayload(payload); } void CustomScene::release() { PX_DELETE(mQuerySystem); PX_DELETE_THIS; } CustomScene::CustomScene() : mNbObjects(0) { const PxU64 contextID = PxU64(this); mQuerySystem = PX_NEW(QuerySystem)(contextID, gBoundsInflation, *this); Pruner* pruner = createAABBPruner(contextID, true, COMPANION_PRUNER_INCREMENTAL, BVH_SPLATTER_POINTS, 4); mPrunerIndex = mQuerySystem->addPruner(pruner, 0); const PxU32 nb = gFactor[gSceneIndex]; for(PxU32 i=0;isetPose(gCamPos[gSceneIndex], gCamDir[gSceneIndex]); #endif } void CustomScene::addGeom(const PxGeometry& geom, const PxTransform& pose, bool isDynamic) { PX_ASSERT(mQuerySystem); Object& obj = mObjects[mNbObjects]; obj.mGeom.storeAny(geom); PrunerPayload payload; setupPayload(payload, mNbObjects, &obj.mGeom); if(gManualBoundsComputation) { PxBounds3 bounds; PxGeometryQuery::computeGeomBounds(bounds, geom, pose, 0.0f, 1.0f + gBoundsInflation); obj.mData = mQuerySystem->addPrunerShape(payload, mPrunerIndex, isDynamic, pose, &bounds); } else { obj.mData = mQuerySystem->addPrunerShape(payload, mPrunerIndex, isDynamic, pose, NULL); } mNbObjects++; } void CustomScene::updateObjects() { if(!mQuerySystem) return; if(gPause && !gOneFrame) { mQuerySystem->update(true, true); return; } gOneFrame = false; static float time = 0.0f; time += 0.005f; const PxU32 nbObjects = mNbObjects; for(PxU32 i=0;iupdatePrunerShape(obj.mData, !gUseDelayedUpdates, pose, &bounds); } else { mQuerySystem->updatePrunerShape(obj.mData, !gUseDelayedUpdates, pose, NULL); } } mQuerySystem->update(true, true); } namespace { struct CustomPrunerFilterCallback : public PrunerFilterCallback { virtual const PxGeometry* validatePayload(const PrunerPayload& payload, PxHitFlags& /*hitFlags*/) { return &getGeometryFromPayload(payload); } }; } static CustomPrunerFilterCallback gFilterCallback; static CachedFuncs gCachedFuncs; /////////////////////////////////////////////////////////////////////////////// // Custom queries. It is not mandatory to use e.g. different raycast queries for different usages - one could just use the same code // for everything. But it is -possible- to do so, the system is flexible enough to let users decide what to do. // Common raycast usage, returns the closest touched object (single hit). bool CustomScene::raycastClosest(const PxVec3& origin, const PxVec3& unitDir, float maxDist, CustomRaycastHit& hit) const { DefaultPrunerRaycastClosestCallback CB(gFilterCallback, gCachedFuncs.mCachedRaycastFuncs, origin, unitDir, maxDist, PxHitFlag::eDEFAULT); mQuerySystem->raycast(origin, unitDir, maxDist, CB, NULL); if(CB.mFoundHit) { static_cast(hit) = CB.mClosestHit; hit.mObjectIndex = getObjectIndexFromPayload(CB.mClosestPayload); } return CB.mFoundHit; } /////////////////////////////////////////////////////////////////////////////// // "Shadow feeler" usage, returns boolean result, early exits as soon as an impact is found. bool CustomScene::raycastAny(const PxVec3& origin, const PxVec3& unitDir, float maxDist) const { DefaultPrunerRaycastAnyCallback CB(gFilterCallback, gCachedFuncs.mCachedRaycastFuncs, origin, unitDir, maxDist); mQuerySystem->raycast(origin, unitDir, maxDist, CB, NULL); return CB.mFoundHit; } /////////////////////////////////////////////////////////////////////////////// namespace { // Beware, there is something a bit subtle here. The query can return multiple hits per query and/or // multiple hits per object. For example a raycast against a scene can touch multiple simple objects, // like e.g. multiple spheres, and return the set of touched spheres to users. But a single raycast // against a complex object like a mesh can also return multiple hits, against multiple triangles of // that same mesh. There are use cases for all of these, and it is up to users to decide what they // need. In this example we do "multiple hits per query", but a single hit per mesh. That is, we do // not use PxHitFlag::eMESH_MULTIPLE. That is why our CustomRaycastCallback only has a single // PxGeomRaycastHit member (mLocalHit). struct CustomRaycastMultipleCallback : public DefaultPrunerRaycastCallback { // This mandatory local hit structure is where DefaultPrunerRaycastCallback writes its // temporary hit when traversing the pruners. PxGeomRaycastHit mLocalHit; // This is the user-provided array where we'll write our results. PxArray& mHits; CustomRaycastMultipleCallback(PxArray& hits, PrunerFilterCallback& filterCB, const GeomRaycastTable& funcs, const PxVec3& origin, const PxVec3& dir, float distance) : DefaultPrunerRaycastCallback(filterCB, funcs, origin, dir, distance, 1, &mLocalHit, PxHitFlag::eDEFAULT, false), mHits (hits) {} // So far this looked similar to the DefaultPrunerRaycastClosestCallback code. But we customize the behavior in // the following function. virtual bool reportHits(const PrunerPayload& payload, PxU32 nbHits, PxGeomRaycastHit* hits) { // Because we didn't use PxHitFlag::eMESH_MULTIPLE we should only be called for a single hit PX_ASSERT(nbHits==1); PX_UNUSED(nbHits); // We process each hit the way we processed the closest hit at the end of CustomScene::raycastClosest CustomRaycastHit customHit; static_cast(customHit) = hits[0]; customHit.mObjectIndex = getObjectIndexFromPayload(payload); // Then we gather the new hit in our user-provided array. This is written for clarity, not performance. // Here we could instead call a user-provided callback. mHits.pushBack(customHit); // We return false to tell the system to ignore that hit now (otherwise the code would shrink the ray etc). // This is also the way one does "post filtering". return false; } PX_NOCOPY(CustomRaycastMultipleCallback) }; } // Generic usage, returns all hits touched by the ray, it's up to users to process them / sort them / etc. // We're using a PxArray here but it's an arbitrary choice, one could also return hits via a callback, etc. bool CustomScene::raycastMultiple(const PxVec3& origin, const PxVec3& unitDir, float maxDist, PxArray& hits) const { CustomRaycastMultipleCallback CB(hits, gFilterCallback, gCachedFuncs.mCachedRaycastFuncs, origin, unitDir, maxDist); mQuerySystem->raycast(origin, unitDir, maxDist, CB, NULL); return hits.size()!=0; } /////////////////////////////////////////////////////////////////////////////// namespace { // The sweep code is more complicated because the swept geometry is arbitrary (to some extent), and for performance reason there is no // generic sweep callback available operating on an anonymous PxGeometry. So the code below is what you can do instead. // It can be simplified in your app though if you know you only need to sweep one kind of geometry (say sphere sweeps). template static bool _sweepClosestT(CustomSweepHit& hit, const QuerySystem& sqs, const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist) { const ShapeData queryVolume(geom, pose, 0.0f); CallbackT pcb(gFilterCallback, gCachedFuncs.mCachedSweepFuncs, geom, pose, queryVolume, unitDir, maxDist, PxHitFlag::eDEFAULT | PxHitFlag::ePRECISE_SWEEP, false); sqs.sweep(queryVolume, unitDir, pcb.mClosestHit.distance, pcb, NULL); if(pcb.mFoundHit) { static_cast(hit) = pcb.mClosestHit; hit.mObjectIndex = getObjectIndexFromPayload(pcb.mClosestPayload); } return pcb.mFoundHit; } } // Common sweep usage, returns the closest touched object (single hit). bool CustomScene::sweepClosest(const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist, CustomSweepHit& hit) const { switch(PxU32(geom.getType())) { case PxGeometryType::eSPHERE: { return _sweepClosestT(hit, *mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eCAPSULE: { return _sweepClosestT(hit, *mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eBOX: { return _sweepClosestT(hit, *mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eCONVEXMESH: { return _sweepClosestT(hit, *mQuerySystem, geom, pose, unitDir, maxDist); } default: { PX_ASSERT(0); return false; } } } /////////////////////////////////////////////////////////////////////////////// namespace { // This could easily be combined with _sweepClosestT to make the code shorter. template static bool _sweepAnyT(const QuerySystem& sqs, const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist) { const ShapeData queryVolume(geom, pose, 0.0f); CallbackT pcb(gFilterCallback, gCachedFuncs.mCachedSweepFuncs, geom, pose, queryVolume, unitDir, maxDist, PxHitFlag::eANY_HIT | PxHitFlag::ePRECISE_SWEEP, true); sqs.sweep(queryVolume, unitDir, pcb.mClosestHit.distance, pcb, NULL); return pcb.mFoundHit; } } // Returns boolean result, early exits as soon as an impact is found. bool CustomScene::sweepAny(const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist) const { switch(PxU32(geom.getType())) { case PxGeometryType::eSPHERE: { return _sweepAnyT(*mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eCAPSULE: { return _sweepAnyT(*mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eBOX: { return _sweepAnyT(*mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eCONVEXMESH: { return _sweepAnyT(*mQuerySystem, geom, pose, unitDir, maxDist); } default: { PX_ASSERT(0); return false; } } } /////////////////////////////////////////////////////////////////////////////// namespace { // We use a similar strategy as for raycasts. Please refer to CustomRaycastMultipleCallback above. template struct CustomSweepMultipleCallback : public BaseCallbackT { PxArray& mHits; CustomSweepMultipleCallback(PxArray& hits, PrunerFilterCallback& filterCB, const GeomSweepFuncs& funcs, const PxGeometry& geom, const PxTransform& pose, const ShapeData& queryVolume, const PxVec3& dir, float distance) : BaseCallbackT (filterCB, funcs, geom, pose, queryVolume, dir, distance, PxHitFlag::eDEFAULT|PxHitFlag::ePRECISE_SWEEP, false), mHits (hits) {} virtual bool reportHit(const PrunerPayload& payload, PxGeomSweepHit& hit) { CustomSweepHit customHit; static_cast(customHit) = hit; customHit.mObjectIndex = getObjectIndexFromPayload(payload); mHits.pushBack(customHit); return false; } PX_NOCOPY(CustomSweepMultipleCallback) }; // Previous template was customizing the callback for multiple hits, this template customizes the previous template for different geoms. template static bool _sweepMultipleT(PxArray& hits, const QuerySystem& sqs, const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist) { const ShapeData queryVolume(geom, pose, 0.0f); CustomSweepMultipleCallback pcb(hits, gFilterCallback, gCachedFuncs.mCachedSweepFuncs, geom, pose, queryVolume, unitDir, maxDist); sqs.sweep(queryVolume, unitDir, pcb.mClosestHit.distance, pcb, NULL); return hits.size()!=0; } } // Generic usage, returns all hits touched by the swept volume, it's up to users to process them / sort them / etc. // We're using a PxArray here but it's an arbitrary choice, one could also return hits via a callback, etc. bool CustomScene::sweepMultiple(const PxGeometry& geom, const PxTransform& pose, const PxVec3& unitDir, float maxDist, PxArray& hits) const { switch(PxU32(geom.getType())) { case PxGeometryType::eSPHERE: { return _sweepMultipleT(hits, *mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eCAPSULE: { return _sweepMultipleT(hits, *mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eBOX: { return _sweepMultipleT(hits, *mQuerySystem, geom, pose, unitDir, maxDist); } case PxGeometryType::eCONVEXMESH: { return _sweepMultipleT(hits, *mQuerySystem, geom, pose, unitDir, maxDist); } default: { PX_ASSERT(0); return false; } } } /////////////////////////////////////////////////////////////////////////////// namespace { struct CustomOverlapAnyCallback : public DefaultPrunerOverlapCallback { bool mFoundHit; CustomOverlapAnyCallback(PrunerFilterCallback& filterCB, const GeomOverlapTable* funcs, const PxGeometry& geometry, const PxTransform& pose) : DefaultPrunerOverlapCallback(filterCB, funcs, geometry, pose), mFoundHit(false) {} virtual bool reportHit(const PrunerPayload& /*payload*/) { mFoundHit = true; return false; // Early exits as soon as we hit something } }; } // Simple boolean overlap. The query returns true if the passed shape touches anything, otherwise it returns false if space was free. bool CustomScene::overlapAny(const PxGeometry& geom, const PxTransform& pose) const { CustomOverlapAnyCallback pcb(gFilterCallback, gCachedFuncs.mCachedOverlapFuncs, geom, pose); const ShapeData queryVolume(geom, pose, 0.0f); mQuerySystem->overlap(queryVolume, pcb, NULL); return pcb.mFoundHit; } /////////////////////////////////////////////////////////////////////////////// namespace { struct CustomOverlapMultipleCallback : public DefaultPrunerOverlapCallback { PxArray& mHits; CustomOverlapMultipleCallback(PxArray& hits, PrunerFilterCallback& filterCB, const GeomOverlapTable* funcs, const PxGeometry& geometry, const PxTransform& pose) : DefaultPrunerOverlapCallback(filterCB, funcs, geometry, pose), mHits(hits) {} virtual bool reportHit(const PrunerPayload& payload) { // In this example we only return touched objects but we don't go deeper. For triangle meshes we could // here go to the triangle-level and return all touched triangles of a mesh, if needed. To do that we'd // fetch the PxTriangleMeshGeometry from the payload and use PxMeshQuery::findOverlapTriangleMesh. That // call is part of the regular PxMeshQuery API though so it's beyond the scope of this snippet, which is // about the Gu-level query system. Instead here we just retrieve & output the touched object's index: mHits.pushBack(getObjectIndexFromPayload(payload)); return true; // Continue query, call us again for next touched object } PX_NOCOPY(CustomOverlapMultipleCallback) }; } // Gather-touched-objects overlap. The query returns all objects touched by query volume, in an array. // This is an arbitrary choice in this snippet, it would be equally easy to report each object individually // to a user-callback, etc. bool CustomScene::overlapMultiple(const PxGeometry& geom, const PxTransform& pose, PxArray& hits) const { CustomOverlapMultipleCallback pcb(hits, gFilterCallback, gCachedFuncs.mCachedOverlapFuncs, geom, pose); const ShapeData queryVolume(geom, pose, 0.0f); mQuerySystem->overlap(queryVolume, pcb, NULL); return hits.size()!=0; } /////////////////////////////////////////////////////////////////////////////// void CustomScene::runQueries() { if(!mQuerySystem) return; // Reset all touched flags & colors const PxVec3 touchedColor(0.25f, 0.5f, 1.0f); for(PxU32 i=0;icommitUpdates(); switch(gSceneIndex) { case RAYCAST_CLOSEST: { const PxVec3 origin(0.0f, 10.0f, 0.0f); const PxVec3 unitDir(0.0f, -1.0f, 0.0f); const float maxDist = 20.0f; CustomRaycastHit hit; const bool hasHit = raycastClosest(origin, unitDir, maxDist, hit); #ifdef RENDER_SNIPPET if(hasHit) { DrawLine(origin, hit.position, PxVec3(1.0f)); DrawLine(hit.position, hit.position + hit.normal, PxVec3(1.0f, 1.0f, 0.0f)); DrawFrame(hit.position, 0.5f); mObjects[hit.mObjectIndex].mTouched = true; } else { DrawLine(origin, origin + unitDir * maxDist, PxVec3(1.0f)); } #else PX_UNUSED(hasHit); #endif } break; case RAYCAST_ANY: { const PxVec3 origin(0.0f, 10.0f, 0.0f); const PxVec3 unitDir(0.0f, -1.0f, 0.0f); const float maxDist = 20.0f; const bool hasHit = raycastAny(origin, unitDir, maxDist); #ifdef RENDER_SNIPPET if(hasHit) DrawLine(origin, origin + unitDir * maxDist, PxVec3(1.0f, 0.0f, 0.0f)); else DrawLine(origin, origin + unitDir * maxDist, PxVec3(0.0f, 1.0f, 0.0f)); #else PX_UNUSED(hasHit); #endif } break; case RAYCAST_MULTIPLE: { const PxVec3 origin(0.0f, 10.0f, 0.0f); const PxVec3 unitDir(0.0f, -1.0f, 0.0f); const float maxDist = 20.0f; PxArray hits; const bool hasHit = raycastMultiple(origin, unitDir, maxDist, hits); #ifdef RENDER_SNIPPET if(hasHit) { DrawLine(origin, origin + unitDir * maxDist, PxVec3(0.5f)); const PxU32 nbHits = hits.size(); for(PxU32 i=0;i hits; const bool hasHit = sweepMultiple(sweptGeom, pose, unitDir, maxDist, hits); #ifdef RENDER_SNIPPET const PxGeometryHolder gh(sweptGeom); renderGeoms(1, &gh, &pose, false, PxVec3(0.0f, 1.0f, 0.0f)); if(hasHit) { { const PxVec3 sweptPos = origin + unitDir * maxDist; DrawLine(origin, sweptPos, PxVec3(0.5f)); } // It can be difficult to see what is touching what so we use different colors to make it clearer. const PxVec3 touchedColors[] = { PxVec3(1.0f, 0.0f, 0.0f), PxVec3(0.0f, 0.0f, 1.0f), PxVec3(1.0f, 0.0f, 1.0f), PxVec3(0.0f, 1.0f, 1.0f), PxVec3(1.0f, 1.0f, 0.0f), PxVec3(1.0f, 1.0f, 1.0f), PxVec3(0.5f, 0.5f, 0.5f), }; const PxU32 nbHits = hits.size(); for(PxU32 i=0;i hits; const bool hasHit = overlapMultiple(queryGeom, pose, hits); #ifdef RENDER_SNIPPET const PxGeometryHolder gh(queryGeom); { const PxVec3 color = hasHit ? PxVec3(1.0f, 0.0f, 0.0f) : PxVec3(0.0f, 1.0f, 0.0f); renderGeoms(1, &gh, &pose, false, color); for(PxU32 i=0;igetPayloadData(obj.mData, &ppd); //DrawBounds(*bounds); const PxVec3& objectColor = obj.mTouched ? obj.mTouchedColor : color; renderGeoms(1, &obj.mGeom, ppd.mTransform, false, objectColor); } //mQuerySystem->visualize(true, true, PxRenderOutput) #endif } } static CustomScene* gScene = NULL; void initPhysics(bool /*interactive*/) { gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback); const PxTolerancesScale scale; PxCookingParams params(scale); params.midphaseDesc.setToDefault(PxMeshMidPhase::eBVH34); // params.midphaseDesc.mBVH34Desc.quantized = false; params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE; params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH; { { const PxF32 width = 3.0f; const PxF32 radius = 1.0f; PxVec3 points[2*16]; for(PxU32 i = 0; i < 16; i++) { const PxF32 cosTheta = PxCos(i*PxPi*2.0f/16.0f); const PxF32 sinTheta = PxSin(i*PxPi*2.0f/16.0f); const PxF32 y = radius*cosTheta; const PxF32 z = radius*sinTheta; points[2*i+0] = PxVec3(-width/2.0f, y, z); points[2*i+1] = PxVec3(+width/2.0f, y, z); } PxConvexMeshDesc convexDesc; convexDesc.points.count = 32; convexDesc.points.stride = sizeof(PxVec3); convexDesc.points.data = points; convexDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX; gConvexMesh = PxCreateConvexMesh(params, convexDesc); } { PxTriangleMeshDesc meshDesc; meshDesc.points.count = SnippetUtils::Bunny_getNbVerts(); meshDesc.points.stride = sizeof(PxVec3); meshDesc.points.data = SnippetUtils::Bunny_getVerts(); meshDesc.triangles.count = SnippetUtils::Bunny_getNbFaces(); meshDesc.triangles.stride = sizeof(int)*3; meshDesc.triangles.data = SnippetUtils::Bunny_getFaces(); gTriangleMesh = PxCreateTriangleMesh(params, meshDesc); } } gScene = new CustomScene; } void renderScene() { if(gScene) gScene->render(); #ifdef RENDER_SNIPPET Snippets::print("Press F1 to F8 to select a scenario."); switch(PxU32(gSceneIndex)) { case RAYCAST_CLOSEST: { Snippets::print("Current scenario: raycast closest"); }break; case RAYCAST_ANY: { Snippets::print("Current scenario: raycast any"); }break; case RAYCAST_MULTIPLE: { Snippets::print("Current scenario: raycast multiple"); }break; case SWEEP_CLOSEST: { Snippets::print("Current scenario: sweep closest"); }break; case SWEEP_ANY: { Snippets::print("Current scenario: sweep any"); }break; case SWEEP_MULTIPLE: { Snippets::print("Current scenario: sweep multiple"); }break; case OVERLAP_ANY: { Snippets::print("Current scenario: overlap any"); }break; case OVERLAP_MULTIPLE: { Snippets::print("Current scenario: overlap multiple"); }break; } #endif } void stepPhysics(bool /*interactive*/) { } void cleanupPhysics(bool /*interactive*/) { PX_RELEASE(gScene); PX_RELEASE(gConvexMesh); PX_RELEASE(gFoundation); printf("SnippetQuerySystemAllQueries done.\n"); } void keyPress(unsigned char key, const PxTransform& /*camera*/) { if(gScene) { if(key=='p' || key=='P') { gPause = !gPause; } else if(key=='o' || key=='O') { gPause = true; gOneFrame = true; } else { if(key>=1 && key<=NB_SCENES) { gSceneIndex = QueryScenario(key-1); PX_RELEASE(gScene); gScene = new CustomScene; } } } } int snippetMain(int, const char*const*) { printf("Query System All Queries snippet.\n"); printf("Press F1 to F8 to select a scene:\n"); printf(" F1......raycast closest\n"); printf(" F2..........raycast any\n"); printf(" F3.....raycast multiple\n"); printf(" F4........sweep closest\n"); printf(" F5............sweep any\n"); printf(" F6.......sweep multiple\n"); printf(" F7..........overlap any\n"); printf(" F8.....overlap multiple\n"); printf("\n"); printf("Press P to Pause.\n"); printf("Press O to step the simulation One frame.\n"); printf("Press the cursor keys to move the camera.\n"); printf("Use the mouse/left mouse button to rotate the camera.\n"); #ifdef RENDER_SNIPPET extern void renderLoop(); renderLoop(); #else static const PxU32 frameCount = 100; initPhysics(false); for(PxU32 i=0; i