// 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-2017 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 "PxSimulationEventCallback.h" #include "NpScene.h" #include "NpRigidStatic.h" #include "NpRigidDynamic.h" #include "NpArticulation.h" #include "NpArticulationLink.h" #include "NpArticulationJoint.h" // PX_AGGREGATE #include "NpAggregate.h" //~PX_AGGREGATE #include "NpVolumeCache.h" #include "NpBatchQuery.h" #include "SqPruner.h" #include "SqPrunerMergeData.h" #include "SqPruningStructure.h" #include "SqSceneQueryManager.h" #if PX_USE_PARTICLE_SYSTEM_API #include "particles/NpParticleSystem.h" #include "particles/NpParticleFluid.h" #include "ScbParticleSystem.h" #endif #if PX_USE_CLOTH_API #include "NpCloth.h" #endif #include "ScbNpDeps.h" #include "CmCollection.h" #include "CmUtils.h" #if PX_SUPPORT_GPU_PHYSX #include "task/PxGpuDispatcher.h" #endif #include "PxsIslandSim.h" using namespace physx; // enable thread checks in all debug builds #if PX_DEBUG || PX_CHECKED #define NP_ENABLE_THREAD_CHECKS 1 #else #define NP_ENABLE_THREAD_CHECKS 0 #endif using namespace shdfnd; using namespace Sq; /////////////////////////////////////////////////////////////////////////////// static PX_FORCE_INLINE bool removeFromSceneCheck(NpScene* npScene, PxScene* scene, const char* name) { if (scene == static_cast(npScene)) { return true; } else { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "%s not assigned to scene or assigned to another scene. Call will be ignored!", name); return false; } } /////////////////////////////////////////////////////////////////////////////// NpSceneQueries::NpSceneQueries(const PxSceneDesc& desc) : mScene (desc, getContextId()), mSQManager (mScene, desc.staticStructure, desc.dynamicStructure, desc.dynamicTreeRebuildRateHint, desc.limits), mCachedRaycastFuncs (Gu::getRaycastFuncTable()), mCachedSweepFuncs (Gu::getSweepFuncTable()), mCachedOverlapFuncs (Gu::getOverlapFuncTable()) #if PX_SUPPORT_PVD , mSingleSqCollector (mScene, false), mBatchedSqCollector (mScene, true) #endif { } NpScene::NpScene(const PxSceneDesc& desc) : NpSceneQueries (desc), mConstraints (PX_DEBUG_EXP("sceneConstraints")), mRigidActors (PX_DEBUG_EXP("sceneRigidActors")), mArticulations (PX_DEBUG_EXP("sceneArticulations")), mAggregates (PX_DEBUG_EXP("sceneAggregates")), #if PX_USE_PARTICLE_SYSTEM_API mPxParticleBaseSet (PX_DEBUG_EXP("sceneParticles")), #endif #if PX_USE_CLOTH_API mPxCloths (PX_DEBUG_EXP("sceneCloths")), #endif mSanityBounds (desc.sanityBounds), mNbClients (1), //we always have the default client. mClientBehaviorFlags (PX_DEBUG_EXP("sceneBehaviorFlags")), mSceneCompletion (mPhysicsDone), mCollisionCompletion (mCollisionDone), mSceneExecution (0, "NpScene.execution"), mSceneCollide (0, "NpScene.collide"), mSceneAdvance (0, "NpScene.solve"), mControllingSimulation (false), mSimThreadStackSize (0), mConcurrentWriteCount (0), mConcurrentReadCount (0), mConcurrentErrorCount (0), mCurrentWriter (0), mHasSimulatedOnce (false), mBetweenFetchResults (false) { mSceneExecution.setObject(this); mSceneCollide.setObject(this); mSceneAdvance.setObject(this); mTaskManager = mScene.getScScene().getTaskManagerPtr(); mThreadReadWriteDepth = Ps::TlsAlloc(); } NpSceneQueries::~NpSceneQueries() { } NpScene::~NpScene() { // PT: we need to do that one first, now that we don't release the objects anymore. Otherwise we end up with a sequence like: // - actor is part of an aggregate, and part of a scene // - actor gets removed from the scene. This does *not* remove it from the aggregate. // - aggregate gets removed from the scene, sees that one contained actor ain't in the scene => we get a warning message PxU32 aggregateCount = mAggregates.size(); while(aggregateCount--) removeAggregate(*mAggregates.getEntries()[aggregateCount], false); #if PX_USE_PARTICLE_SYSTEM_API PxU32 partCount = mPxParticleBaseSet.size(); while(partCount--) removeActor(*mPxParticleBaseSet.getEntries()[partCount], false); #endif #if PX_USE_CLOTH_API PxU32 clothCount = mPxCloths.size(); while(clothCount--) removeActor(*mPxCloths.getEntries()[clothCount], false); #endif PxU32 rigidActorCount = mRigidActors.size(); while(rigidActorCount--) removeActor(*mRigidActors[rigidActorCount], false); PxU32 articCount = mArticulations.size(); while(articCount--) removeArticulation(*mArticulations.getEntries()[articCount], false); // release volume caches Array caches; caches.reserve(mVolumeCaches.size()); for(HashSet::Iterator iter = mVolumeCaches.getIterator(); !iter.done(); ++iter) caches.pushBack(*iter); for(PxU32 i = 0; i < caches.size(); i++) releaseVolumeCache(caches[i]); bool unlock = mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK; #if PX_SUPPORT_PVD getSingleSqCollector().release(); getBatchedSqCollector().release(); #endif // release batch queries PxU32 numSq = mBatchQueries.size(); while(numSq--) PX_DELETE(mBatchQueries[numSq]); mBatchQueries.clear(); mScene.release(); // unlock the lock taken in release(), must unlock before // mRWLock is destroyed otherwise behavior is undefined if (unlock) unlockWrite(); TlsFree(mThreadReadWriteDepth); } /////////////////////////////////////////////////////////////////////////////// void NpScene::release() { // need to acquire lock for release, note this is unlocked in the destructor if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) lockWrite(__FILE__, __LINE__); // It will be hard to do a write check here since all object release calls in the scene destructor do it and would mess // up the test. If we really want it on scene destruction as well, we need to either have internal and external release // calls or come up with a different approach (for example using thread ID as detector variable). if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::release(): Scene is still being simulated! PxScene::fetchResults() is called implicitly."); if(getSimulationStage() == Sc::SimulationStage::eCOLLIDE) { fetchCollision(true); } if(getSimulationStage() == Sc::SimulationStage::eFETCHCOLLIDE) // need to call getSimulationStage() again beacause fetchCollision() might change the value. { // this is for split sim advance(NULL); } fetchResults(true, NULL); } NpPhysics::getInstance().releaseSceneInternal(*this); } /////////////////////////////////////////////////////////////////////////////// bool NpScene::loadFromDesc(const PxSceneDesc& desc) { { if(desc.limits.maxNbActors) mRigidActors.reserve(desc.limits.maxNbActors); //const PxU32 totalNbShapes = desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes; mScene.getScScene().preAllocate(desc.limits.maxNbActors, desc.limits.maxNbBodies, desc.limits.maxNbStaticShapes, desc.limits.maxNbDynamicShapes); } userData = desc.userData; return true; } /////////////////////////////////////////////////////////////////////////////// void NpScene::setGravity(const PxVec3& g) { NP_WRITE_CHECK(this); mScene.setGravity(g); } PxVec3 NpScene::getGravity() const { NP_READ_CHECK(this); return mScene.getGravity(); } /////////////////////////////////////////////////////////////////////////////// void NpScene::setBounceThresholdVelocity(const PxReal t) { NP_WRITE_CHECK(this); mScene.setBounceThresholdVelocity(t); } PxReal NpScene::getBounceThresholdVelocity() const { NP_READ_CHECK(this) return mScene.getBounceThresholdVelocity(); } /////////////////////////////////////////////////////////////////////////////// void NpScene::setLimits(const PxSceneLimits& limits) { NP_WRITE_CHECK(this); if(limits.maxNbActors) mRigidActors.reserve(limits.maxNbActors); mScene.getScScene().preAllocate(limits.maxNbActors, limits.maxNbBodies, limits.maxNbStaticShapes, limits.maxNbDynamicShapes); mScene.setLimits(limits); mSQManager.preallocate(limits.maxNbStaticShapes, limits.maxNbDynamicShapes); } ////////////////////////////////////////////////////////////////////////// PxSceneLimits NpScene::getLimits() const { NP_READ_CHECK(this); return mScene.getLimits(); } /////////////////////////////////////////////////////////////////////////////// void NpScene::setFlag(PxSceneFlag::Enum flag, bool value) { NP_WRITE_CHECK(this); // this call supports mutable flags only PX_CHECK_AND_RETURN(PxSceneFlags(flag) & PxSceneFlags(PxSceneFlag::eMUTABLE_FLAGS), "PxScene::setFlag: This flag is not mutable - you can only set it once in PxSceneDesc at startup!"); PxSceneFlags currentFlags = mScene.getFlags(); if(value) currentFlags |= flag; else currentFlags &= ~PxSceneFlags(flag); mScene.setFlags(currentFlags); } PxSceneFlags NpScene::getFlags() const { NP_READ_CHECK(this); return mScene.getFlags(); } /////////////////////////////////////////////////////////////////////////////// // PT: make sure we always add to array and set the array index properly / at the same time template static PX_FORCE_INLINE void addRigidActorToArray(T& a, Ps::Array& rigidActors) { a.setRigidActorArrayIndex(rigidActors.size()); rigidActors.pushBack(&a); } void NpScene::addActor(PxActor& actor) { PX_PROFILE_ZONE("API.addActor", getContextId()); NP_WRITE_CHECK(this); PX_SIMD_GUARD; PxRigidStatic* a = actor.is(); if(a) { #if PX_CHECKED if(!static_cast(a)->checkConstraintValidity()) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor has invalid constraint and may not be added to scene"); return; } #endif if(static_cast(a)->getShapeManager().getPruningStructure()) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); return; } } PxRigidDynamic* aD = actor.is(); if(aD && static_cast(aD)->getShapeManager().getPruningStructure()) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); return; } const Scb::ControlState::Enum cs = NpActor::getScbFromPxActor(actor).getControlState(); if((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (NpActor::getOwnerScene(actor) == this))) addActorInternal(actor); else Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): Actor already assigned to a scene. Call will be ignored!"); } void NpScene::addActorInternal(PxActor& actor) { switch(actor.getConcreteType()) { case PxConcreteType::eRIGID_STATIC: { NpRigidStatic& npStatic = static_cast(actor); #if PX_CHECKED checkPositionSanity(npStatic, npStatic.getGlobalPose(), "PxScene::addActor or PxScene::addAggregate"); #endif addRigidStatic(npStatic); } break; case PxConcreteType::eRIGID_DYNAMIC: { NpRigidDynamic& npDynamic = static_cast(actor); #if PX_CHECKED checkPositionSanity(npDynamic, npDynamic.getGlobalPose(), "PxScene::addActor or PxScene::addAggregate"); #endif addRigidDynamic(npDynamic); } break; #if PX_USE_PARTICLE_SYSTEM_API case PxConcreteType::ePARTICLE_SYSTEM: { NpParticleSystem& npSystem = static_cast(actor); addParticleSystem(npSystem); } break; case PxConcreteType::ePARTICLE_FLUID: { NpParticleFluid& npFluid = static_cast(actor); addParticleFluid(npFluid); } break; #endif #if PX_USE_CLOTH_API case PxConcreteType::eCLOTH: { NpCloth& npCloth = static_cast(actor); addCloth(npCloth); } break; #endif case PxConcreteType::eARTICULATION_LINK: { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addActor(): Individual articulation links can not be added to the scene"); } break; default: PX_ASSERT(0); } } void NpScene::updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Actor& scbActor, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure) { // all the things Scb does in non-buffered insertion SceneQueryManager& sqManager = getSceneQueryManagerFast(); scbActor.setScbScene(&mScene); scbActor.setControlState(Scb::ControlState::eIN_SCENE); NpShape*const * shapes = shapeManager.getShapes(); PxU32 nbShapes = shapeManager.getNbShapes(); for(PxU32 i=0;i(body), shapeManager, actorDynamic, bounds, hasPrunerStructure); } void NpScene::addActors(PxActor*const* actors, PxU32 nbActors) { addActorsInternal(actors, nbActors, NULL); } void NpScene::addActors(const PxPruningStructure& ps) { const Sq::PruningStructure& prunerStructure = static_cast(ps); if(!prunerStructure.isValid()) { Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxScene::addActors(): Provided pruning structure is not valid."); return; } addActorsInternal(prunerStructure.getActors(), prunerStructure.getNbActors(), &prunerStructure); } void NpScene::addActorsInternal(PxActor*const* PX_RESTRICT actors, PxU32 nbActors, const Sq::PruningStructure* pS) { PX_PROFILE_ZONE("API.addActors", getContextId()); NP_WRITE_CHECK(this); PX_SIMD_GUARD; if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addActors() not allowed while simulation is running."); return; } const bool hasPrunerStructure = pS ? true : false; Sc::Scene& scScene = mScene.getScScene(); PxU32 actorsDone; Sc::BatchInsertionState scState; scScene.startBatchInsertion(scState); scState.staticActorOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getScbRigidStaticFast().getScStatic()))); scState.staticShapeTableOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getShapeManager().getShapeTable()))); scState.dynamicActorOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getScbBodyFast().getScBody()))); scState.dynamicShapeTableOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getShapeManager().getShapeTable()))); scState.shapeOffset = ptrdiff_t(NpShapeGetScPtrOffset()); Ps::InlineArray shapeBounds; for(actorsDone=0; actorsDonegetConcreteType(); if(type == PxConcreteType::eRIGID_STATIC) { NpRigidStatic& a = *static_cast(actors[actorsDone]); #if PX_CHECKED if(!a.checkConstraintValidity()) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor has invalid constraint and may not be added to scene"); break; } checkPositionSanity(a, a.getGlobalPose(), "PxScene::addActors"); #endif if(!hasPrunerStructure && a.getShapeManager().getPruningStructure()) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); break; } if(!(a.getScbRigidStaticFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) { shapeBounds.resizeUninitialized(a.NpRigidStatic::getNbShapes()+1); // PT: +1 for safe reads in addPrunerData/inflateBounds scScene.addStatic(&a, scState, shapeBounds.begin()); updateScbStateAndSetupSq(a, a.getScbActorFast(), a.getShapeManager(), false, shapeBounds.begin(), hasPrunerStructure); addRigidActorToArray(a, mRigidActors); a.addConstraintsToScene(); } else addRigidStatic(a, hasPrunerStructure); } else if(type == PxConcreteType::eRIGID_DYNAMIC) { NpRigidDynamic& a = *static_cast(actors[actorsDone]); #if PX_CHECKED checkPositionSanity(a, a.getGlobalPose(), "PxScene::addActors"); #endif if(!hasPrunerStructure && a.getShapeManager().getPruningStructure()) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); break; } if(!(a.getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) { shapeBounds.resizeUninitialized(a.NpRigidDynamic::getNbShapes()+1); // PT: +1 for safe reads in addPrunerData/inflateBounds scScene.addBody(&a, scState, shapeBounds.begin()); updateScbStateAndSetupSq(a, a.getScbBodyFast(), a.getShapeManager(), true, shapeBounds.begin(), hasPrunerStructure); addRigidActorToArray(a, mRigidActors); a.addConstraintsToScene(); } else addRigidDynamic(a, hasPrunerStructure); } else if(type == PxConcreteType::eCLOTH || type == PxConcreteType::ePARTICLE_SYSTEM || type == PxConcreteType::ePARTICLE_FLUID) { addActorInternal(*actors[actorsDone]); } else { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addRigidActors(): articulation link not permitted"); break; } } // merge sq PrunerStructure if(pS) { if(pS->getTreeNodes(PruningIndex::eSTATIC)) { AABBPrunerMergeData params(pS->getTreeNbNodes(PruningIndex::eSTATIC), pS->getTreeNodes(PruningIndex::eSTATIC), pS->getNbObjects(PruningIndex::eSTATIC), pS->getTreeIndices(PruningIndex::eSTATIC)); mSQManager.get(PruningIndex::eSTATIC).pruner()->merge(¶ms); } if(pS->getTreeNodes(PruningIndex::eDYNAMIC)) { AABBPrunerMergeData params(pS->getTreeNbNodes(PruningIndex::eDYNAMIC), pS->getTreeNodes(PruningIndex::eDYNAMIC), pS->getNbObjects(PruningIndex::eDYNAMIC), pS->getTreeIndices(PruningIndex::eDYNAMIC)); mSQManager.get(PruningIndex::eDYNAMIC).pruner()->merge(¶ms); } } scScene.finishBatchInsertion(scState); // if we failed, still complete everything for the successful inserted actors before backing out #if PX_SUPPORT_PVD for(PxU32 i=0;igetConcreteType()==PxConcreteType::eRIGID_STATIC) && (!(static_cast(actors[i])->getScbRigidStaticFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) mScene.getScenePvdClient().addStaticAndShapesToPvd(static_cast(actors[i])->getScbRigidStaticFast()); else if ((actors[i]->getConcreteType() == PxConcreteType::eRIGID_DYNAMIC) && (!(static_cast(actors[i])->getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) mScene.getScenePvdClient().addBodyAndShapesToPvd(static_cast(actors[i])->getScbBodyFast()); } #endif if(actorsDonegetConcreteType(); if (!removeFromSceneCheck(this, actors[actorsDone]->getScene(), "PxScene::removeActors(): Actor")) { break; } removeState.bufferedShapes.clear(); removeState.removedShapes.clear(); if(type == PxConcreteType::eRIGID_STATIC) { NpRigidStatic& actor = *static_cast(actors[actorsDone]); const PxActorFlags actorFlags = actor.getScbRigidStaticFast().getActorFlags(); if(actor.getShapeManager().getNbShapes()) Ps::prefetch(actor.getShapeManager().getShapes()[0],sizeof(NpShape)); scScene.prefetchForRemove(actor.getScbRigidStaticFast().getScStatic()); Ps::prefetch(mRigidActors[mRigidActors.size()-1],sizeof(NpRigidDynamic)); const bool noSimBuffered = actorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); if (!noSimBuffered) actor.removeConstraintsFromScene(); actor.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast()); Scb::RigidStatic& rs = actor.getScbRigidStaticFast(); mScene.removeActor(rs, wakeOnLostTouch, rs.isSimDisabledInternally()); removeFromRigidActorList(actor.getRigidActorArrayIndex()); } else if(type == PxConcreteType::eRIGID_DYNAMIC) { NpRigidDynamic& actor = *static_cast(actors[actorsDone]); const PxActorFlags actorFlags = actor.getScbBodyFast().getActorFlags(); if(actor.getShapeManager().getNbShapes()) Ps::prefetch(actor.getShapeManager().getShapes()[0],sizeof(NpShape)); scScene.prefetchForRemove(actor.getScbBodyFast().getScBody()); Ps::prefetch(mRigidActors[mRigidActors.size()-1],sizeof(NpRigidDynamic)); const bool noSimBuffered = actorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); if (!noSimBuffered) actor.removeConstraintsFromScene(); actor.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast()); Scb::Body& b = actor.getScbBodyFast(); mScene.removeActor(b, wakeOnLostTouch, b.isSimDisabledInternally()); removeFromRigidActorList(actor.getRigidActorArrayIndex()); } else if(type == PxConcreteType::eCLOTH || type == PxConcreteType::ePARTICLE_SYSTEM || type == PxConcreteType::ePARTICLE_FLUID) { removeActorInternal(*actors[actorsDone],wakeOnLostTouch, true); } else { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::removeActor(): Individual articulation links can not be removed from the scene"); break; } } scScene.setBatchRemove(NULL); } void NpScene::removeActor(PxActor& actor, bool wakeOnLostTouch) { PX_PROFILE_ZONE("API.removeActor", getContextId()); NP_WRITE_CHECK(this); if (removeFromSceneCheck(this, actor.getScene(), "PxScene::removeActor(): Actor")) { removeActorInternal(actor, wakeOnLostTouch, true); } } void NpScene::removeActorInternal(PxActor& actor, bool wakeOnLostTouch, bool removeFromAggregate) { switch(actor.getType()) { case PxActorType::eRIGID_STATIC: { NpRigidStatic& npStatic = static_cast(actor); removeRigidStatic(npStatic, wakeOnLostTouch, removeFromAggregate); } break; case PxActorType::eRIGID_DYNAMIC: { NpRigidDynamic& npDynamic = static_cast(actor); removeRigidDynamic(npDynamic, wakeOnLostTouch, removeFromAggregate); } break; #if PX_USE_PARTICLE_SYSTEM_API case PxActorType::ePARTICLE_SYSTEM: { NpParticleSystem& npSystem = static_cast(actor); removeParticleSystem(npSystem); } break; case PxActorType::ePARTICLE_FLUID: { NpParticleFluid& npFluid = static_cast(actor); removeParticleFluid(npFluid); } break; #endif #if PX_USE_CLOTH_API case PxActorType::eCLOTH: { NpCloth& npCloth = static_cast(actor); removeCloth(npCloth); } break; #endif case PxActorType::eARTICULATION_LINK: { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::removeActor(): Individual articulation links can not be removed from the scene"); } break; case PxActorType::eACTOR_COUNT: case PxActorType::eACTOR_FORCE_DWORD: PX_ASSERT(0); } } /////////////////////////////////////////////////////////////////////////////// // PT: TODO: inline this one in the header for consistency void NpScene::removeFromRigidActorList(const PxU32& index) { PX_ASSERT(index != 0xFFFFFFFF); PX_ASSERT(index < mRigidActors.size()); { const PxU32 size = mRigidActors.size() - 1; mRigidActors.replaceWithLast(index); if(size && size != index) { PxRigidActor& rigidActor = *mRigidActors[index]; switch(rigidActor.getType()) { case PxActorType::eRIGID_STATIC: { NpRigidStatic& npStatic = static_cast(rigidActor); npStatic.setRigidActorArrayIndex(index); } break; case PxActorType::eRIGID_DYNAMIC: { NpRigidDynamic& npDynamic = static_cast(rigidActor); npDynamic.setRigidActorArrayIndex(index); } break; #if PX_USE_CLOTH_API case PxActorType::eCLOTH: #endif #if PX_USE_PARTICLE_SYSTEM_API case PxActorType::ePARTICLE_FLUID: case PxActorType::ePARTICLE_SYSTEM: #endif case PxActorType::eARTICULATION_LINK: case PxActorType::eACTOR_COUNT: case PxActorType::eACTOR_FORCE_DWORD: PX_ASSERT(0); break; } } } } /////////////////////////////////////////////////////////////////////////////// template static PX_FORCE_INLINE void addActorT(T& actor, T2& scbActor, Ps::Array& actors, NpScene* scene, bool hasPrunerStructure) { const bool noSimBuffered = scbActor.getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); PxBounds3 bounds[8+1]; // PT: +1 for safe reads in addPrunerData/inflateBounds const bool canReuseBounds = !noSimBuffered && !scene->getScene().isPhysicsBuffering() && actor.getShapeManager().getNbShapes()<=8; PxBounds3* uninflatedBounds = canReuseBounds ? bounds : NULL; scene->getScene().addActor(scbActor, noSimBuffered, uninflatedBounds); actor.getShapeManager().setupAllSceneQuery(scene, actor, hasPrunerStructure, uninflatedBounds); if(!noSimBuffered) actor.addConstraintsToScene(); addRigidActorToArray(actor, actors); } void NpScene::addRigidStatic(NpRigidStatic& actor, bool hasPrunerStructure) { addActorT(actor, actor.getScbRigidStaticFast(), mRigidActors, this, hasPrunerStructure); } void NpScene::addRigidDynamic(NpRigidDynamic& body, bool hasPrunerStructure) { addActorT(body, body.getScbBodyFast(), mRigidActors, this, hasPrunerStructure); } /////////////////////////////////////////////////////////////////////////////// template static PX_FORCE_INLINE void removeActorT(T& actor, T2& scbActor, NpScene* scene, bool wakeOnLostTouch, bool removeFromAggregate) { PX_ASSERT(NpActor::getAPIScene(actor) == scene); const bool noSimBuffered = scbActor.getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); if(removeFromAggregate) { PxU32 index = 0xffffffff; NpAggregate* aggregate = actor.getNpAggregate(index); if(aggregate) { aggregate->removeActorAndReinsert(actor, false); PX_ASSERT(!actor.getAggregate()); } } actor.getShapeManager().teardownAllSceneQuery(scene->getSceneQueryManagerFast()); if(!noSimBuffered) actor.removeConstraintsFromScene(); scene->getScene().removeActor(scbActor, wakeOnLostTouch, scbActor.isSimDisabledInternally()); scene->removeFromRigidActorList(actor.getRigidActorArrayIndex()); } void NpScene::removeRigidStatic(NpRigidStatic& actor, bool wakeOnLostTouch, bool removeFromAggregate) { removeActorT(actor, actor.getScbRigidStaticFast(), this, wakeOnLostTouch, removeFromAggregate); } void NpScene::removeRigidDynamic(NpRigidDynamic& body, bool wakeOnLostTouch, bool removeFromAggregate) { removeActorT(body, body.getScbBodyFast(), this, wakeOnLostTouch, removeFromAggregate); } /////////////////////////////////////////////////////////////////////////////// void NpScene::addArticulation(PxArticulation& articulation) { PX_PROFILE_ZONE("API.addArticulation", getContextId()); NP_WRITE_CHECK(this); PX_CHECK_AND_RETURN(articulation.getNbLinks()>0, "PxScene::addArticulation: empty articulations may not be added to simulation."); PX_SIMD_GUARD; if (this->getFlags() & PxSceneFlag::eENABLE_GPU_DYNAMICS) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): Articulations are not currently supported when PxSceneFlag::eENABLE_GPU_DYNAMICS is set!"); return; } Scb::Articulation& art = static_cast(articulation).getArticulation(); const Scb::ControlState::Enum cs = art.getControlState(); if ((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (art.getScbScene()->getPxScene() == this))) addArticulationInternal(articulation); else Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation already assigned to a scene. Call will be ignored!"); } void NpScene::addArticulationInternal(PxArticulation& articulation) { NpArticulation& npa = static_cast(articulation); // Add root link first PxU32 nbLinks = npa.getNbLinks(); PX_ASSERT(nbLinks > 0); NpArticulationLink* rootLink = npa.getLinks()[0]; #if PX_CHECKED checkPositionSanity(*rootLink, rootLink->getGlobalPose(), "PxScene::addArticulation or PxScene::addAggregate"); #endif if(rootLink->getMass()==0) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); rootLink->setMass(1.0f); } PxVec3 inertia0 = rootLink->getMassSpaceInertiaTensor(); if(inertia0.x == 0.0f || inertia0.y == 0.0f || inertia0.z == 0.0f) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); rootLink->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); } bool linkTriggersWakeUp = !rootLink->getScbBodyFast().checkSleepReadinessBesidesWakeCounter(); addArticulationLinkBody(*rootLink); // Add articulation Scb::Articulation& scbArt = npa.getArticulation(); mScene.addArticulation(scbArt); addArticulationLinkConstraint(*rootLink); // Add links & joints PX_ALLOCA(linkStack, NpArticulationLink*, nbLinks); linkStack[0] = rootLink; PxU32 curLink = 0; PxU32 stackSize = 1; while(curLink < (nbLinks-1)) { PX_ASSERT(curLink < stackSize); NpArticulationLink* l = linkStack[curLink]; NpArticulationLink*const* children = l->getChildren(); for(PxU32 i=0; i < l->getNbChildren(); i++) { NpArticulationLink* child = children[i]; #if PX_CHECKED checkPositionSanity(*child, child->getGlobalPose(), "PxScene::addArticulation"); #endif if(child->getMass()==0) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); child->setMass(1.0f); } PxVec3 inertia = child->getMassSpaceInertiaTensor(); if(inertia.x == 0.0f || inertia.y == 0.0f || inertia.z == 0.0f) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); child->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); } linkTriggersWakeUp = linkTriggersWakeUp || (!child->getScbBodyFast().checkSleepReadinessBesidesWakeCounter()); addArticulationLink(*child); // Adds joint too linkStack[stackSize] = child; stackSize++; } curLink++; } if ((scbArt.getWakeCounter() == 0.0f) && linkTriggersWakeUp) { // this is for the buffered insert case, where the articulation needs to wake up, if one of the links triggers activation. npa.wakeUpInternal(true, false); } mArticulations.insert(&npa); } void NpScene::removeArticulation(PxArticulation& articulation, bool wakeOnLostTouch) { PX_PROFILE_ZONE("API.removeArticulation", getContextId()); NP_WRITE_CHECK(this); if (removeFromSceneCheck(this, articulation.getScene(), "PxScene::removeArticulation(): Articulation")) { removeArticulationInternal(articulation, wakeOnLostTouch, true); } } void NpScene::removeArticulationInternal(PxArticulation& articulation, bool wakeOnLostTouch, bool removeFromAggregate) { NpArticulation& npa = static_cast(articulation); PxU32 nbLinks = npa.getNbLinks(); PX_ASSERT(nbLinks > 0); if(removeFromAggregate && articulation.getAggregate()) { static_cast(articulation.getAggregate())->removeArticulationAndReinsert(articulation, false); PX_ASSERT(!articulation.getAggregate()); } //!!!AL // Inefficient. We might want to introduce a LL method to kill the whole LL articulation together with all joints in one go, then // the order of removing the links/joints does not matter anymore. // Remove links & joints PX_ALLOCA(linkStack, NpArticulationLink*, nbLinks); linkStack[0] = npa.getLinks()[0]; PxU32 curLink = 0, stackSize = 1; while(curLink < (nbLinks-1)) { PX_ASSERT(curLink < stackSize); NpArticulationLink* l = linkStack[curLink]; NpArticulationLink*const* children = l->getChildren(); for(PxU32 i=0; i < l->getNbChildren(); i++) { linkStack[stackSize] = children[i]; stackSize++; } curLink++; } PxRigidBodyFlags flag; for(PxI32 j=PxI32(nbLinks); j-- > 0; ) { flag |=linkStack[j]->getScbBodyFast().getScBody().getCore().mFlags; removeArticulationLink(*linkStack[j], wakeOnLostTouch); } if (flag & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) { IG::NodeIndex index = npa.getScbArticulation().getScArticulation().getIslandNodeIndex(); if (index.isValid()) mScene.getScScene().resetSpeculativeCCDArticulationLink(index.index()); } // Remove articulation mScene.removeArticulation(npa.getArticulation()); removeFromArticulationList(articulation); } /////////////////////////////////////////////////////////////////////////////// void NpScene::addArticulationLinkBody(NpArticulationLink& link) { mScene.addActor(link.getScbBodyFast(), false, NULL); link.getShapeManager().setupAllSceneQuery(this, link, false); } void NpScene::addArticulationLinkConstraint(NpArticulationLink& link) { NpArticulationJoint* j = static_cast(link.getInboundJoint()); if (j) mScene.addArticulationJoint(j->getScbArticulationJoint()); link.addConstraintsToScene(); } void NpScene::addArticulationLink(NpArticulationLink& link) { addArticulationLinkBody(link); addArticulationLinkConstraint(link); } void NpScene::removeArticulationLink(NpArticulationLink& link, bool wakeOnLostTouch) { NpArticulationJoint* j = static_cast(link.getInboundJoint()); link.removeConstraintsFromScene(); link.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast()); if (j) mScene.removeArticulationJoint(j->getScbArticulationJoint()); mScene.removeActor(link.getScbBodyFast(), wakeOnLostTouch, false); } /////////////////////////////////////////////////////////////////////////////// // PX_AGGREGATE void NpScene::addAggregate(PxAggregate& aggregate) { PX_PROFILE_ZONE("API.addAggregate", getContextId()); NP_WRITE_CHECK(this); PX_SIMD_GUARD; NpAggregate& np = static_cast(aggregate); const PxU32 nb = np.getCurrentSizeFast(); #if PX_CHECKED for(PxU32 i=0;iis(); if(a && !static_cast(a)->checkConstraintValidity()) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addAggregate(): Aggregate contains an actor with an invalid constraint!"); return; } } #endif Scb::Aggregate& agg = np.getScbAggregate(); const Scb::ControlState::Enum cs = agg.getControlState(); if ((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (agg.getScbScene()->getPxScene() == this))) { mScene.addAggregate(agg); for(PxU32 i=0;i(aggregate); if(np.getScene()!=this) return; const PxU32 nb = np.getCurrentSizeFast(); for(PxU32 j=0;jgetType() != PxActorType::eARTICULATION_LINK) { Scb::Actor& scb = NpActor::getScbFromPxActor(*a); np.getScbAggregate().removeActor(scb, false); // This is only here to make sure the aggregateID gets set to invalid on sync removeActorInternal(*a, wakeOnLostTouch, false); } else if (a->getScene()) { NpArticulationLink& al = static_cast(*a); NpArticulation& npArt = al.getRoot(); NpArticulationLink* const* links = npArt.getLinks(); for(PxU32 i=0; i < npArt.getNbLinks(); i++) { np.getScbAggregate().removeActor(links[i]->getScbActorFast(), false); // This is only here to make sure the aggregateID gets set to invalid on sync } removeArticulationInternal(npArt, wakeOnLostTouch, false); } } mScene.removeAggregate(np.getScbAggregate()); removeFromAggregateList(aggregate); } PxU32 NpScene::getNbAggregates() const { NP_READ_CHECK(this); return mAggregates.size(); } PxU32 NpScene::getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex) const { NP_READ_CHECK(this); return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mAggregates.getEntries(), mAggregates.size()); } //~PX_AGGREGATE void NpScene::addCollection(const PxCollection& collection) { PX_PROFILE_ZONE("API.addCollection", getContextId()); const Cm::Collection& col = static_cast(collection); PxU32 nb = col.internalGetNbObjects(); #if PX_CHECKED for(PxU32 i=0;iis(); if(a && !static_cast(a)->checkConstraintValidity()) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addCollection(): collection contains an actor with an invalid constraint!"); return; } } #endif Ps::Array actorsToInsert; actorsToInsert.reserve(nb); struct Local { static void addActorIfNeeded(PxActor* actor, Ps::Array& actorArray) { if(actor->getAggregate()) return; // The actor will be added when the aggregate is added actorArray.pushBack(actor); } }; for(PxU32 i=0;igetConcreteType(); //NpArticulationLink, NpArticulationJoint are added with the NpArticulation //Actors and Articulations that are members of an Aggregate are added with the NpAggregate if(serialType==PxConcreteType::eRIGID_DYNAMIC) { NpRigidDynamic* np = static_cast(s); // if pruner structure exists for the actor, actor will be added with the pruner structure if(!np->getShapeManager().getPruningStructure()) Local::addActorIfNeeded(np, actorsToInsert); } else if(serialType==PxConcreteType::eRIGID_STATIC) { NpRigidStatic* np = static_cast(s); // if pruner structure exists for the actor, actor will be added with the pruner structure if(!np->getShapeManager().getPruningStructure()) Local::addActorIfNeeded(np, actorsToInsert); } else if(serialType==PxConcreteType::eSHAPE) { } #if PX_USE_CLOTH_API else if (serialType==PxConcreteType::eCLOTH) { NpCloth* np = static_cast(s); Local::addActorIfNeeded(np, actorsToInsert); } #endif #if PX_USE_PARTICLE_SYSTEM_API else if(serialType==PxConcreteType::ePARTICLE_SYSTEM) { NpParticleSystem* np = static_cast(s); Local::addActorIfNeeded(np, actorsToInsert); } else if(serialType==PxConcreteType::ePARTICLE_FLUID) { NpParticleFluid* np = static_cast(s); Local::addActorIfNeeded(np, actorsToInsert); } #endif else if(serialType==PxConcreteType::eARTICULATION) { NpArticulation* np = static_cast(s); if(!np->getAggregate()) // The actor will be added when the aggregate is added addArticulation(*np); } else if(serialType==PxConcreteType::eAGGREGATE) { NpAggregate* np = static_cast(s); addAggregate(*np); } else if(serialType == PxConcreteType::ePRUNING_STRUCTURE) { PxPruningStructure* ps = static_cast(s); addActors(*ps); } } if(!actorsToInsert.empty()) addActorsInternal(&actorsToInsert[0], actorsToInsert.size(), NULL); } /////////////////////////////////////////////////////////////////////////////// PxU32 NpScene::getNbActors(PxActorTypeFlags types) const { NP_READ_CHECK(this); PxU32 nbActors = 0; if (types & PxActorTypeFlag::eRIGID_STATIC) { for(PxU32 i=mRigidActors.size(); i--;) { if (mRigidActors[i]->is()) nbActors++; } } if (types & PxActorTypeFlag::eRIGID_DYNAMIC) { for(PxU32 i=mRigidActors.size(); i--;) { if (mRigidActors[i]->is()) nbActors++; } } #if PX_USE_PARTICLE_SYSTEM_API if (types & PxActorTypeFlag::ePARTICLE_SYSTEM) { PxParticleBase*const* particles = mPxParticleBaseSet.getEntries(); const PxU32 particleBaseCount = mPxParticleBaseSet.size(); for(PxU32 i=0; i < particleBaseCount; i++) { if (particles[i]->is()) nbActors++; } } if (types & PxActorTypeFlag::ePARTICLE_FLUID) { PxParticleBase*const* particles = mPxParticleBaseSet.getEntries(); const PxU32 particleBaseCount = mPxParticleBaseSet.size(); for(PxU32 i=0; i < particleBaseCount; i++) { if (particles[i]->is()) nbActors++; } } #endif #if PX_USE_CLOTH_API if (types & PxActorTypeFlag::eCLOTH) { nbActors += mPxCloths.size(); } #endif return nbActors; } PxU32 NpScene::getActors(PxActorTypeFlags types, PxActor** buffer, PxU32 bufferSize, PxU32 startIndex) const { NP_READ_CHECK(this); PxU32 writeCount = 0; PxU32 virtualIndex = 0; // PT: virtual index of actor, continuous across different actor containers. if(types & (PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC)) { const PxU32 size = mRigidActors.size(); for(PxU32 i=0; (i < size) && (writeCount < bufferSize); i++) { if ((types & PxActorTypeFlag::eRIGID_STATIC ) && mRigidActors[i]->is()) { if (virtualIndex >= startIndex) buffer[writeCount++] = mRigidActors[i]; virtualIndex++; } else if ((types & PxActorTypeFlag::eRIGID_DYNAMIC) && mRigidActors[i]->is()) { if (virtualIndex >= startIndex) buffer[writeCount++] = mRigidActors[i]; virtualIndex++; } } } #if PX_USE_PARTICLE_SYSTEM_API if (types & (PxActorTypeFlag::ePARTICLE_SYSTEM | PxActorTypeFlag::ePARTICLE_FLUID)) { const PxU32 size = mPxParticleBaseSet.size(); PxParticleBase*const* particles = mPxParticleBaseSet.getEntries(); for(PxU32 i=0; (i < size) && (writeCount < bufferSize); i++) { if ((types & PxActorTypeFlag::ePARTICLE_SYSTEM ) && particles[i]->is()) { if (virtualIndex >= startIndex) buffer[writeCount++] = particles[i]; virtualIndex++; } else if ((types & PxActorTypeFlag::ePARTICLE_FLUID) && particles[i]->is()) { if (virtualIndex >= startIndex) buffer[writeCount++] = particles[i]; virtualIndex++; } } } #endif #if PX_USE_CLOTH_API if (types & PxActorTypeFlag::eCLOTH) { const PxU32 size = mPxCloths.size(); PxCloth*const* clothList = mPxCloths.getEntries(); for(PxU32 i=0; (i < size) && (writeCount < bufferSize); i++) { if(virtualIndex>=startIndex) buffer[writeCount++] = clothList[i]; virtualIndex++; } } #endif return writeCount; } /////////////////////////////////////////////////////////////////////////////// PX_DEPRECATED const PxActiveTransform* NpScene::getActiveTransforms(PxU32& nbTransformsOut, PxClientID client) { NP_READ_CHECK(this); return mScene.getActiveTransforms(nbTransformsOut, client); } PxActor** NpScene::getActiveActors(PxU32& nbActorsOut, PxClientID client) { NP_READ_CHECK(this); return mScene.getActiveActors(nbActorsOut, client); } /////////////////////////////////////////////////////////////////////////////// PxU32 NpScene::getNbArticulations() const { NP_READ_CHECK(this); return mArticulations.size(); } PxU32 NpScene::getArticulations(PxArticulation** userBuffer, PxU32 bufferSize, PxU32 startIndex) const { NP_READ_CHECK(this); return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mArticulations.getEntries(), mArticulations.size()); } /////////////////////////////////////////////////////////////////////////////// PxU32 NpScene::getNbConstraints() const { NP_READ_CHECK(this); return mConstraints.size(); } PxU32 NpScene::getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const { NP_READ_CHECK(this); return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mConstraints.getEntries(), mConstraints.size()); } /////////////////////////////////////////////////////////////////////////////// const PxRenderBuffer& NpScene::getRenderBuffer() { if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) { // will be reading the Sc::Scene renderable which is getting written // during the sim, hence, avoid call while simulation is running. Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::getRenderBuffer() not allowed while simulation is running."); } return mRenderBuffer; } void NpScene::visualize() { NP_READ_CHECK(this); PX_PROFILE_ZONE("NpScene::visualize", getContextId()); mRenderBuffer.clear(); // clear last frame visualizations #if PX_ENABLE_DEBUG_VISUALIZATION if(getVisualizationParameter(PxVisualizationParameter::eSCALE) == 0.0f) return; Cm::RenderOutput out(mRenderBuffer); // Visualize scene axis const PxReal worldAxes = getVisualizationParameter(PxVisualizationParameter::eWORLD_AXES); if (worldAxes != 0) out << Cm::DebugBasis(PxVec3(worldAxes)); // Visualize articulations for(PxU32 i=0;i(mArticulations.getEntries()[i])->visualize(out, this); // Visualize rigid actors and rigid bodies PxRigidActor*const* rigidActors = mRigidActors.begin(); const PxU32 rigidActorCount = mRigidActors.size(); #if PX_USE_CLOTH_API // Visualize cloths for(PxU32 i=0;i(mPxCloths.getEntries()[i])->visualize(out, this); #endif for(PxU32 i=0; i < rigidActorCount; i++) { PxRigidActor* a = rigidActors[i]; if (a->getType() == PxActorType::eRIGID_DYNAMIC) static_cast(a)->visualize(out, this); else static_cast(a)->visualize(out, this); } // Visualize pruning structures const bool visStatic = getVisualizationParameter(PxVisualizationParameter::eCOLLISION_STATIC) != 0.0f; const bool visDynamic = getVisualizationParameter(PxVisualizationParameter::eCOLLISION_DYNAMIC) != 0.0f; //flushQueryUpdates(); // DE7834 if(visStatic && mSQManager.get(PruningIndex::eSTATIC).pruner()) mSQManager.get(PruningIndex::eSTATIC).pruner()->visualize(out, PxU32(PxDebugColor::eARGB_BLUE)); if(visDynamic && mSQManager.get(PruningIndex::eDYNAMIC).pruner()) mSQManager.get(PruningIndex::eDYNAMIC).pruner()->visualize(out, PxU32(PxDebugColor::eARGB_RED)); if(getVisualizationParameter(PxVisualizationParameter::eMBP_REGIONS) != 0.0f) { out << PxTransform(PxIdentity); const PxU32 nbRegions = mScene.getNbBroadPhaseRegions(); for(PxU32 i=0;i= 0.0f, "PxScene::setClothInterCollisionDistance: distance must be non-negative."); mScene.setClothInterCollisionDistance(distance); } PxF32 NpScene::getClothInterCollisionDistance() const { NP_READ_CHECK(this); return mScene.getClothInterCollisionDistance(); } void NpScene::setClothInterCollisionStiffness(PxF32 stiffness) { NP_WRITE_CHECK(this); PX_CHECK_AND_RETURN(stiffness >= 0.0f, "PxScene::setClothInterCollisionStiffness: stiffness must be non-negative."); return mScene.setClothInterCollisionStiffness(stiffness); } PxF32 NpScene::getClothInterCollisionStiffness() const { NP_READ_CHECK(this); return mScene.getClothInterCollisionStiffness(); } void NpScene::setClothInterCollisionNbIterations(PxU32 nbIterations) { NP_WRITE_CHECK(this); mScene.setClothInterCollisionNbIterations(nbIterations); } PxU32 NpScene::getClothInterCollisionNbIterations() const { NP_READ_CHECK(this); return mScene.getClothInterCollisionNbIterations(); } #endif /////////////////////////////////////////////////////////////////////////////// // Callbacks void NpScene::setSimulationEventCallback(PxSimulationEventCallback* callback, PxClientID client) { NP_WRITE_CHECK(this); mScene.setSimulationEventCallback(callback, client); } PxSimulationEventCallback* NpScene::getSimulationEventCallback(PxClientID client) const { NP_READ_CHECK(this); return mScene.getSimulationEventCallback(client); } void NpScene::setContactModifyCallback(PxContactModifyCallback* callback) { NP_WRITE_CHECK(this); mScene.setContactModifyCallback(callback); } PxContactModifyCallback* NpScene::getContactModifyCallback() const { NP_READ_CHECK(this); return mScene.getContactModifyCallback(); } void NpScene::setCCDContactModifyCallback(PxCCDContactModifyCallback* callback) { NP_WRITE_CHECK(this); mScene.setCCDContactModifyCallback(callback); } PxCCDContactModifyCallback* NpScene::getCCDContactModifyCallback() const { NP_READ_CHECK(this); return mScene.getCCDContactModifyCallback(); } void NpScene::setBroadPhaseCallback(PxBroadPhaseCallback* callback, PxClientID client) { NP_WRITE_CHECK(this); mScene.setBroadPhaseCallback(callback, client); } PxBroadPhaseCallback* NpScene::getBroadPhaseCallback(PxClientID client) const { NP_READ_CHECK(this); return mScene.getBroadPhaseCallback(client); } void NpScene::setCCDMaxPasses(PxU32 ccdMaxPasses) { NP_WRITE_CHECK(this); mScene.setCCDMaxPasses(ccdMaxPasses); } PxU32 NpScene::getCCDMaxPasses() const { NP_READ_CHECK(this); return mScene.getCCDMaxPasses(); } PxBroadPhaseType::Enum NpScene::getBroadPhaseType() const { NP_READ_CHECK(this); return mScene.getBroadPhaseType(); } bool NpScene::getBroadPhaseCaps(PxBroadPhaseCaps& caps) const { NP_READ_CHECK(this); return mScene.getBroadPhaseCaps(caps); } PxU32 NpScene::getNbBroadPhaseRegions() const { NP_READ_CHECK(this); return mScene.getNbBroadPhaseRegions(); } PxU32 NpScene::getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const { NP_READ_CHECK(this); return mScene.getBroadPhaseRegions(userBuffer, bufferSize, startIndex); } PxU32 NpScene::addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion) { PX_PROFILE_ZONE("BroadPhase.addBroadPhaseRegion", getContextId()); NP_WRITE_CHECK(this); PX_CHECK_MSG(region.bounds.isValid(), "PxScene::addBroadPhaseRegion(): invalid bounds provided!"); if(region.bounds.isEmpty()) { Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxScene::addBroadPhaseRegion(): region bounds are empty. Call will be ignored."); return 0xffffffff; } return mScene.addBroadPhaseRegion(region, populateRegion); } bool NpScene::removeBroadPhaseRegion(PxU32 handle) { NP_WRITE_CHECK(this); return mScene.removeBroadPhaseRegion(handle); } /////////////////////////////////////////////////////////////////////////////// // Filtering void NpScene::setFilterShaderData(const void* data, PxU32 dataSize) { NP_WRITE_CHECK(this); PX_CHECK_AND_RETURN(( ((dataSize == 0) && (data == NULL)) || ((dataSize > 0) && (data != NULL)) ), "PxScene::setFilterShaderData(): data pointer must not be NULL unless the specified data size is 0 too and vice versa."); mScene.setFilterShaderData(data, dataSize); } const void* NpScene::getFilterShaderData() const { NP_READ_CHECK(this); return mScene.getFilterShaderData(); } PxU32 NpScene::getFilterShaderDataSize() const { NP_READ_CHECK(this); return mScene.getFilterShaderDataSize(); } PxSimulationFilterShader NpScene::getFilterShader() const { NP_READ_CHECK(this); return mScene.getFilterShader(); } PxSimulationFilterCallback* NpScene::getFilterCallback() const { NP_READ_CHECK(this); return mScene.getFilterCallback(); } void NpScene::resetFiltering(PxActor& actor) { NP_WRITE_CHECK(this); PX_CHECK_AND_RETURN(NpActor::getAPIScene(actor) && (NpActor::getAPIScene(actor) == this), "PxScene::resetFiltering(): actor not in scene!"); switch(actor.getConcreteType()) { case PxConcreteType::eRIGID_STATIC: { NpRigidStatic& npStatic = static_cast(actor); npStatic.resetFiltering(npStatic.getScbRigidStaticFast(), NULL, 0); } break; case PxConcreteType::eRIGID_DYNAMIC: { NpRigidDynamic& npDynamic = static_cast(actor); if (npDynamic.resetFiltering(npDynamic.getScbBodyFast(), NULL, 0)) npDynamic.wakeUpInternal(); } break; case PxConcreteType::eARTICULATION_LINK: { NpArticulationLink& npLink = static_cast(actor); if (npLink.resetFiltering(npLink.getScbBodyFast(), NULL, 0)) npLink.getRoot().wakeUpInternal(false, true); } break; #if PX_USE_PARTICLE_SYSTEM_API case PxConcreteType::ePARTICLE_SYSTEM: { NpParticleSystem& npSystem = static_cast(actor); npSystem.getScbParticleSystem().resetFiltering(); } break; case PxConcreteType::ePARTICLE_FLUID: { NpParticleFluid& npFluid = static_cast(actor); npFluid.getScbParticleSystem().resetFiltering(); } break; #endif default: Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxScene::resetFiltering(): only PxParticleBase and PxRigidActor support this operation!"); } } void NpScene::resetFiltering(PxRigidActor& actor, PxShape*const* shapes, PxU32 shapeCount) { NP_WRITE_CHECK(this); PX_CHECK_AND_RETURN(NpActor::getAPIScene(actor) && (NpActor::getAPIScene(actor) == this), "PxScene::resetFiltering(): actor not in scene!"); PX_SIMD_GUARD; switch(actor.getConcreteType()) { case PxConcreteType::eRIGID_STATIC: { NpRigidStatic& npStatic = static_cast(actor); npStatic.resetFiltering(npStatic.getScbRigidStaticFast(), shapes, shapeCount); } break; case PxConcreteType::eRIGID_DYNAMIC: { NpRigidDynamic& npDynamic = static_cast(actor); if (npDynamic.resetFiltering(npDynamic.getScbBodyFast(), shapes, shapeCount)) npDynamic.wakeUpInternal(); } break; case PxConcreteType::eARTICULATION_LINK: { NpArticulationLink& npLink = static_cast(actor); if (npLink.resetFiltering(npLink.getScbBodyFast(), shapes, shapeCount)) npLink.getRoot().wakeUpInternal(false, true); } break; } } /////////////////////////////////////////////////////////////////////////////// PxPhysics& NpScene::getPhysics() { return NpPhysics::getInstance(); } void NpScene::updateDirtyShaders() { PX_PROFILE_ZONE("Sim.updateDirtyShaders", getContextId()); // this should continue to be done in the Np layer even after SC has taken over // all vital simulation functions, because it needs to complete before simulate() // returns to the application // However, the implementation needs fixing so that it does work proportional to // the number of dirty shaders PxConstraint*const* constraints = mConstraints.getEntries(); for(PxU32 i=0;i(constraints[i])->updateConstants(); } } /////////////////////////////////////////////////////////////////////////////// void NpScene::simulateOrCollide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation, const char* invalidCallMsg, Sc::SimulationStage::Enum simStage) { PX_SIMD_GUARD; { // write guard must end before simulation kicks off worker threads // otherwise the simulation callbacks could overlap with this function // and perform API reads,triggering an error NP_WRITE_CHECK(this); PX_PROFILE_START_CROSSTHREAD("Basic.simulate", getContextId()); if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) { //fetchResult doesn't get called Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, invalidCallMsg); return; } PX_CHECK_AND_RETURN(elapsedTime > 0, "PxScene::collide/simulate: The elapsed time must be positive!"); PX_CHECK_AND_RETURN((reinterpret_cast(scratchBlock)&15) == 0, "PxScene::simulate: scratch block must be 16-byte aligned!"); PX_CHECK_AND_RETURN((scratchBlockSize&16383) == 0, "PxScene::simulate: scratch block size must be a multiple of 16K"); #if PX_SUPPORT_PVD //signal the frame is starting. mScene.getScenePvdClient().frameStart(elapsedTime); #endif #if PX_ENABLE_DEBUG_VISUALIZATION visualize(); #endif updateDirtyShaders(); #if PX_SUPPORT_PVD mScene.getScenePvdClient().updateJoints(); #endif mScene.getScScene().setScratchBlock(scratchBlock, scratchBlockSize); mElapsedTime = elapsedTime; if (simStage == Sc::SimulationStage::eCOLLIDE) mScene.getScScene().setElapsedTime(elapsedTime); mControllingSimulation = controlSimulation; //sync all the material events NpPhysics& physics = static_cast(this->getPhysics()); NpMaterialManager& manager = physics.getMaterialManager(); NpMaterial** materials = manager.getMaterials(); mScene.updateLowLevelMaterial(materials); #if PX_USE_PARTICLE_SYSTEM_API mScene.preSimulateUpdateAppThread(elapsedTime); #endif setSimulationStage(simStage); mScene.setPhysicsBuffering(true); mHasSimulatedOnce = true; } { PX_PROFILE_ZONE("Sim.taskFrameworkSetup", getContextId()); if (controlSimulation) { { PX_PROFILE_ZONE("Sim.resetDependencies", getContextId()); // Only reset dependencies, etc if we own the TaskManager. Will be false // when an NpScene is controlled by an APEX scene. mTaskManager->resetDependencies(); } mTaskManager->startSimulation(); } if (simStage == Sc::SimulationStage::eCOLLIDE) { mCollisionCompletion.setContinuation(*mTaskManager, completionTask); mSceneCollide.setContinuation(&mCollisionCompletion); //Initialize scene completion task mSceneCompletion.setContinuation(*mTaskManager, NULL); } else { mSceneCompletion.setContinuation(*mTaskManager, completionTask); mSceneExecution.setContinuation(*mTaskManager, &mSceneCompletion); } #if PX_SUPPORT_GPU_PHYSX //workaround to prevent premature launching of gpu launch task if (PxGpuDispatcher* gpuDispatcher = getGpuDispatcher()) { //GPU pre-launch task must complete before scene completion can run gpuDispatcher->addPreLaunchDependent(mSceneCompletion); } #endif if (simStage == Sc::SimulationStage::eCOLLIDE) { mCollisionCompletion.removeReference(); mSceneCollide.removeReference(); } else { mSceneCompletion.removeReference(); mSceneExecution.removeReference(); } } } void NpScene::simulate(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation) { simulateOrCollide( elapsedTime, completionTask, scratchBlock, scratchBlockSize, controlSimulation, "PxScene::simulate: Simulation is still processing last simulate call, you should call fetchResults()!", Sc::SimulationStage::eADVANCE); } void NpScene::advance( physx::PxBaseTask* completionTask) { NP_WRITE_CHECK(this); //issue error if advance() doesn't get called between fetchCollision() and fetchResult() if(getSimulationStage() != Sc::SimulationStage::eFETCHCOLLIDE) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::advance: advance() called illegally! advance() needed to be called after fetchCollision() and before fetchResult()!!"); return; } //apply buffering for forces, velocities, kinematic targets and wake-up events mScene.syncWriteThroughProperties(); //if mSimulateStage == eFETCHCOLLIDE, which means collide() has been kicked off and finished running, we can run advance() safely { //change the mSimulateStaget to eADVANCE to indicate the next stage to run is fetchResult() setSimulationStage(Sc::SimulationStage::eADVANCE); { PX_PROFILE_ZONE("Sim.taskFrameworkSetup", getContextId()); mSceneCompletion.setDependent(completionTask); mSceneAdvance.setContinuation(*mTaskManager, &mSceneCompletion); mSceneCompletion.removeReference(); mSceneAdvance.removeReference(); } } } void NpScene::collide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation) { simulateOrCollide( elapsedTime, completionTask, scratchBlock, scratchBlockSize, controlSimulation, "PxScene::collide: collide() called illegally! If it isn't the first frame, collide() needed to be called between fetchResults() and fetchCollision(). Otherwise, collide() needed to be called before fetchCollision()", Sc::SimulationStage::eCOLLIDE); } bool NpScene::checkResultsInternal(bool block) { PX_PROFILE_ZONE("Basic.checkResults", getContextId()); return mPhysicsDone.wait(block ? Ps::Sync::waitForever : 0); } bool NpScene::checkCollisionInternal(bool block) { PX_PROFILE_ZONE("Basic.checkCollision", getContextId()); return mCollisionDone.wait(block ? Ps::Sync::waitForever : 0); } bool NpScene::checkResults(bool block) { return checkResultsInternal(block); } bool NpScene::checkCollision(bool block) { return checkCollisionInternal(block); } void NpScene::fireOutOfBoundsCallbacks() { PX_PROFILE_ZONE("Sim.fireOutOfBoundsCallbacks", getContextId()); // Fire broad-phase callbacks { Sc::Scene& scene = mScene.getScScene(); using namespace physx::Sc; bool outputWarning = scene.fireOutOfBoundsCallbacks(); // Aggregates { void** outAgg = scene.getOutOfBoundsAggregates(); const PxU32 nbOut1 = scene.getNbOutOfBoundsAggregates(); for(PxU32 i=0;i(outAgg[i]); NpAggregate* np = static_cast(px); if(np->getScbAggregate().getControlState()==Scb::ControlState::eREMOVE_PENDING) continue; // PT: used to avoid calling the callback twice for the same client bool flags[PX_MAX_CLIENTS]; PxMemZero(flags, PX_MAX_CLIENTS*sizeof(bool)); PxU32 nbActors = np->getCurrentSizeFast(); for(PxU32 j=0;jgetActorFast(j); const PxClientID clientID = pxActor->getOwnerClient(); if(!flags[clientID]) { flags[clientID] = true; PxBroadPhaseCallback* cb = scene.getBroadPhaseCallback(clientID); if(cb) { cb->onObjectOutOfBounds(*px); } else { outputWarning = true; } } } } scene.clearOutOfBoundsAggregates(); } if(outputWarning) Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "At least one object is out of the broadphase bounds. To manage those objects, define a PxBroadPhaseCallback for each used client."); } } bool NpScene::fetchCollision(bool block) { if(getSimulationStage() != Sc::SimulationStage::eCOLLIDE) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchCollision: fetchCollision() should be called after collide() and before advance()!"); return false; } //if collision isn't finish running (and block is false), then return false if(!checkCollisionInternal(block)) return false; // take write check *after* collision() finished, otherwise // we will block fetchCollision() from using the API NP_WRITE_CHECK_NOREENTRY(this); setSimulationStage(Sc::SimulationStage::eFETCHCOLLIDE); return true; } class SqRefFinder: public Sc::SqRefFinder { public: virtual Sq::PrunerHandle find(const PxRigidBody* body, const PxShape* shape) { const Sq::PrunerData prunerdata = NpActor::getShapeManager(*body)->findSceneQueryData(*static_cast(shape)); return Sq::getPrunerHandle(prunerdata); } private: }; // The order of the following operations is important! // 1. Process object deletions which were carried out while the simulation was running (since these effect contact and trigger reports) // 2. Write contact reports to global stream (taking pending deletions into account), clear some simulation buffers (deleted objects etc.), ... // 3. Send reports which have to be done before the data is synced (contact & trigger reports etc.) such that the user gets the old state. // 4. Mark the simulation as not running internally to allow reading data which should not be read otherwise // 5. Synchronize the simulation and user state // 6. Fire callbacks which need to reflect the synchronized object state void NpScene::fetchResultsPreContactCallbacks() { #if PX_SUPPORT_PVD mScene.getScenePvdClient().updateContacts(); #endif mScene.prepareOutOfBoundsCallbacks(); mScene.processPendingRemove(); mScene.endSimulation(); { PX_PROFILE_ZONE("Sim.fireCallbacksPreSync", getContextId()); fireOutOfBoundsCallbacks(); // fire out-of-bounds callbacks mScene.fireBrokenConstraintCallbacks(); mScene.fireTriggerCallbacks(); } } void NpScene::fetchResultsPostContactCallbacks() { mScene.postCallbacksPreSync(); mScene.syncEntireScene(); // double buffering SqRefFinder sqRefFinder; mScene.getScScene().syncSceneQueryBounds(mSQManager.getDynamicBoundsSync(), sqRefFinder); mSQManager.afterSync(!(getFlagsFast()&PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT)); #if PX_DEBUG && 0 mSQManager.validateSimUpdates(); #endif #if PX_SUPPORT_PVD mScene.getScenePvdClient().updateSceneQueries(); getSingleSqCollector().clear(); getBatchedSqCollector().clear(); #endif // fire sleep and wake-up events // we do this after buffer-swapping so that the events have the new state { PX_PROFILE_ZONE("Sim.fireCallbacksPostSync", getContextId()); mScene.fireCallBacksPostSync(); } mScene.postReportsCleanup(); // build the list of active transforms { PX_PROFILE_ZONE("Sim.buildActiveTransforms", getContextId()); if (mScene.getFlags() & PxSceneFlag::eENABLE_ACTIVETRANSFORMS) mScene.buildActiveTransforms(); if (mScene.getFlags() & PxSceneFlag::eENABLE_ACTIVE_ACTORS) mScene.buildActiveActors(); } mRenderBuffer.append(mScene.getScScene().getRenderBuffer()); PX_ASSERT(getSimulationStage() != Sc::SimulationStage::eCOMPLETE); if (mControllingSimulation) { mTaskManager->stopSimulation(); } setSimulationStage(Sc::SimulationStage::eCOMPLETE); mPhysicsDone.reset(); // allow Physics to run again mCollisionDone.reset(); } bool NpScene::fetchResults(bool block, PxU32* errorState) { if(getSimulationStage() != Sc::SimulationStage::eADVANCE) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchResults: fetchResults() called illegally! It must be called after advance() or simulate()"); return false; } if(!checkResultsInternal(block)) return false; { PX_SIMD_GUARD; // take write check *after* simulation has finished, otherwise // we will block simulation callbacks from using the API // disallow re-entry to detect callbacks making write calls NP_WRITE_CHECK_NOREENTRY(this); // we use cross thread profile here, to show the event in cross thread view // PT: TODO: why do we want to show it in the cross thread view? PX_PROFILE_START_CROSSTHREAD("Basic.fetchResults", getContextId()); PX_PROFILE_ZONE("Sim.fetchResults", getContextId()); fetchResultsPreContactCallbacks(); { // PT: TODO: why a cross-thread event here? PX_PROFILE_START_CROSSTHREAD("Basic.processCallbacks", getContextId()); mScene.fireQueuedContactCallbacks(); PX_PROFILE_STOP_CROSSTHREAD("Basic.processCallbacks", getContextId()); } fetchResultsPostContactCallbacks(); PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchResults", getContextId()); PX_PROFILE_STOP_CROSSTHREAD("Basic.simulate", getContextId()); if(errorState) *errorState = 0; } #if PX_SUPPORT_PVD mScene.getScenePvdClient().frameEnd(); #endif return true; } bool NpScene::fetchResultsStart(const PxContactPairHeader*& contactPairs, PxU32& nbContactPairs, bool block) { if (getSimulationStage() != Sc::SimulationStage::eADVANCE) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PXScene::fetchResultsStart: fetchResultsStart() called illegally! It must be called after advance() or simulate()"); return false; } if (!checkResultsInternal(block)) return false; PX_SIMD_GUARD; NP_WRITE_CHECK(this); // we use cross thread profile here, to show the event in cross thread view PX_PROFILE_START_CROSSTHREAD("Basic.fetchResults", getContextId()); PX_PROFILE_ZONE("Sim.fetchResultsStart", getContextId()); fetchResultsPreContactCallbacks(); const Ps::Array& pairs = mScene.getQueuedContactPairHeaders(); nbContactPairs = pairs.size(); contactPairs = pairs.begin(); mBetweenFetchResults = true; return true; } void NpContactCallbackTask::setData(NpScene* scene, const PxContactPairHeader* contactPairHeaders, const uint32_t nbContactPairHeaders) { mScene = scene; mContactPairHeaders = contactPairHeaders; mNbContactPairHeaders = nbContactPairHeaders; } void NpContactCallbackTask::run() { mScene->lockRead(); for (uint32_t i = 0; i < mNbContactPairHeaders; ++i) { const physx::PxContactPairHeader& pairHeader = mContactPairHeaders[i]; physx::PxRigidActor* aActor = pairHeader.actors[0]; physx::PxRigidActor* bActor = pairHeader.actors[1]; physx::PxClientID clientActor0 = aActor->getOwnerClient(); physx::PxClientID clientActor1 = bActor->getOwnerClient(); physx::PxSimulationEventCallback* aCallback = mScene->getSimulationEventCallback(clientActor0); physx::PxSimulationEventCallback* bCallback = mScene->getSimulationEventCallback(clientActor1); uint8_t actor0ClientBehaviorFlags = aActor->getClientBehaviorFlags();//aPair->getActorAClientBehavior(); uint8_t actor1ClientBehaviorFlags = bActor->getClientBehaviorFlags(); if (aCallback && ( (clientActor0 == clientActor1) //easy common case: the same client owns both shapes || ( //else actor1 has a different owner -- see if we can still send this pair to the client of actor0: (actor0ClientBehaviorFlags & physx::PxClientBehaviorFlag::eREPORT_FOREIGN_OBJECTS_TO_CONTACT_NOTIFY)//this client accepts foreign objects && (actor1ClientBehaviorFlags & physx::PxActorClientBehaviorFlag::eREPORT_TO_FOREIGN_CLIENTS_CONTACT_NOTIFY)//this actor can be sent to foreign client ) )) aCallback->onContact(pairHeader, pairHeader.pairs, pairHeader.nbPairs); if ( (clientActor0 != clientActor1) //don't call the same client twice && bCallback && (actor1ClientBehaviorFlags & physx::PxClientBehaviorFlag::eREPORT_FOREIGN_OBJECTS_TO_CONTACT_NOTIFY)//this client accepts foreign objects && (actor0ClientBehaviorFlags & physx::PxActorClientBehaviorFlag::eREPORT_TO_FOREIGN_CLIENTS_CONTACT_NOTIFY)//this actor can be sent to foreign client ) bCallback->onContact(pairHeader, pairHeader.pairs, pairHeader.nbPairs); } mScene->unlockRead(); } void NpScene::processCallbacks(physx::PxBaseTask* continuation) { PX_PROFILE_START_CROSSTHREAD("Basic.processCallbacks", getContextId()); PX_PROFILE_ZONE("Sim.processCallbacks", getContextId()); //ML: because Apex destruction callback isn't thread safe so that we make this run single thread first const Ps::Array& pairs = mScene.getQueuedContactPairHeaders(); const PxU32 nbPairs = pairs.size(); const PxContactPairHeader* contactPairs = pairs.begin(); const PxU32 nbToProcess = 256; Cm::FlushPool* flushPool = mScene.getScScene().getFlushPool(); for (PxU32 i = 0; i < nbPairs; i += nbToProcess) { NpContactCallbackTask* task = PX_PLACEMENT_NEW(flushPool->allocate(sizeof(NpContactCallbackTask)), NpContactCallbackTask)(); task->setData(this, contactPairs+i, PxMin(nbToProcess, nbPairs - i)); task->setContinuation(continuation); task->removeReference(); } } void NpScene::fetchResultsFinish(PxU32* errorState) { { PX_SIMD_GUARD; PX_PROFILE_STOP_CROSSTHREAD("Basic.processCallbacks", getContextId()); PX_PROFILE_ZONE("Basic.fetchResultsFinish", getContextId()); mBetweenFetchResults = false; NP_WRITE_CHECK(this); fetchResultsPostContactCallbacks(); if (errorState) *errorState = 0; PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchResults", getContextId()); PX_PROFILE_STOP_CROSSTHREAD("Basic.simulate", getContextId()); } #if PX_SUPPORT_PVD mScene.getScenePvdClient().frameEnd(); #endif } void NpScene::flushSimulation(bool sendPendingReports) { PX_PROFILE_ZONE("API.flushSimulation", getContextId()); NP_WRITE_CHECK_NOREENTRY(this); PX_SIMD_GUARD; if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::flushSimulation(): This call is not allowed while the simulation is running. Call will be ignored"); return; } mScene.flush(sendPendingReports); mSQManager.flushMemory(); //!!! TODO: Shrink all NpObject lists? } void NpScene::flushQueryUpdates() { // DS: how do we profile const methods?????? PX_PROFILE_ZONE("API.flushQueryUpdates", getContextId()); NP_READ_CHECK(this); PX_SIMD_GUARD; if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::flushQueryUpdates(): This call is not allowed while the simulation is running. Call will be ignored"); return; } mSQManager.flushUpdates(); } /* Replaces finishRun() with the addition of appropriate thread sync(pulled out of PhysicsThread()) Note: this function can be called from the application thread or the physics thread, depending on the scene flags. */ void NpScene::executeScene(PxBaseTask* continuation) { mScene.simulate(mElapsedTime, continuation); } void NpScene::executeCollide(PxBaseTask* continuation) { mScene.collide(mElapsedTime, continuation); } void NpScene::executeAdvance(PxBaseTask* continuation) { mScene.advance(mElapsedTime, continuation); } /////////////////////////////////////////////////////////////////////////////// void NpScene::addMaterial(const NpMaterial& mat) { mScene.addMaterial(mat.getScMaterial()); } void NpScene::updateMaterial(const NpMaterial& mat) { //PxU32 index = mat.getTableIndex(); mScene.updateMaterial(mat.getScMaterial()); } void NpScene::removeMaterial(const NpMaterial& mat) { //PxU32 index = mat.getTableIndex(); mScene.removeMaterial(mat.getScMaterial()); } /////////////////////////////////////////////////////////////////////////////// void NpScene::setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) { NP_WRITE_CHECK(this); PX_CHECK_AND_RETURN((group1 < PX_MAX_DOMINANCE_GROUP && group2 < PX_MAX_DOMINANCE_GROUP), "PxScene::setDominanceGroupPair: invalid params! Groups must be <= 31!"); //can't change matrix diagonal PX_CHECK_AND_RETURN(group1 != group2, "PxScene::setDominanceGroupPair: invalid params! Groups must be unequal! Can't change matrix diagonal!"); PX_CHECK_AND_RETURN( ((dominance.dominance0) == 1.0f && (dominance.dominance1 == 1.0f)) || ((dominance.dominance0) == 1.0f && (dominance.dominance1 == 0.0f)) || ((dominance.dominance0) == 0.0f && (dominance.dominance1 == 1.0f)) , "PxScene::setDominanceGroupPair: invalid params! dominance must be one of (1,1), (1,0), or (0,1)!"); mScene.setDominanceGroupPair(group1, group2, dominance); } PxDominanceGroupPair NpScene::getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const { NP_READ_CHECK(this); PX_CHECK_AND_RETURN_VAL((group1 < PX_MAX_DOMINANCE_GROUP && group2 < PX_MAX_DOMINANCE_GROUP), "PxScene::getDominanceGroupPair: invalid params! Groups must be <= 31!", PxDominanceGroupPair(PxU8(1u), PxU8(1u))); return mScene.getDominanceGroupPair(group1, group2); } /////////////////////////////////////////////////////////////////////////////// #if PX_USE_PARTICLE_SYSTEM_API void NpScene::addParticleSystem(NpParticleSystem& system) { PX_PROFILE_ZONE("API.addParticleSystem", getContextId()); PX_CHECK_AND_RETURN(mScene.getScScene().getParticleContext(), "PxRegisterParticles needs to be called before scene creation. PxParticleSystem not added to scene."); mScene.addParticleSystem(system.getScbParticleSystem()); mPxParticleBaseSet.insert(&system); updatePhysXIndicator(); } void NpScene::removeParticleSystem(NpParticleSystem& system) { PX_PROFILE_ZONE("API.removeParticleSystem", getContextId()); PX_ASSERT(system.getNpScene() == this); PX_CHECK_AND_RETURN(mScene.getScScene().getParticleContext(), "PxRegisterParticles needs to be called before scene creation. PxParticleFluid not added to scene."); mScene.removeParticleSystem(system.getScbParticleSystem(), false); removeFromParticleBaseList(system); updatePhysXIndicator(); } void NpScene::addParticleFluid(NpParticleFluid& fluid) { PX_PROFILE_ZONE("API.addParticleFluid", getContextId()); mScene.addParticleSystem(fluid.getScbParticleSystem()); mPxParticleBaseSet.insert(&fluid); updatePhysXIndicator(); } void NpScene::removeParticleFluid(NpParticleFluid& fluid) { PX_PROFILE_ZONE("API.removeParticleFluid", getContextId()); PX_ASSERT(fluid.getNpScene() == this); mScene.removeParticleSystem(fluid.getScbParticleSystem(), false); removeFromParticleBaseList(fluid); updatePhysXIndicator(); } #endif // PX_USE_PARTICLE_SYSTEM_API /////////////////////////////////////////////////////////////////////////////// #if PX_USE_CLOTH_API void NpScene::addCloth(NpCloth& cloth) { PX_PROFILE_ZONE("API.addCloth", getContextId()); mScene.addCloth(cloth.getScbCloth()); mPxCloths.insert(&cloth); updatePhysXIndicator(); } void NpScene::removeCloth(NpCloth& cloth) { PX_PROFILE_ZONE("API.removeCloth", getContextId()); PX_ASSERT(NpActor::getAPIScene(cloth) == this); mScene.removeCloth(cloth.getScbCloth()); removeFromClothList(cloth); updatePhysXIndicator(); } #endif // PX_USE_CLOTH_API /////////////////////////////////////////////////////////////////////////////// #if PX_SUPPORT_GPU_PHYSX void NpScene::updatePhysXIndicator() { Ps::IntBool isGpu = 0; #if PX_USE_PARTICLE_SYSTEM_API PxParticleBase*const* particleBaseList = mPxParticleBaseSet.getEntries(); for (PxU32 i = 0; !isGpu && i < mPxParticleBaseSet.size(); i++) { NpParticleSystem* particles = (NpParticleSystem*)particleBaseList[i]->is(); NpParticleFluid* fluid = (NpParticleFluid*)particleBaseList[i]->is(); isGpu |= particles && particles->getScbParticleSystem().getScParticleSystem().isGpu(); isGpu |= fluid && fluid->getScbParticleSystem().getScParticleSystem().isGpu(); } #endif #if PX_USE_CLOTH_API PxCloth*const* clothList = mPxCloths.getEntries(); for (PxU32 i = 0; !isGpu && i < mPxCloths.size(); i++) { NpCloth* pCloth = (NpCloth*)clothList[i]->is(); isGpu = pCloth->getScbCloth().getScCloth().isGpu(); } #endif mPhysXIndicator.setIsGpu(isGpu != 0); } #endif //PX_SUPPORT_GPU_PHYSX /////////////////////////////////////////////////////////////////////////////// PxVolumeCache* NpScene::createVolumeCache(PxU32 maxStaticShapes, PxU32 maxDynamicShapes) { NpVolumeCache* cache = PX_NEW(NpVolumeCache)(&mSQManager, maxStaticShapes, maxDynamicShapes); mVolumeCaches.insert(cache); return cache; } void NpScene::releaseVolumeCache(NpVolumeCache* volumeCache) { bool found = mVolumeCaches.erase(volumeCache); PX_UNUSED(found); PX_ASSERT_WITH_MESSAGE(found, "volume cache not found in releaseVolumeCache"); PX_DELETE(static_cast(volumeCache)); } void NpScene::setDynamicTreeRebuildRateHint(PxU32 dynamicTreeRebuildRateHint) { PX_CHECK_AND_RETURN((dynamicTreeRebuildRateHint >= 4), "PxScene::setDynamicTreeRebuildRateHint(): Param has to be >= 4!"); mSQManager.setDynamicTreeRebuildRateHint(dynamicTreeRebuildRateHint); } PxU32 NpScene::getDynamicTreeRebuildRateHint() const { NP_READ_CHECK(this); return mSQManager.getDynamicTreeRebuildRateHint(); } void NpScene::forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure) { PX_PROFILE_ZONE("API.forceDynamicTreeRebuild", getContextId()); NP_WRITE_CHECK(this); PX_SIMD_GUARD; mSQManager.forceDynamicTreeRebuild(rebuildStaticStructure, rebuildDynamicStructure); } void NpScene::setSolverBatchSize(PxU32 solverBatchSize) { NP_WRITE_CHECK(this); mScene.setSolverBatchSize(solverBatchSize); } PxU32 NpScene::getSolverBatchSize(void) const { NP_READ_CHECK(this); // get from our local copy return mScene.getSolverBatchSize(); } /////////////////////////////////////////////////////////////////////////////// bool NpScene::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) { NP_WRITE_CHECK(this); PX_CHECK_AND_RETURN_VAL(PxIsFinite(value), "PxScene::setVisualizationParameter: value is not valid.", false); if (param >= PxVisualizationParameter::eNUM_VALUES) { Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "setVisualizationParameter: parameter out of range."); return false; } else if (value < 0.0f) { Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "setVisualizationParameter: value must be larger or equal to 0."); return false; } else { mScene.setVisualizationParameter(param, value); return true; } } PxReal NpScene::getVisualizationParameter(PxVisualizationParameter::Enum param) const { if (param < PxVisualizationParameter::eNUM_VALUES) return mScene.getVisualizationParameter(param); else Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "getVisualizationParameter: param is not an enum."); return 0.0f; } void NpScene::setVisualizationCullingBox(const PxBounds3& box) { NP_WRITE_CHECK(this); PX_CHECK_MSG(box.isValid(), "PxScene::setVisualizationCullingBox(): invalid bounds provided!"); mScene.setVisualizationCullingBox(box); } PxBounds3 NpScene::getVisualizationCullingBox() const { NP_READ_CHECK(this); const PxBounds3& bounds = mScene.getVisualizationCullingBox(); PX_ASSERT(bounds.isValid()); return bounds; } void NpScene::setNbContactDataBlocks(PxU32 numBlocks) { PX_CHECK_AND_RETURN((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), "PxScene::setNbContactDataBlock: This call is not allowed while the simulation is running. Call will be ignored!"); mScene.getScScene().setNbContactDataBlocks(numBlocks); } PxU32 NpScene::getNbContactDataBlocksUsed() const { PX_CHECK_AND_RETURN_VAL((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), "PxScene::getNbContactDataBlocksUsed: This call is not allowed while the simulation is running. Returning 0.", 0); return mScene.getScScene().getNbContactDataBlocksUsed(); } PxU32 NpScene::getMaxNbContactDataBlocksUsed() const { PX_CHECK_AND_RETURN_VAL((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), "PxScene::getMaxNbContactDataBlocksUsed: This call is not allowed while the simulation is running. Returning 0.", 0); return mScene.getScScene().getMaxNbContactDataBlocksUsed(); } PxU32 NpScene::getTimestamp() const { return mScene.getScScene().getTimeStamp(); } PxU32 NpScene::getSceneQueryStaticTimestamp() const { return mSQManager.get(PruningIndex::eSTATIC).timestamp(); } PxCpuDispatcher* NpScene::getCpuDispatcher() const { return getTaskManager()->getCpuDispatcher(); } PxGpuDispatcher* NpScene::getGpuDispatcher() const { return getTaskManager()->getGpuDispatcher(); } PxPruningStructureType::Enum NpScene::getStaticStructure() const { return mSQManager.get(PruningIndex::eSTATIC).type(); } PxPruningStructureType::Enum NpScene::getDynamicStructure() const { return mSQManager.get(PruningIndex::eDYNAMIC).type(); } PxReal NpScene::getFrictionOffsetThreshold() const { return mScene.getScScene().getFrictionOffsetThreshold(); } PxU32 NpScene::getContactReportStreamBufferSize() const { return mScene.getScScene().getDefaultContactReportStreamBufferSize(); } #if PX_CHECKED void NpScene::checkPositionSanity(const PxRigidActor& a, const PxTransform& pose, const char* fnName) const { if(!mSanityBounds.contains(pose.p)) Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "%s: actor pose for %lp is outside sanity bounds\n", fnName, &a); } #endif namespace { struct ThreadReadWriteCount { PxU8 readDepth; // depth of re-entrant reads PxU8 writeDepth; // depth of re-entrant writes PxU8 readLockDepth; // depth of read-locks PxU8 writeLockDepth; // depth of write-locks }; } #if NP_ENABLE_THREAD_CHECKS NpScene::StartWriteResult::Enum NpScene::startWrite(bool allowReentry) { PX_COMPILE_TIME_ASSERT(sizeof(ThreadReadWriteCount) == 4); if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) { ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); if (mBetweenFetchResults) return StartWriteResult::eIN_FETCHRESULTS; // ensure we already have the write lock return localCounts.writeLockDepth > 0 ? StartWriteResult::eOK : StartWriteResult::eNO_LOCK; } { ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); StartWriteResult::Enum result; if (mBetweenFetchResults) result = StartWriteResult::eIN_FETCHRESULTS; // check we are the only thread reading (allows read->write order on a single thread) and no other threads are writing else if (mConcurrentReadCount != localCounts.readDepth || mConcurrentWriteCount != localCounts.writeDepth) result = StartWriteResult::eRACE_DETECTED; else result = StartWriteResult::eOK; // increment shared write counter Ps::atomicIncrement(&mConcurrentWriteCount); // in the normal case (re-entry is allowed) then we simply increment // the writeDepth by 1, otherwise (re-entry is not allowed) increment // by 2 to force subsequent writes to fail by creating a mismatch between // the concurrent write counter and the local counter, any value > 1 will do localCounts.writeDepth += allowReentry ? 1 : 2; TlsSet(mThreadReadWriteDepth, PxUnionCast(localCounts)); if (result != StartWriteResult::eOK) Ps::atomicIncrement(&mConcurrentErrorCount); return result; } } void NpScene::stopWrite(bool allowReentry) { if (!(mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) { Ps::atomicDecrement(&mConcurrentWriteCount); // decrement depth of writes for this thread ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); // see comment in startWrite() if (allowReentry) localCounts.writeDepth--; else localCounts.writeDepth-=2; TlsSet(mThreadReadWriteDepth, PxUnionCast(localCounts)); } } bool NpScene::startRead() const { if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) { ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); // ensure we already have the write or read lock return localCounts.writeLockDepth > 0 || localCounts.readLockDepth > 0; } else { Ps::atomicIncrement(&mConcurrentReadCount); // update current threads read depth ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); localCounts.readDepth++; TlsSet(mThreadReadWriteDepth, PxUnionCast(localCounts)); // success if the current thread is already performing a write (API re-entry) or no writes are in progress bool success = (localCounts.writeDepth > 0 || mConcurrentWriteCount == 0); if (!success) Ps::atomicIncrement(&mConcurrentErrorCount); return success; } } void NpScene::stopRead() const { if (!(mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) { Ps::atomicDecrement(&mConcurrentReadCount); // update local threads read depth ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); localCounts.readDepth--; TlsSet(mThreadReadWriteDepth, PxUnionCast(localCounts)); } } #else NpScene::StartWriteResult::Enum NpScene::startWrite(bool) { PX_ASSERT(0); return NpScene::StartWriteResult::eOK; } void NpScene::stopWrite(bool) {} bool NpScene::startRead() const { PX_ASSERT(0); return false; } void NpScene::stopRead() const {} #endif // NP_ENABLE_THREAD_CHECKS void NpScene::lockRead(const char* /*file*/, PxU32 /*line*/) { // increment this threads read depth ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); localCounts.readLockDepth++; TlsSet(mThreadReadWriteDepth, PxUnionCast(localCounts)); // if we are the current writer then do nothing (allow reading from threads with write ownership) if (mCurrentWriter == Thread::getId()) return; // only lock on first read if (localCounts.readLockDepth == 1) mRWLock.lockReader(); } void NpScene::unlockRead() { // increment this threads read depth ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); if (localCounts.readLockDepth < 1) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::unlockRead() called without matching call to PxScene::lockRead(), behaviour will be undefined."); return; } localCounts.readLockDepth--; TlsSet(mThreadReadWriteDepth, PxUnionCast(localCounts)); // if we are the current writer then do nothing (allow reading from threads with write ownership) if (mCurrentWriter == Thread::getId()) return; // only unlock on last read if (localCounts.readLockDepth == 0) mRWLock.unlockReader(); } void NpScene::lockWrite(const char* file, PxU32 line) { // increment this threads write depth ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); if (localCounts.writeLockDepth == 0 && localCounts.readLockDepth > 0) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, file?file:__FILE__, file?int(line):__LINE__, "PxScene::lockWrite() detected after a PxScene::lockRead(), lock upgrading is not supported, behaviour will be undefined."); return; } localCounts.writeLockDepth++; TlsSet(mThreadReadWriteDepth, PxUnionCast(localCounts)); // only lock on first call if (localCounts.writeLockDepth == 1) mRWLock.lockWriter(); PX_ASSERT(mCurrentWriter == 0 || mCurrentWriter == Thread::getId()); // set ourselves as the current writer mCurrentWriter = Thread::getId(); } void NpScene::unlockWrite() { // increment this thread's write depth ThreadReadWriteCount localCounts = PxUnionCast(TlsGet(mThreadReadWriteDepth)); if (localCounts.writeLockDepth < 1) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::unlockWrite() called without matching call to PxScene::lockWrite(), behaviour will be undefined."); return; } localCounts.writeLockDepth--; TlsSet(mThreadReadWriteDepth, PxUnionCast(localCounts)); PX_ASSERT(mCurrentWriter == Thread::getId()); if (localCounts.writeLockDepth == 0) { mCurrentWriter = 0; mRWLock.unlockWriter(); } } PxReal NpScene::getWakeCounterResetValue() const { NP_READ_CHECK(this); return getWakeCounterResetValueInteral(); } static PX_FORCE_INLINE void shiftRigidActor(PxRigidActor* a, const PxVec3& shift) { PxActorType::Enum t = a->getType(); if (t == PxActorType::eRIGID_DYNAMIC) { NpRigidDynamic* rd = static_cast(a); rd->getScbBodyFast().onOriginShift(shift); } else if (t == PxActorType::eRIGID_STATIC) { NpRigidStatic* rs = static_cast(a); rs->getScbRigidStaticFast().onOriginShift(shift); } else { PX_ASSERT(t == PxActorType::eARTICULATION_LINK); NpArticulationLink* al = static_cast(a); al->getScbBodyFast().onOriginShift(shift); } } void NpScene::shiftOrigin(const PxVec3& shift) { PX_PROFILE_ZONE("API.shiftOrigin", getContextId()); NP_WRITE_CHECK(this); if(mScene.isPhysicsBuffering()) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::shiftOrigin() not allowed while simulation is running. Call will be ignored."); return; } PX_SIMD_GUARD; const PxU32 prefetchLookAhead = 4; PxU32 rigidCount = mRigidActors.size(); PxRigidActor*const* rigidActors = mRigidActors.begin(); PxU32 batchIterCount = rigidCount / prefetchLookAhead; PxU32 idx = 0; for(PxU32 i=0; i < batchIterCount; i++) { // prefetch elements for next batch if (i < (batchIterCount-1)) { Ps::prefetchLine(rigidActors[idx + prefetchLookAhead]); Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead]) + 128); // for the buffered pose Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 1]); Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 1]) + 128); Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 2]); Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 2]) + 128); Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 3]); Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 3]) + 128); } else { for(PxU32 k=(idx + prefetchLookAhead); k < rigidCount; k++) { Ps::prefetchLine(rigidActors[k]); Ps::prefetchLine(reinterpret_cast(rigidActors[k]) + 128); } } for(PxU32 j=idx; j < (idx + prefetchLookAhead); j++) { shiftRigidActor(rigidActors[j], shift); } idx += prefetchLookAhead; } // process remaining objects for(PxU32 i=idx; i < rigidCount; i++) { shiftRigidActor(rigidActors[i], shift); } PxArticulation*const* articulations = mArticulations.getEntries(); for(PxU32 i=0; i < mArticulations.size(); i++) { NpArticulation* np = static_cast(articulations[i]); NpArticulationLink*const* links = np->getLinks(); for(PxU32 j=0; j < np->getNbLinks(); j++) { shiftRigidActor(links[j], shift); } } mScene.shiftOrigin(shift); // // shift scene query related data structures // mSQManager.shiftOrigin(shift); Ps::HashSet::Iterator it = mVolumeCaches.getIterator(); while (!it.done()) { NpVolumeCache* cache = (*it); cache->onOriginShift(shift); ++it; } #if PX_ENABLE_DEBUG_VISUALIZATION // // debug visualization // mRenderBuffer.shift(-shift); #endif } #if PX_SUPPORT_PVD PxPvdSceneClient* NpScene::getScenePvdClient() { return &mScene.getScenePvdClient(); } #else PxPvdSceneClient* NpScene::getScenePvdClient() { return NULL; } #endif PxBatchQuery* NpScene::createBatchQuery(const PxBatchQueryDesc& desc) { PX_PROFILE_ZONE("API.createBatchQuery", getContextId()); PX_CHECK_AND_RETURN_NULL(desc.isValid(),"Supplied PxBatchQueryDesc is not valid. createBatchQuery returns NULL."); NpBatchQuery* bq = PX_NEW(NpBatchQuery)(*this, desc); mBatchQueries.pushBack(bq); return bq; } void NpScene::releaseBatchQuery(PxBatchQuery* sq) { PX_PROFILE_ZONE("API.releaseBatchQuery", getContextId()); NpBatchQuery* npsq = static_cast(sq); bool found = mBatchQueries.findAndReplaceWithLast(npsq); PX_UNUSED(found); PX_ASSERT(found); PX_DELETE_AND_RESET(npsq); }