// // 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-2019 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 "NpArticulationReducedCoordinate.h" #include "NpArticulationLink.h" #include "NpArticulationJoint.h" #include "NpAggregate.h" #include "NpBatchQuery.h" #include "SqPruner.h" #include "SqPruningStructure.h" #include "SqSceneQueryManager.h" #include "GuBVHStructure.h" #include "ScbNpDeps.h" #include "ScArticulationSim.h" #include "ScConstraintSim.h" #include "CmCollection.h" #include "CmUtils.h" #include "extensions/PxJoint.h" #include "PxsIslandSim.h" #include "common/PxProfileZone.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()), mSceneQueriesStaticPrunerUpdate (getContextId(), 0, "NpSceneQueries.sceneQueriesStaticPrunerUpdate"), mSceneQueriesDynamicPrunerUpdate(getContextId(), 0, "NpSceneQueries.sceneQueriesDynamicPrunerUpdate"), mSceneQueryUpdateMode (desc.sceneQueryUpdateMode) #if PX_SUPPORT_PVD , mSingleSqCollector (mScene, false), mBatchedSqCollector (mScene, true) #endif { mSceneQueriesStaticPrunerUpdate.setObject(this); mSceneQueriesDynamicPrunerUpdate.setObject(this); } 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")), mSanityBounds (desc.sanityBounds), mNbClients (1), //we always have the default client. mClientBehaviorFlags (PX_DEBUG_EXP("sceneBehaviorFlags")), mSceneCompletion (getContextId(), mPhysicsDone), mCollisionCompletion (getContextId(), mCollisionDone), mSceneQueriesCompletion (getContextId(), mSceneQueriesDone), mSceneExecution (getContextId(), 0, "NpScene.execution"), mSceneCollide (getContextId(), 0, "NpScene.collide"), mSceneAdvance (getContextId(), 0, "NpScene.solve"), mControllingSimulation (false), mSimThreadStackSize (0), mConcurrentWriteCount (0), mConcurrentReadCount (0), mConcurrentErrorCount (0), mCurrentWriter (0), mSceneQueriesUpdateRunning (false), mHasSimulatedOnce (false), mBetweenFetchResults (false), mBuildFrozenActors (false) { mSceneExecution.setObject(this); mSceneCollide.setObject(this); mSceneAdvance.setObject(this); mTaskManager = mScene.getScScene().getTaskManagerPtr(); mCudaContextManager = mScene.getScScene().getCudaContextManager(); mThreadReadWriteDepth = Ps::TlsAlloc(); updatePhysXIndicator(); } 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); PxU32 rigidActorCount = mRigidActors.size(); while(rigidActorCount--) removeActor(*mRigidActors[rigidActorCount], false); PxU32 articCount = mArticulations.size(); while(articCount--) removeArticulation(*mArticulations.getEntries()[articCount], false); 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, const PxBVHStructure* bvhStructure) { 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, bvhStructure); 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, const PxBVHStructure* bvhStructure) { // BvhStructure check if(bvhStructure) { const PxRigidActor* rigidActor = actor.is(); if(!rigidActor || bvhStructure->getNbBounds() == 0 || bvhStructure->getNbBounds() > rigidActor->getNbShapes()) { Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxRigidActor::setBVHStructure structure is empty or does not match shapes in the actor."); return; } } 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, static_cast(bvhStructure)); } 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, static_cast(bvhStructure)); } break; 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, NULL, 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(), false); updateScbStateAndSetupSq(a, a.getScbBodyFast(), a.getShapeManager(), true, shapeBounds.begin(), hasPrunerStructure); addRigidActorToArray(a, mRigidActors); a.addConstraintsToScene(); } else addRigidDynamic(a, NULL, hasPrunerStructure); } else { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addRigidActors(): articulation link not permitted"); break; } } // merge sq PrunerStructure if(pS) { mSQManager.addPruningStructure(*pS); } 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(), actor); 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(), actor); Scb::Body& b = actor.getScbBodyFast(); mScene.removeActor(b, wakeOnLostTouch, b.isSimDisabledInternally()); removeFromRigidActorList(actor.getRigidActorArrayIndex()); } 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; 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; 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, const Gu::BVHStructure* bvhStructure, 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, bvhStructure); actor.getShapeManager().setupAllSceneQuery(scene, actor, hasPrunerStructure, uninflatedBounds, bvhStructure); if(!noSimBuffered) actor.addConstraintsToScene(); addRigidActorToArray(actor, actors); } void NpScene::addRigidStatic(NpRigidStatic& actor, const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure) { addActorT(actor, actor.getScbRigidStaticFast(), mRigidActors, this, bvhStructure, hasPrunerStructure); } void NpScene::addRigidDynamic(NpRigidDynamic& body, const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure) { addActorT(body, body.getScbBodyFast(), mRigidActors, this, bvhStructure, 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(), actor); 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(PxArticulationBase& 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 && articulation.getConcreteType() != PxConcreteType::eARTICULATION_REDUCED_COORDINATE) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): Only Reduced coordinate articulations are currently supported when PxSceneFlag::eENABLE_GPU_DYNAMICS is set!"); return; } if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE && articulation.getConcreteType() == PxConcreteType::eARTICULATION_REDUCED_COORDINATE) { Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): this call is not allowed while the simulation is running. Call will be ignored!"); return; } PxArticulationImpl& npa = *reinterpret_cast(articulation.getImpl()); Scb::Articulation& art = npa.getScbArticulation(); 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!"); } static void checkArticulationLink(NpScene* scene, NpArticulationLink* link) { #if PX_CHECKED scene->checkPositionSanity(*link, link->getGlobalPose(), "PxScene::addArticulation or PxScene::addAggregate"); #else PX_UNUSED(scene); #endif if(link->getMass()==0.0f) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); link->setMass(1.0f); } const PxVec3 inertia0 = link->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)"); link->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); } } void NpScene::addArticulationInternal(PxArticulationBase& npa) { // Add root link first PxU32 nbLinks = npa.getNbLinks(); PX_ASSERT(nbLinks > 0); PxArticulationImpl* impl = reinterpret_cast(npa.getImpl()); NpArticulationLink* rootLink = static_cast(impl->getRoot()); checkArticulationLink(this, rootLink); bool linkTriggersWakeUp = !rootLink->getScbBodyFast().checkSleepReadinessBesidesWakeCounter(); addArticulationLinkBody(*rootLink); // Add articulation PxArticulationImpl* npaImpl = npa.getImpl(); Scb::Articulation& scbArt = npaImpl->getScbArticulation(); mScene.addArticulation(scbArt); Sc::ArticulationCore& scArtCore = scbArt.getScArticulation(); Sc::ArticulationSim* scArtSim = scArtCore.getSim(); if (scArtSim) { PxU32 handle = scArtSim->findBodyIndex(*rootLink->getScbBodyFast().getScBody().getSim()); rootLink->setLLIndex(handle); } rootLink->setInboundJointDof(0); 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]; checkArticulationLink(this, child); linkTriggersWakeUp = linkTriggersWakeUp || (!child->getScbBodyFast().checkSleepReadinessBesidesWakeCounter()); addArticulationLink(*child); // Adds joint too //child->setInboundJointDof(scArtSim->getDof(cHandle)); 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. npaImpl->wakeUpInternal(true, false); } mArticulations.insert(&npa); if (scArtSim) { scArtSim->checkResize(); linkStack[0] = rootLink; 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++) { NpArticulationLink* child = children[i]; child->setInboundJointDof(scArtSim->getDof(child->getLinkIndex())); if (npa.getConcreteType() == PxConcreteType::eARTICULATION_REDUCED_COORDINATE) { PxArticulationJointReducedCoordinate* joint = static_cast(child->getInboundJoint()); PxArticulationJointType::Enum jointType = joint->getJointType(); if (jointType == PxArticulationJointType::eUNDEFINED) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): The application need to set joint type. defaulting joint type to eFix"); joint->setJointType(PxArticulationJointType::eFIX); child->setInboundJointDof(0); } if (jointType != PxArticulationJointType::eFIX) { PxArticulationMotion::Enum motionX = joint->getMotion(PxArticulationAxis::eX); PxArticulationMotion::Enum motionY = joint->getMotion(PxArticulationAxis::eY); PxArticulationMotion::Enum motionZ = joint->getMotion(PxArticulationAxis::eZ); PxArticulationMotion::Enum motionSwing1 = joint->getMotion(PxArticulationAxis::eSWING1); PxArticulationMotion::Enum motionSwing2 = joint->getMotion(PxArticulationAxis::eSWING2); PxArticulationMotion::Enum motionTwist = joint->getMotion(PxArticulationAxis::eTWIST); //PxArticulationMotion::eLOCKED is 0 if (!(motionX | motionY | motionZ | motionSwing1 | motionSwing2 | motionTwist)) { //if all axis are locked, which means the user doesn't set the motion. In this case, we should change the joint type to be //fix to avoid crash in the solver Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): The application need to set joint motion. defaulting joint type to eFix"); joint->setJointType(PxArticulationJointType::eFIX); child->setInboundJointDof(0); } } } linkStack[stackSize] = child; stackSize++; } curLink++; } } //add loop joints if (npa.getConcreteType() == PxConcreteType::eARTICULATION_REDUCED_COORDINATE) { if ((scbArt.getScArticulation().getArticulationFlags() & PxArticulationFlag::eFIX_BASE)) { rootLink->setKinematicLink(true); } //This method will prepare link data for the gpu mScene.getScScene().addArticulationSimControl(scbArt.getScArticulation()); NpArticulationReducedCoordinate* npaRC = static_cast(&npa); for (PxU32 i = 0; i < npaRC->mLoopJoints.size(); ++i) { PxJoint* joint = npaRC->mLoopJoints[i]; NpConstraint* constraint = static_cast(joint->getConstraint()); Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); scArtSim->addLoopConstraint(cSim); } } } void NpScene::removeArticulation(PxArticulationBase& 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(PxArticulationBase& npa, bool wakeOnLostTouch, bool removeFromAggregate) { PxU32 nbLinks = npa.getNbLinks(); PX_ASSERT(nbLinks > 0); if(removeFromAggregate && npa.getAggregate()) { static_cast(npa.getAggregate())->removeArticulationAndReinsert(npa, false); PX_ASSERT(!npa.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.getImpl()->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) { PxArticulationImpl* impl = npa.getImpl(); IG::NodeIndex index = impl->getScbArticulation().getScArticulation().getIslandNodeIndex(); if (index.isValid()) mScene.getScScene().resetSpeculativeCCDArticulationLink(index.index()); } // Remove articulation mScene.removeArticulation(npa.getImpl()->getScbArticulation()); removeFromArticulationList(npa); } /////////////////////////////////////////////////////////////////////////////// void NpScene::addArticulationLinkBody(NpArticulationLink& link) { mScene.addActor(link.getScbBodyFast(), false, NULL, NULL); link.getShapeManager().setupAllSceneQuery(this, link, false); } void NpScene::addArticulationLinkConstraint(NpArticulationLink& link) { PxArticulationJointBase* j = link.getInboundJoint(); if (j) { PxArticulationJointImpl* impl = j->getImpl(); mScene.addArticulationJoint(impl->getScbArticulationJoint()); } link.addConstraintsToScene(); } void NpScene::addArticulationLink(NpArticulationLink& link) { addArticulationLinkBody(link); addArticulationLinkConstraint(link); Sc::ArticulationCore& scArtCore = link.getArticulation().getImpl()->getScbArticulation().getScArticulation(); Sc::ArticulationSim* scArtSim = scArtCore.getSim(); if (scArtSim) { PxU32 cHandle = scArtSim->findBodyIndex(*link.getScbBodyFast().getScBody().getSim()); link.setLLIndex(cHandle); } } void NpScene::removeArticulationLink(NpArticulationLink& link, bool wakeOnLostTouch) { PxArticulationJointBase* j =link.getInboundJoint(); link.removeConstraintsFromScene(); link.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast(), link); if (j) mScene.removeArticulationJoint(j->getImpl()->getScbArticulationJoint()); mScene.removeActor(link.getScbBodyFast(), wakeOnLostTouch, false); } /////////////////////////////////////////////////////////////////////////////// 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(NpConnectorType::eBvhStructure, &bvhStructure, 1)) { npActor.removeConnector(actor, NpConnectorType::eBvhStructure, bvhStructure, "PxBVHStructure connector could not have been removed!"); } np.addActorInternal(actor, *this, bvhStructure); // if a bvh structure was used dec ref count, we increased the ref count when adding the actor connection if(bvhStructure) { bvhStructure->decRefCount(); } } mAggregates.insert(&aggregate); } else Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addAggregate(): Aggregate already assigned to a scene. Call will be ignored!"); } void NpScene::removeAggregate(PxAggregate& aggregate, bool wakeOnLostTouch) { PX_PROFILE_ZONE("API.removeAggregate", getContextId()); NP_WRITE_CHECK(this); if(!removeFromSceneCheck(this, aggregate.getScene(), "PxScene::removeAggregate(): Aggregate")) return; NpAggregate& np = static_cast(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); PxArticulationBase& npArt = al.getRoot(); PxArticulationImpl* impl = reinterpret_cast(npArt.getImpl()); NpArticulationLink* const* links = impl->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()); } /////////////////////////////////////////////////////////////////////////////// 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) { } else if(serialType==PxConcreteType::eARTICULATION) { NpArticulation* np = static_cast(s); if (!np->getAggregate()) // The actor will be added when the aggregate is added { addArticulation(static_cast(*np)); } } else if (serialType == PxConcreteType::eARTICULATION_REDUCED_COORDINATE) { NpArticulationReducedCoordinate* np = static_cast(s); if (!np->getAggregate()) // The actor will be added when the aggregate is added { addArticulation(static_cast(*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++; } } 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++; } } } return writeCount; } /////////////////////////////////////////////////////////////////////////////// PxActor** NpScene::getActiveActors(PxU32& nbActorsOut) { NP_READ_CHECK(this); return mScene.getActiveActors(nbActorsOut); } PxActor** NpScene::getFrozenActors(PxU32& nbActorsOut) { NP_READ_CHECK(this); return mScene.getFrozenActors(nbActorsOut); } void NpScene::setFrozenActorFlag(const bool buildFrozenActors) { #if PX_CHECKED PxSceneFlags combinedFlag(PxSceneFlag::eENABLE_ACTIVE_ACTORS | PxSceneFlag::eENABLE_STABILIZATION); PX_CHECK_AND_RETURN((getFlags() & combinedFlag)== combinedFlag, "NpScene::setFrozenActorFlag: Cannot raise BuildFrozenActors if PxSceneFlag::eENABLE_STABILIZATION and PxSceneFlag::eENABLE_ACTIVE_ACTORS is not raised!"); #endif mBuildFrozenActors = buildFrozenActors; } /////////////////////////////////////////////////////////////////////////////// PxU32 NpScene::getNbArticulations() const { NP_READ_CHECK(this); return mArticulations.size(); } PxU32 NpScene::getArticulations(PxArticulationBase** 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(); 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) && (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)) { PxArticulationImpl* impl = reinterpret_cast(npLink.getRoot().getImpl()); impl->wakeUpInternal(false, true); } } break; default: Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxScene::resetFiltering(): only PxRigidActor supports 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)) { PxArticulationImpl* impl = reinterpret_cast(npLink.getRoot().getImpl()); impl->wakeUpInternal(false, true); } } break; } } PxPairFilteringMode::Enum NpScene::getKinematicKinematicFilteringMode() const { NP_READ_CHECK(this); return mScene.getScScene().getKineKineFilteringMode(); } PxPairFilteringMode::Enum NpScene::getStaticKinematicFilteringMode() const { NP_READ_CHECK(this); return mScene.getScScene().getStaticKineFilteringMode(); } /////////////////////////////////////////////////////////////////////////////// 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); 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); mCollisionCompletion.removeReference(); mSceneCollide.removeReference(); } else { mSceneCompletion.setContinuation(*mTaskManager, completionTask); mSceneExecution.setContinuation(*mTaskManager, &mSceneCompletion); 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(); PxBroadPhaseCallback* cb = scene.getBroadPhaseCallback(); for(PxU32 i=0;i(outAgg[i]); NpAggregate* np = static_cast(px); if(np->getScbAggregate().getControlState()==Scb::ControlState::eREMOVE_PENDING) continue; 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.updateCompoundActors(mScene.getScScene().getActiveCompoundBodiesArray(), mScene.getScScene().getNumActiveCompoundBodies()); mSQManager.afterSync(getSceneQueryUpdateModeFast()); #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 actors { PX_PROFILE_ZONE("Sim.buildActiveActors", getContextId()); const bool buildActiveActors = mScene.getFlags() & PxSceneFlag::eENABLE_ACTIVE_ACTORS; if (buildActiveActors && mBuildFrozenActors) mScene.buildActiveAndFrozenActors(); else if (buildActiveActors) 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 { PX_SIMD_GUARD; 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() { physx::PxSimulationEventCallback* callback = mScene->getSimulationEventCallback(); if(!callback) return; mScene->lockRead(); for(uint32_t i=0; ionContact(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_WRITE_CHECK(this); PX_SIMD_GUARD; 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_SUPPORT_GPU_PHYSX void NpScene::updatePhysXIndicator() { Ps::IntBool isGpu = mScene.getScScene().isUsingGpuRigidBodies(); mPhysXIndicator.setIsGpu(isGpu != 0); } #endif //PX_SUPPORT_GPU_PHYSX /////////////////////////////////////////////////////////////////////////////// void NpScene::setSceneQueryUpdateMode(PxSceneQueryUpdateMode::Enum updateMode) { NP_WRITE_CHECK(this); mSceneQueryUpdateMode = updateMode; } PxSceneQueryUpdateMode::Enum NpScene::getSceneQueryUpdateMode() const { NP_READ_CHECK(this); return mSceneQueryUpdateMode; } 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(); } void NpScene::setSolverArticulationBatchSize(PxU32 solverBatchSize) { NP_WRITE_CHECK(this); mScene.setSolverArticulationBatchSize(solverBatchSize); } PxU32 NpScene::getSolverArticulationBatchSize(void) const { NP_READ_CHECK(this); // get from our local copy return mScene.getSolverArticulationBatchSize(); } /////////////////////////////////////////////////////////////////////////////// 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(); } PxCudaContextManager* NpScene::getCudaContextManager() const { return mCudaContextManager; } 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 { ThreadReadWriteCount(const size_t data) : readDepth(data & 0xFF), writeDepth((data >> 8) & 0xFF), readLockDepth((data >> 16) & 0xFF), writeLockDepth((data >> 24) & 0xFF) { } size_t getData() const { return size_t(writeLockDepth) << 24 | size_t(readLockDepth) << 16 | size_t(writeDepth) << 8 | size_t(readDepth); } 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(TlsGetValue(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(TlsGetValue(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; TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); 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 (TlsGetValue(mThreadReadWriteDepth)); // see comment in startWrite() if (allowReentry) localCounts.writeDepth--; else localCounts.writeDepth-=2; TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); } } bool NpScene::startRead() const { if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) { ThreadReadWriteCount localCounts (TlsGetValue(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 (TlsGetValue(mThreadReadWriteDepth)); localCounts.readDepth++; TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); // 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 (TlsGetValue(mThreadReadWriteDepth)); localCounts.readDepth--; TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); } } #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 (TlsGetValue(mThreadReadWriteDepth)); localCounts.readLockDepth++; TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); // if we are the current writer then increment the reader count but don't actually lock (allow reading from threads with write ownership) if(localCounts.readLockDepth == 1) mRWLock.lockReader(mCurrentWriter != Thread::getId()); } void NpScene::unlockRead() { // increment this threads read depth ThreadReadWriteCount localCounts (TlsGetValue(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--; TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); // 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 (TlsGetValue(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++; TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); // 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 (TlsGetValue(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--; TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); 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); } PxArticulationBase*const* articulations = mArticulations.getEntries(); for(PxU32 i=0; i < mArticulations.size(); i++) { PxArticulationBase* np = (articulations[i]); NpArticulationLink*const* links = reinterpret_cast(np->getImpl())->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); #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 PxsSimulationController* NpScene::getSimulationController() { return mScene.getScScene().getSimulationController(); } void NpScene::setActiveActors(PxActor** actors, PxU32 nbActors) { NP_WRITE_CHECK(this); mScene.setActiveActors(actors, nbActors); } void NpScene::forceSceneQueryRebuild() { SqRefFinder sqRefFinder; mScene.getScScene().syncSceneQueryBounds(mSQManager.getDynamicBoundsSync(), sqRefFinder); mSQManager.afterSync(getSceneQueryUpdateModeFast()); } void NpScene::sceneQueriesUpdate(physx::PxBaseTask* completionTask, bool controlSimulation) { PX_SIMD_GUARD; bool runUpdateTasks[PruningIndex::eCOUNT] = {true, true}; { // write guard must end before scene queries tasks kicks off worker threads NP_WRITE_CHECK(this); PX_PROFILE_START_CROSSTHREAD("Basic.sceneQueriesUpdate", getContextId()); if(mSceneQueriesUpdateRunning) { //fetchSceneQueries doesn't get called Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchSceneQueries was not called!"); return; } // flush scene queries updates mSQManager.flushUpdates(); // prepare scene queries for build - copy bounds runUpdateTasks[PruningIndex::eSTATIC] = mSQManager.prepareSceneQueriesUpdate(PruningIndex::eSTATIC); runUpdateTasks[PruningIndex::eDYNAMIC] = mSQManager.prepareSceneQueriesUpdate(PruningIndex::eDYNAMIC); mSceneQueriesUpdateRunning = true; } { PX_PROFILE_ZONE("Sim.sceneQueriesTaskSetup", 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(); } mSceneQueriesCompletion.setContinuation(*mTaskManager, completionTask); if(runUpdateTasks[PruningIndex::eSTATIC]) mSceneQueriesStaticPrunerUpdate.setContinuation(&mSceneQueriesCompletion); if(runUpdateTasks[PruningIndex::eDYNAMIC]) mSceneQueriesDynamicPrunerUpdate.setContinuation(&mSceneQueriesCompletion); mSceneQueriesCompletion.removeReference(); if(runUpdateTasks[PruningIndex::eSTATIC]) mSceneQueriesStaticPrunerUpdate.removeReference(); if(runUpdateTasks[PruningIndex::eDYNAMIC]) mSceneQueriesDynamicPrunerUpdate.removeReference(); } } bool NpScene::checkSceneQueriesInternal(bool block) { PX_PROFILE_ZONE("Basic.checkSceneQueries", getContextId()); return mSceneQueriesDone.wait(block ? Ps::Sync::waitForever : 0); } bool NpScene::checkQueries(bool block) { return checkSceneQueriesInternal(block); } bool NpScene::fetchQueries(bool block) { if(!mSceneQueriesUpdateRunning) { //fetchSceneQueries doesn't get called Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchQueries: fetchQueries() called illegally! It must be called after sceneQueriesUpdate()"); return false; } if(!checkSceneQueriesInternal(block)) return false; { PX_SIMD_GUARD; NP_WRITE_CHECK(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.fetchQueries", getContextId()); // flush updates and commit if work is done mSQManager.flushUpdates(); PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchQueries", getContextId()); PX_PROFILE_STOP_CROSSTHREAD("Basic.sceneQueriesUpdate", getContextId()); mSceneQueriesDone.reset(); mSceneQueriesUpdateRunning = false; } return true; } void NpScene::frameEnd() { #if PX_SUPPORT_PVD mScene.getScenePvdClient().frameEnd(); #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); }