3074 lines
97 KiB
C++
3074 lines
97 KiB
C++
//
|
|
// 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<PxScene*>(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<class T>
|
|
static PX_FORCE_INLINE void addRigidActorToArray(T& a, Ps::Array<PxRigidActor*>& 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<PxRigidStatic>();
|
|
if(a)
|
|
{
|
|
#if PX_CHECKED
|
|
if(!static_cast<NpRigidStatic*>(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<NpRigidStatic*>(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<PxRigidDynamic>();
|
|
if(aD && static_cast<NpRigidDynamic*>(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<PxRigidActor>();
|
|
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<NpRigidStatic&>(actor);
|
|
#if PX_CHECKED
|
|
checkPositionSanity(npStatic, npStatic.getGlobalPose(), "PxScene::addActor or PxScene::addAggregate");
|
|
#endif
|
|
addRigidStatic(npStatic, static_cast<const Gu::BVHStructure*>(bvhStructure));
|
|
}
|
|
break;
|
|
|
|
case PxConcreteType::eRIGID_DYNAMIC:
|
|
{
|
|
NpRigidDynamic& npDynamic = static_cast<NpRigidDynamic&>(actor);
|
|
#if PX_CHECKED
|
|
checkPositionSanity(npDynamic, npDynamic.getGlobalPose(), "PxScene::addActor or PxScene::addAggregate");
|
|
#endif
|
|
addRigidDynamic(npDynamic, static_cast<const Gu::BVHStructure*>(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<nbShapes;i++)
|
|
{
|
|
NpShape& shape = *shapes[i];
|
|
const PxShapeFlags shapeFlags = shape.getFlagsUnbuffered(); // PT: note that the regular code reads buffered flags
|
|
|
|
shape.incRefCount();
|
|
if(shape.isExclusiveFast())
|
|
{
|
|
shape.getScbShape().setScbScene(&mScene);
|
|
shape.getScbShape().setControlState(Scb::ControlState::eIN_SCENE);
|
|
}
|
|
|
|
// PT: this part is copied from 'NpShapeManager::setupAllSceneQuery'
|
|
if(shapeFlags & PxShapeFlag::eSCENE_QUERY_SHAPE) // PT: TODO: refactor with 'isSceneQuery' in shape manager?
|
|
shapeManager.addPrunerShape(sqManager, i, shape, rigidActor, actorDynamic, bounds ? bounds+i : NULL, hasPrunerStructure);
|
|
}
|
|
}
|
|
|
|
PX_FORCE_INLINE void NpScene::updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Body& body, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure)
|
|
{
|
|
body.initBufferedState();
|
|
updateScbStateAndSetupSq(rigidActor, static_cast<Scb::Actor&>(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<const Sq::PruningStructure&>(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<NpRigidStatic*>(0)->getScbRigidStaticFast().getScStatic())));
|
|
scState.staticShapeTableOffset = ptrdiff_t(size_t(&(reinterpret_cast<NpRigidStatic*>(0)->getShapeManager().getShapeTable())));
|
|
scState.dynamicActorOffset = ptrdiff_t(size_t(&(reinterpret_cast<NpRigidDynamic*>(0)->getScbBodyFast().getScBody())));
|
|
scState.dynamicShapeTableOffset = ptrdiff_t(size_t(&(reinterpret_cast<NpRigidDynamic*>(0)->getShapeManager().getShapeTable())));
|
|
scState.shapeOffset = ptrdiff_t(NpShapeGetScPtrOffset());
|
|
|
|
Ps::InlineArray<PxBounds3, 8> shapeBounds;
|
|
for(actorsDone=0; actorsDone<nbActors; actorsDone++)
|
|
{
|
|
if(actorsDone+1<nbActors)
|
|
Ps::prefetch(actors[actorsDone+1], sizeof(NpRigidDynamic)); // worst case: PxRigidStatic is smaller
|
|
|
|
const Scb::ControlState::Enum cs = NpActor::getScbFromPxActor(*actors[actorsDone]).getControlState();
|
|
if (!((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (NpActor::getOwnerScene(*actors[actorsDone]) == this))))
|
|
{
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): Actor already assigned to a scene. Call will be ignored!");
|
|
break;
|
|
}
|
|
|
|
const PxType type = actors[actorsDone]->getConcreteType();
|
|
if(type == PxConcreteType::eRIGID_STATIC)
|
|
{
|
|
NpRigidStatic& a = *static_cast<NpRigidStatic*>(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<NpRigidDynamic*>(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;i<actorsDone;i++)
|
|
{
|
|
if ((actors[i]->getConcreteType()==PxConcreteType::eRIGID_STATIC) && (!(static_cast<NpRigidStatic*>(actors[i])->getScbRigidStaticFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)))
|
|
mScene.getScenePvdClient().addStaticAndShapesToPvd(static_cast<NpRigidStatic*>(actors[i])->getScbRigidStaticFast());
|
|
else if ((actors[i]->getConcreteType() == PxConcreteType::eRIGID_DYNAMIC) && (!(static_cast<NpRigidDynamic*>(actors[i])->getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)))
|
|
mScene.getScenePvdClient().addBodyAndShapesToPvd(static_cast<NpRigidDynamic*>(actors[i])->getScbBodyFast());
|
|
}
|
|
#endif
|
|
|
|
if(actorsDone<nbActors) // Everything is consistent up to the failure point, so just use removeActor to back out gracefully if necessary
|
|
{
|
|
for(PxU32 j=0;j<actorsDone;j++)
|
|
removeActorInternal(*actors[j], false, true);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void NpScene::removeActors(PxActor*const* PX_RESTRICT actors, PxU32 nbActors, bool wakeOnLostTouch)
|
|
{
|
|
PX_PROFILE_ZONE("API.removeActors", getContextId());
|
|
NP_WRITE_CHECK(this);
|
|
|
|
Sc::Scene& scScene = mScene.getScScene();
|
|
// resize the bitmap so it does not allocate each remove actor call
|
|
scScene.resizeReleasedBodyIDMaps(mRigidActors.size(),nbActors);
|
|
Sc::BatchRemoveState removeState;
|
|
scScene.setBatchRemove(&removeState);
|
|
|
|
for(PxU32 actorsDone=0; actorsDone<nbActors; actorsDone++)
|
|
{
|
|
if(actorsDone+1<nbActors)
|
|
Ps::prefetch(actors[actorsDone+1], sizeof(NpRigidDynamic)); // worst case: PxRigidStatic is smaller
|
|
|
|
PxType type = actors[actorsDone]->getConcreteType();
|
|
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<NpRigidStatic*>(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<NpRigidDynamic*>(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<NpRigidStatic&>(actor);
|
|
removeRigidStatic(npStatic, wakeOnLostTouch, removeFromAggregate);
|
|
}
|
|
break;
|
|
|
|
case PxActorType::eRIGID_DYNAMIC:
|
|
{
|
|
NpRigidDynamic& npDynamic = static_cast<NpRigidDynamic&>(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<NpRigidStatic&>(rigidActor);
|
|
npStatic.setRigidActorArrayIndex(index);
|
|
}
|
|
break;
|
|
case PxActorType::eRIGID_DYNAMIC:
|
|
{
|
|
NpRigidDynamic& npDynamic = static_cast<NpRigidDynamic&>(rigidActor);
|
|
npDynamic.setRigidActorArrayIndex(index);
|
|
}
|
|
break;
|
|
case PxActorType::eARTICULATION_LINK:
|
|
case PxActorType::eACTOR_COUNT:
|
|
case PxActorType::eACTOR_FORCE_DWORD:
|
|
PX_ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<class T, class T2>
|
|
static PX_FORCE_INLINE void addActorT(T& actor, T2& scbActor, Ps::Array<PxRigidActor*>& 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<class T, class T2>
|
|
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<PxArticulationImpl*>(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<PxArticulationImpl*>(npa.getImpl());
|
|
NpArticulationLink* rootLink = static_cast<NpArticulationLink*>(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<PxArticulationJointReducedCoordinate*>(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<NpArticulationReducedCoordinate*>(&npa);
|
|
|
|
for (PxU32 i = 0; i < npaRC->mLoopJoints.size(); ++i)
|
|
{
|
|
PxJoint* joint = npaRC->mLoopJoints[i];
|
|
NpConstraint* constraint = static_cast<NpConstraint*>(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<NpAggregate*>(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<NpAggregate&>(aggregate);
|
|
|
|
const PxU32 nb = np.getCurrentSizeFast();
|
|
#if PX_CHECKED
|
|
for(PxU32 i=0;i<nb;i++)
|
|
{
|
|
PxRigidStatic* a = np.getActorFast(i)->is<PxRigidStatic>();
|
|
if(a && !static_cast<NpRigidStatic*>(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<nb;i++)
|
|
{
|
|
PX_ASSERT(np.getActorFast(i));
|
|
PxActor& actor = *np.getActorFast(i);
|
|
|
|
//A.B. check if a bvh structure was connected to that actor, we will use it for the insert and remove it
|
|
NpActor& npActor = NpActor::getFromPxActor(actor);
|
|
Gu::BVHStructure* bvhStructure = NULL;
|
|
if(npActor.getConnectors<Gu::BVHStructure>(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<NpAggregate&>(aggregate);
|
|
if(np.getScene()!=this)
|
|
return;
|
|
|
|
const PxU32 nb = np.getCurrentSizeFast();
|
|
for(PxU32 j=0;j<nb;j++)
|
|
{
|
|
PxActor* a = np.getActorFast(j);
|
|
PX_ASSERT(a);
|
|
|
|
if (a->getType() != 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<NpArticulationLink&>(*a);
|
|
PxArticulationBase& npArt = al.getRoot();
|
|
PxArticulationImpl* impl = reinterpret_cast<PxArticulationImpl*>(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<const Cm::Collection&>(collection);
|
|
|
|
PxU32 nb = col.internalGetNbObjects();
|
|
#if PX_CHECKED
|
|
for(PxU32 i=0;i<nb;i++)
|
|
{
|
|
PxRigidStatic* a = col.internalGetObject(i)->is<PxRigidStatic>();
|
|
if(a && !static_cast<NpRigidStatic*>(a)->checkConstraintValidity())
|
|
{
|
|
Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addCollection(): collection contains an actor with an invalid constraint!");
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Ps::Array<PxActor*> actorsToInsert;
|
|
actorsToInsert.reserve(nb);
|
|
|
|
struct Local
|
|
{
|
|
static void addActorIfNeeded(PxActor* actor, Ps::Array<PxActor*>& actorArray)
|
|
{
|
|
if(actor->getAggregate())
|
|
return; // The actor will be added when the aggregate is added
|
|
actorArray.pushBack(actor);
|
|
}
|
|
};
|
|
|
|
for(PxU32 i=0;i<nb;i++)
|
|
{
|
|
PxBase* s = col.internalGetObject(i);
|
|
const PxType serialType = s->getConcreteType();
|
|
|
|
//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<NpRigidDynamic*>(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<NpRigidStatic*>(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<NpArticulation*>(s);
|
|
if (!np->getAggregate()) // The actor will be added when the aggregate is added
|
|
{
|
|
addArticulation(static_cast<PxArticulationBase&>(*np));
|
|
}
|
|
}
|
|
else if (serialType == PxConcreteType::eARTICULATION_REDUCED_COORDINATE)
|
|
{
|
|
NpArticulationReducedCoordinate* np = static_cast<NpArticulationReducedCoordinate*>(s);
|
|
if (!np->getAggregate()) // The actor will be added when the aggregate is added
|
|
{
|
|
addArticulation(static_cast<PxArticulationBase&>(*np));
|
|
}
|
|
}
|
|
else if(serialType==PxConcreteType::eAGGREGATE)
|
|
{
|
|
NpAggregate* np = static_cast<NpAggregate*>(s);
|
|
addAggregate(*np);
|
|
}
|
|
else if(serialType == PxConcreteType::ePRUNING_STRUCTURE)
|
|
{
|
|
PxPruningStructure* ps = static_cast<PxPruningStructure*>(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<PxRigidStatic>())
|
|
nbActors++;
|
|
}
|
|
}
|
|
|
|
if (types & PxActorTypeFlag::eRIGID_DYNAMIC)
|
|
{
|
|
for(PxU32 i=mRigidActors.size(); i--;)
|
|
{
|
|
if (mRigidActors[i]->is<PxRigidDynamic>())
|
|
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<PxRigidStatic>())
|
|
{
|
|
if (virtualIndex >= startIndex)
|
|
buffer[writeCount++] = mRigidActors[i];
|
|
virtualIndex++;
|
|
}
|
|
else if ((types & PxActorTypeFlag::eRIGID_DYNAMIC) && mRigidActors[i]->is<PxRigidDynamic>())
|
|
{
|
|
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.size();i++)
|
|
static_cast<NpArticulation *>(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<NpRigidDynamic*>(a)->visualize(out, this);
|
|
else
|
|
static_cast<NpRigidStatic*>(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<nbRegions;i++)
|
|
{
|
|
PxBroadPhaseRegionInfo info;
|
|
mScene.getBroadPhaseRegions(&info, 1, i);
|
|
|
|
if(info.active)
|
|
out << PxU32(PxDebugColor::eARGB_YELLOW);
|
|
else
|
|
out << PxU32(PxDebugColor::eARGB_BLACK);
|
|
out << Cm::DebugBox(info.region.bounds);
|
|
}
|
|
}
|
|
|
|
if(getVisualizationParameter(PxVisualizationParameter::eCULL_BOX)!=0.0f)
|
|
{
|
|
const PxBounds3& cullbox = getScene().getVisualizationCullingBox();
|
|
if(!cullbox.isEmpty())
|
|
{
|
|
out << PxU32(PxDebugColor::eARGB_YELLOW);
|
|
out << Cm::DebugBox(cullbox);
|
|
}
|
|
}
|
|
|
|
#if PX_SUPPORT_PVD
|
|
mScene.getScenePvdClient().visualize(mRenderBuffer);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void NpScene::getSimulationStatistics(PxSimulationStatistics& s) const
|
|
{
|
|
NP_READ_CHECK(this);
|
|
|
|
if (getSimulationStage() == Sc::SimulationStage::eCOMPLETE)
|
|
{
|
|
#if PX_ENABLE_SIM_STATS
|
|
mScene.getStats(s);
|
|
#else
|
|
PX_UNUSED(s);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//will be reading data that is getting written during the sim, hence, avoid call while simulation is running.
|
|
Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::getSimulationStatistics() not allowed while simulation is running. Call will be ignored.");
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Multiclient
|
|
|
|
PxClientID NpScene::createClient()
|
|
{
|
|
NP_WRITE_CHECK(this);
|
|
|
|
PX_CHECK_AND_RETURN_NULL(mNbClients < PX_MAX_CLIENTS, "PxScene::createClient: Maximum number of clients reached! No new client created.");
|
|
mNbClients++; //track this just for error checking
|
|
return mScene.createClient();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//FrictionModel
|
|
|
|
void NpScene::setFrictionType(PxFrictionType::Enum frictionType)
|
|
{
|
|
NP_WRITE_CHECK(this);
|
|
PX_CHECK_AND_RETURN(!mHasSimulatedOnce, "PxScene::setFrictionType: This flag can only be set before calling Simulate() or Collide() for the first time");
|
|
mScene.setFrictionType(frictionType);
|
|
}
|
|
|
|
PxFrictionType::Enum NpScene::getFrictionType() const
|
|
{
|
|
NP_READ_CHECK(this);
|
|
return mScene.getFrictionType();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Callbacks
|
|
|
|
void NpScene::setSimulationEventCallback(PxSimulationEventCallback* callback)
|
|
{
|
|
NP_WRITE_CHECK(this);
|
|
mScene.setSimulationEventCallback(callback);
|
|
}
|
|
|
|
PxSimulationEventCallback* NpScene::getSimulationEventCallback() const
|
|
{
|
|
NP_READ_CHECK(this);
|
|
return mScene.getSimulationEventCallback();
|
|
}
|
|
|
|
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)
|
|
{
|
|
NP_WRITE_CHECK(this);
|
|
mScene.setBroadPhaseCallback(callback);
|
|
}
|
|
|
|
PxBroadPhaseCallback* NpScene::getBroadPhaseCallback() const
|
|
{
|
|
NP_READ_CHECK(this);
|
|
return mScene.getBroadPhaseCallback();
|
|
}
|
|
|
|
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<NpRigidStatic&>(actor);
|
|
npStatic.resetFiltering(npStatic.getScbRigidStaticFast(), NULL, 0);
|
|
}
|
|
break;
|
|
|
|
case PxConcreteType::eRIGID_DYNAMIC:
|
|
{
|
|
NpRigidDynamic& npDynamic = static_cast<NpRigidDynamic&>(actor);
|
|
if (npDynamic.resetFiltering(npDynamic.getScbBodyFast(), NULL, 0))
|
|
npDynamic.wakeUpInternal();
|
|
}
|
|
break;
|
|
|
|
case PxConcreteType::eARTICULATION_LINK:
|
|
{
|
|
NpArticulationLink& npLink = static_cast<NpArticulationLink&>(actor);
|
|
if (npLink.resetFiltering(npLink.getScbBodyFast(), NULL, 0))
|
|
{
|
|
PxArticulationImpl* impl = reinterpret_cast<PxArticulationImpl*>(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<NpRigidStatic&>(actor);
|
|
npStatic.resetFiltering(npStatic.getScbRigidStaticFast(), shapes, shapeCount);
|
|
}
|
|
break;
|
|
|
|
case PxConcreteType::eRIGID_DYNAMIC:
|
|
{
|
|
NpRigidDynamic& npDynamic = static_cast<NpRigidDynamic&>(actor);
|
|
if (npDynamic.resetFiltering(npDynamic.getScbBodyFast(), shapes, shapeCount))
|
|
npDynamic.wakeUpInternal();
|
|
}
|
|
break;
|
|
|
|
case PxConcreteType::eARTICULATION_LINK:
|
|
{
|
|
NpArticulationLink& npLink = static_cast<NpArticulationLink&>(actor);
|
|
if (npLink.resetFiltering(npLink.getScbBodyFast(), shapes, shapeCount))
|
|
{
|
|
PxArticulationImpl* impl = reinterpret_cast<PxArticulationImpl*>(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<mConstraints.size();i++)
|
|
{
|
|
static_cast<NpConstraint*>(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<size_t>(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<NpPhysics&>(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<nbOut1;i++)
|
|
{
|
|
PxAggregate* px = reinterpret_cast<PxAggregate*>(outAgg[i]);
|
|
NpAggregate* np = static_cast<NpAggregate*>(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<const NpShape*>(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<PxContactPairHeader>& 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; i<mNbContactPairHeaders; ++i)
|
|
{
|
|
const physx::PxContactPairHeader& pairHeader = mContactPairHeaders[i];
|
|
callback->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<PxContactPairHeader>& 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<NpRigidDynamic*>(a);
|
|
rd->getScbBodyFast().onOriginShift(shift);
|
|
}
|
|
else if (t == PxActorType::eRIGID_STATIC)
|
|
{
|
|
NpRigidStatic* rs = static_cast<NpRigidStatic*>(a);
|
|
rs->getScbRigidStaticFast().onOriginShift(shift);
|
|
}
|
|
else
|
|
{
|
|
PX_ASSERT(t == PxActorType::eARTICULATION_LINK);
|
|
NpArticulationLink* al = static_cast<NpArticulationLink*>(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<PxU8*>(rigidActors[idx + prefetchLookAhead]) + 128); // for the buffered pose
|
|
Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 1]);
|
|
Ps::prefetchLine(reinterpret_cast<PxU8*>(rigidActors[idx + prefetchLookAhead + 1]) + 128);
|
|
Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 2]);
|
|
Ps::prefetchLine(reinterpret_cast<PxU8*>(rigidActors[idx + prefetchLookAhead + 2]) + 128);
|
|
Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 3]);
|
|
Ps::prefetchLine(reinterpret_cast<PxU8*>(rigidActors[idx + prefetchLookAhead + 3]) + 128);
|
|
}
|
|
else
|
|
{
|
|
for(PxU32 k=(idx + prefetchLookAhead); k < rigidCount; k++)
|
|
{
|
|
Ps::prefetchLine(rigidActors[k]);
|
|
Ps::prefetchLine(reinterpret_cast<PxU8*>(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<PxArticulationImpl*>(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<NpBatchQuery*>(sq);
|
|
bool found = mBatchQueries.findAndReplaceWithLast(npsq);
|
|
PX_UNUSED(found); PX_ASSERT(found);
|
|
PX_DELETE_AND_RESET(npsq);
|
|
}
|
|
|