721 lines
25 KiB
C++
721 lines
25 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-2025 NVIDIA Corporation. All rights reserved.
|
||
|
|
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
|
||
|
|
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
|
||
|
|
|
||
|
|
#include "common/PxProfileZone.h"
|
||
|
|
#include "ScBodySim.h"
|
||
|
|
#include "ScShapeSim.h"
|
||
|
|
#include "ScArticulationSim.h"
|
||
|
|
#include "ScScene.h"
|
||
|
|
#include "DyIslandManager.h"
|
||
|
|
|
||
|
|
using namespace physx;
|
||
|
|
using namespace Sc;
|
||
|
|
|
||
|
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
void BodySim::addToSpeculativeCCDMap()
|
||
|
|
{
|
||
|
|
if(mNodeIndex.isValid())
|
||
|
|
{
|
||
|
|
if(isArticulationLink())
|
||
|
|
mScene.setSpeculativeCCDArticulationLink(mNodeIndex.index());
|
||
|
|
else
|
||
|
|
mScene.setSpeculativeCCDRigidBody(mNodeIndex.index());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void BodySim::removeFromSpeculativeCCDMap()
|
||
|
|
{
|
||
|
|
if(mNodeIndex.isValid())
|
||
|
|
{
|
||
|
|
if(isArticulationLink())
|
||
|
|
mScene.resetSpeculativeCCDArticulationLink(mNodeIndex.index());
|
||
|
|
else
|
||
|
|
mScene.resetSpeculativeCCDRigidBody(mNodeIndex.index());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// PT: TODO: consider using a non-member function for this one
|
||
|
|
void BodySim::updateContactDistance(PxReal* contactDistance, PxReal dt, const Bp::BoundsArray& boundsArray)
|
||
|
|
{
|
||
|
|
const PxsRigidBody& llBody = getLowLevelBody();
|
||
|
|
|
||
|
|
const PxRigidBodyFlags flags = llBody.getCore().mFlags;
|
||
|
|
|
||
|
|
// PT: TODO: no need to test eENABLE_SPECULATIVE_CCD if we parsed mSpeculativeCCDRigidBodyBitMap initially
|
||
|
|
if((flags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) && !(llBody.mInternalFlags & PxsRigidBody::eFROZEN))
|
||
|
|
{
|
||
|
|
// PT: if both CCD flags are enabled we're in "hybrid mode" and we only use speculative contacts for the angular part
|
||
|
|
const PxReal linearInflation = (flags & PxRigidBodyFlag::eENABLE_CCD) ? 0.0f : llBody.getLinearVelocity().magnitude() * dt;
|
||
|
|
|
||
|
|
const float angVelMagTimesDt = llBody.getAngularVelocity().magnitude() * dt;
|
||
|
|
|
||
|
|
PxU32 nbElems = getNbElements();
|
||
|
|
ElementSim** elems = getElements();
|
||
|
|
while(nbElems--)
|
||
|
|
{
|
||
|
|
ShapeSim* current = static_cast<ShapeSim*>(*elems++);
|
||
|
|
|
||
|
|
const PxU32 index = current->getElementID();
|
||
|
|
|
||
|
|
const PxBounds3& bounds = boundsArray.getBounds(index);
|
||
|
|
|
||
|
|
const PxReal radius = bounds.getExtents().magnitude();
|
||
|
|
|
||
|
|
//Heuristic for angular velocity...
|
||
|
|
const PxReal angularInflation = angVelMagTimesDt * radius;
|
||
|
|
|
||
|
|
contactDistance[index] = linearInflation + current->getContactOffset() + angularInflation;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::ArticulationSim::updateContactDistance(PxReal* contactDistance, PxReal dt, const Bp::BoundsArray& boundsArray)
|
||
|
|
{
|
||
|
|
const PxU32 size = mBodies.size();
|
||
|
|
for(PxU32 i=0; i<size; i++)
|
||
|
|
mBodies[i]->updateContactDistance(contactDistance, dt, boundsArray);
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
class SpeculativeCCDBaseTask : public Cm::Task
|
||
|
|
{
|
||
|
|
PX_NOCOPY(SpeculativeCCDBaseTask)
|
||
|
|
public:
|
||
|
|
const Bp::BoundsArray& mBoundsArray;
|
||
|
|
float* mContactDistances;
|
||
|
|
const float mDt;
|
||
|
|
|
||
|
|
SpeculativeCCDBaseTask(PxU64 contextID, const Bp::BoundsArray& boundsArray, PxReal* contactDistances, PxReal dt) :
|
||
|
|
Cm::Task (contextID),
|
||
|
|
mBoundsArray (boundsArray),
|
||
|
|
mContactDistances (contactDistances),
|
||
|
|
mDt (dt)
|
||
|
|
{}
|
||
|
|
};
|
||
|
|
|
||
|
|
class SpeculativeCCDContactDistanceUpdateTask : public SpeculativeCCDBaseTask
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
static const PxU32 MaxBodies = 128;
|
||
|
|
BodySim* mBodySims[MaxBodies];
|
||
|
|
PxU32 mNbBodies;
|
||
|
|
|
||
|
|
SpeculativeCCDContactDistanceUpdateTask(PxU64 contextID, PxReal* contactDistances, PxReal dt, const Bp::BoundsArray& boundsArray) :
|
||
|
|
SpeculativeCCDBaseTask (contextID, boundsArray, contactDistances, dt),
|
||
|
|
mNbBodies (0)
|
||
|
|
{}
|
||
|
|
|
||
|
|
virtual void runInternal()
|
||
|
|
{
|
||
|
|
const PxU32 nb = mNbBodies;
|
||
|
|
for(PxU32 i=0; i<nb; i++)
|
||
|
|
mBodySims[i]->updateContactDistance(mContactDistances, mDt, mBoundsArray);
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual const char* getName() const { return "SpeculativeCCDContactDistanceUpdateTask"; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
PX_NOCOPY(SpeculativeCCDContactDistanceUpdateTask)
|
||
|
|
};
|
||
|
|
|
||
|
|
class SpeculativeCCDContactDistanceArticulationUpdateTask : public SpeculativeCCDBaseTask
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
ArticulationSim* mArticulation;
|
||
|
|
|
||
|
|
SpeculativeCCDContactDistanceArticulationUpdateTask(PxU64 contextID, PxReal* contactDistances, PxReal dt, const Bp::BoundsArray& boundsArray, ArticulationSim* sim) :
|
||
|
|
SpeculativeCCDBaseTask (contextID, boundsArray, contactDistances, dt),
|
||
|
|
mArticulation (sim)
|
||
|
|
{}
|
||
|
|
|
||
|
|
virtual void runInternal()
|
||
|
|
{
|
||
|
|
mArticulation->updateContactDistance(mContactDistances, mDt, mBoundsArray);
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual const char* getName() const { return "SpeculativeCCDContactDistanceArticulationUpdateTask"; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
PX_NOCOPY(SpeculativeCCDContactDistanceArticulationUpdateTask)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
static SpeculativeCCDContactDistanceUpdateTask* createCCDTask(Cm::FlushPool& pool, PxU64 contextID, PxReal* contactDistances, PxReal dt, const Bp::BoundsArray& boundsArray)
|
||
|
|
{
|
||
|
|
return PX_PLACEMENT_NEW(pool.allocate(sizeof(SpeculativeCCDContactDistanceUpdateTask)), SpeculativeCCDContactDistanceUpdateTask)(contextID, contactDistances, dt, boundsArray);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::updateContactDistances(PxBaseTask* continuation)
|
||
|
|
{
|
||
|
|
PX_PROFILE_ZONE("Scene.updateContactDistances", mContextId);
|
||
|
|
|
||
|
|
Cm::FlushPool& pool = mLLContext->getTaskPool();
|
||
|
|
IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim();
|
||
|
|
bool hasContactDistanceChanged = mHasContactDistanceChanged;
|
||
|
|
|
||
|
|
// PT: TODO: it is quite unfortunate that we cannot shortcut parsing the bitmaps. Consider switching to arrays.
|
||
|
|
// We remove sleeping bodies from the map but we never shrink it....
|
||
|
|
|
||
|
|
// PT: TODO: why do we need to involve the island manager here?
|
||
|
|
// PT: TODO: why do we do that on sleeping bodies? Why don't we use mActiveBodies?
|
||
|
|
// PxArray<BodyCore*> mActiveBodies; // Sorted: kinematic before dynamic
|
||
|
|
// ===> because we remove bodies from the bitmap in BodySim::deactivate()
|
||
|
|
|
||
|
|
//calculate contact distance for speculative CCD shapes
|
||
|
|
if(1)
|
||
|
|
{
|
||
|
|
PxBitMap::Iterator speculativeCCDIter(mSpeculativeCCDRigidBodyBitMap);
|
||
|
|
|
||
|
|
SpeculativeCCDContactDistanceUpdateTask* ccdTask = createCCDTask(pool, mContextId, mContactDistance->begin(), mDt, *mBoundsArray);
|
||
|
|
|
||
|
|
PxBitMapPinned& changedMap = mAABBManager->getChangedAABBMgActorHandleMap();
|
||
|
|
|
||
|
|
const size_t bodyOffset = PX_OFFSET_OF_RT(BodySim, getLowLevelBody());
|
||
|
|
|
||
|
|
//printf("\n");
|
||
|
|
//PxU32 count = 0;
|
||
|
|
|
||
|
|
PxU32 nbBodies = 0;
|
||
|
|
PxU32 index;
|
||
|
|
while((index = speculativeCCDIter.getNext()) != PxBitMap::Iterator::DONE)
|
||
|
|
{
|
||
|
|
PxsRigidBody* rigidBody = getRigidBodyFromIG(islandSim, PxNodeIndex(index));
|
||
|
|
BodySim* bodySim = reinterpret_cast<BodySim*>(reinterpret_cast<PxU8*>(rigidBody)-bodyOffset);
|
||
|
|
if(bodySim)
|
||
|
|
{
|
||
|
|
//printf("%d\n", bodySim->getActiveListIndex());
|
||
|
|
//printf("%d: %d\n", count++, bodySim->isActive());
|
||
|
|
|
||
|
|
hasContactDistanceChanged = true;
|
||
|
|
ccdTask->mBodySims[nbBodies++] = bodySim;
|
||
|
|
|
||
|
|
// PT: ### changedMap pattern #1
|
||
|
|
// PT: TODO: isn't there a problem here? The task function will only touch the shapes whose body has the
|
||
|
|
// speculative flag and isn't frozen, but here we mark all shapes as changed no matter what.
|
||
|
|
//
|
||
|
|
// Also we test some bodySim data and one bit of each ShapeSim here, not great.
|
||
|
|
PxU32 nbElems = bodySim->getNbElements();
|
||
|
|
ElementSim** elems = bodySim->getElements();
|
||
|
|
while(nbElems--)
|
||
|
|
{
|
||
|
|
ShapeSim* sim = static_cast<ShapeSim*>(*elems++);
|
||
|
|
if(sim->getFlags() & PxShapeFlag::eSIMULATION_SHAPE)
|
||
|
|
changedMap.growAndSet(sim->getElementID());
|
||
|
|
}
|
||
|
|
|
||
|
|
// PT: TODO: better load balancing?
|
||
|
|
if(nbBodies == SpeculativeCCDContactDistanceUpdateTask::MaxBodies)
|
||
|
|
{
|
||
|
|
ccdTask->mNbBodies = nbBodies;
|
||
|
|
nbBodies = 0;
|
||
|
|
startTask(ccdTask, continuation);
|
||
|
|
|
||
|
|
if(continuation)
|
||
|
|
ccdTask = createCCDTask(pool, mContextId, mContactDistance->begin(), mDt, *mBoundsArray);
|
||
|
|
else
|
||
|
|
ccdTask->mNbBodies = 0; // PT: no need to create a new task in single-threaded mode
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(nbBodies)
|
||
|
|
{
|
||
|
|
ccdTask->mNbBodies = nbBodies;
|
||
|
|
startTask(ccdTask, continuation);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/* else
|
||
|
|
{
|
||
|
|
// PT: codepath without mSpeculativeCCDRigidBodyBitMap
|
||
|
|
PxU32 nb = mActiveBodies.size();
|
||
|
|
BodyCore** bodies = mActiveBodies.begin();
|
||
|
|
|
||
|
|
while(nb--)
|
||
|
|
{
|
||
|
|
const BodyCore* current = *bodies++;
|
||
|
|
BodySim* bodySim = current->getSim();
|
||
|
|
if(bodySim)
|
||
|
|
{
|
||
|
|
...
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}*/
|
||
|
|
|
||
|
|
//calculate contact distance for articulation links
|
||
|
|
{
|
||
|
|
PxBitMap::Iterator articulateCCDIter(mSpeculativeCDDArticulationBitMap);
|
||
|
|
PxU32 index;
|
||
|
|
while((index = articulateCCDIter.getNext()) != PxBitMap::Iterator::DONE)
|
||
|
|
{
|
||
|
|
ArticulationSim* articulationSim = getArticulationSim(islandSim, PxNodeIndex(index));
|
||
|
|
if(articulationSim)
|
||
|
|
{
|
||
|
|
hasContactDistanceChanged = true;
|
||
|
|
if(continuation)
|
||
|
|
{
|
||
|
|
SpeculativeCCDContactDistanceArticulationUpdateTask* articulationUpdateTask = PX_PLACEMENT_NEW(pool.allocate(sizeof(SpeculativeCCDContactDistanceArticulationUpdateTask)), SpeculativeCCDContactDistanceArticulationUpdateTask)(mContextId, mContactDistance->begin(), mDt, *mBoundsArray, articulationSim);
|
||
|
|
articulationUpdateTask->setContinuation(continuation);
|
||
|
|
articulationUpdateTask->removeReference();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
articulationSim->updateContactDistance(mContactDistance->begin(), mDt, *mBoundsArray);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mHasContactDistanceChanged = hasContactDistanceChanged;
|
||
|
|
}
|
||
|
|
|
||
|
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
#include "ScNPhaseCore.h"
|
||
|
|
#include "ScShapeInteraction.h"
|
||
|
|
#include "PxsCCD.h"
|
||
|
|
#include "PxsSimulationController.h"
|
||
|
|
#include "CmTransformUtils.h"
|
||
|
|
|
||
|
|
void Sc::Scene::setCCDContactModifyCallback(PxCCDContactModifyCallback* callback)
|
||
|
|
{
|
||
|
|
mCCDContext->setCCDContactModifyCallback(callback);
|
||
|
|
}
|
||
|
|
|
||
|
|
PxCCDContactModifyCallback* Sc::Scene::getCCDContactModifyCallback() const
|
||
|
|
{
|
||
|
|
return mCCDContext->getCCDContactModifyCallback();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::setCCDMaxPasses(PxU32 ccdMaxPasses)
|
||
|
|
{
|
||
|
|
mCCDContext->setCCDMaxPasses(ccdMaxPasses);
|
||
|
|
}
|
||
|
|
|
||
|
|
PxU32 Sc::Scene::getCCDMaxPasses() const
|
||
|
|
{
|
||
|
|
return mCCDContext->getCCDMaxPasses();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::setCCDThreshold(PxReal t)
|
||
|
|
{
|
||
|
|
mCCDContext->setCCDThreshold(t);
|
||
|
|
}
|
||
|
|
|
||
|
|
PxReal Sc::Scene::getCCDThreshold() const
|
||
|
|
{
|
||
|
|
return mCCDContext->getCCDThreshold();
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::collectPostSolverVelocitiesBeforeCCD()
|
||
|
|
{
|
||
|
|
if(mContactReportsNeedPostSolverVelocity)
|
||
|
|
{
|
||
|
|
ActorPairReport*const* actorPairs = mNPhaseCore->getContactReportActorPairs();
|
||
|
|
PxU32 nbActorPairs = mNPhaseCore->getNbContactReportActorPairs();
|
||
|
|
for(PxU32 i=0; i < nbActorPairs; i++)
|
||
|
|
{
|
||
|
|
if(i < (nbActorPairs - 1))
|
||
|
|
PxPrefetchLine(actorPairs[i+1]);
|
||
|
|
|
||
|
|
ActorPairReport* aPair = actorPairs[i];
|
||
|
|
|
||
|
|
ContactStreamManager& cs = aPair->getContactStreamManager();
|
||
|
|
|
||
|
|
PxU32 streamManagerFlag = cs.getFlags();
|
||
|
|
if(streamManagerFlag & ContactStreamManagerFlag::eINVALID_STREAM)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
PxU8* stream = mNPhaseCore->getContactReportPairData(cs.bufferIndex);
|
||
|
|
|
||
|
|
if(i + 1 < nbActorPairs)
|
||
|
|
PxPrefetch(&(actorPairs[i+1]->getContactStreamManager()));
|
||
|
|
|
||
|
|
if(!cs.extraDataSize)
|
||
|
|
continue;
|
||
|
|
else if (streamManagerFlag & ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY)
|
||
|
|
cs.setContactReportPostSolverVelocity(stream, aPair->getActorA(), aPair->getActorB());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::updateCCDMultiPass(PxBaseTask* parentContinuation)
|
||
|
|
{
|
||
|
|
getCcdBodies().forceSize_Unsafe(mSimulationControllerCallback->getNbCcdBodies());
|
||
|
|
|
||
|
|
// second run of the broadphase for making sure objects we have integrated did not tunnel.
|
||
|
|
if(mPublicFlags & PxSceneFlag::eENABLE_CCD)
|
||
|
|
{
|
||
|
|
if(mContactReportsNeedPostSolverVelocity)
|
||
|
|
{
|
||
|
|
// the CCD code will overwrite the post solver body velocities, hence, we need to extract the info
|
||
|
|
// first if any CCD enabled pair requested it.
|
||
|
|
collectPostSolverVelocitiesBeforeCCD();
|
||
|
|
}
|
||
|
|
|
||
|
|
//We use 2 CCD task chains to be able to chain together an arbitrary number of ccd passes
|
||
|
|
if(mPostCCDPass.size() != 2)
|
||
|
|
{
|
||
|
|
mPostCCDPass.clear();
|
||
|
|
mUpdateCCDSinglePass.clear();
|
||
|
|
mCCDBroadPhase.clear();
|
||
|
|
mCCDBroadPhaseAABB.clear();
|
||
|
|
mPostCCDPass.reserve(2);
|
||
|
|
mUpdateCCDSinglePass.reserve(2);
|
||
|
|
mUpdateCCDSinglePass2.reserve(2);
|
||
|
|
mUpdateCCDSinglePass3.reserve(2);
|
||
|
|
mCCDBroadPhase.reserve(2);
|
||
|
|
mCCDBroadPhaseAABB.reserve(2);
|
||
|
|
for (int j = 0; j < 2; j++)
|
||
|
|
{
|
||
|
|
mPostCCDPass.pushBack(Cm::DelegateTask<Sc::Scene, &Sc::Scene::postCCDPass>(mContextId, this, "ScScene.postCCDPass"));
|
||
|
|
mUpdateCCDSinglePass.pushBack(Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateCCDSinglePass>(mContextId, this, "ScScene.updateCCDSinglePass"));
|
||
|
|
mUpdateCCDSinglePass2.pushBack(Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateCCDSinglePassStage2>(mContextId, this, "ScScene.updateCCDSinglePassStage2"));
|
||
|
|
mUpdateCCDSinglePass3.pushBack(Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateCCDSinglePassStage3>(mContextId, this, "ScScene.updateCCDSinglePassStage3"));
|
||
|
|
mCCDBroadPhase.pushBack(Cm::DelegateTask<Sc::Scene, &Sc::Scene::ccdBroadPhase>(mContextId, this, "ScScene.ccdBroadPhase"));
|
||
|
|
mCCDBroadPhaseAABB.pushBack(Cm::DelegateTask<Sc::Scene, &Sc::Scene::ccdBroadPhaseAABB>(mContextId, this, "ScScene.ccdBroadPhaseAABB"));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//reset thread context in a place we know all tasks possibly accessing it, are in sync with. (see US6664)
|
||
|
|
mLLContext->resetThreadContexts();
|
||
|
|
|
||
|
|
mCCDContext->updateCCDBegin();
|
||
|
|
|
||
|
|
mCCDBroadPhase[0].setContinuation(parentContinuation);
|
||
|
|
mCCDBroadPhaseAABB[0].setContinuation(&mCCDBroadPhase[0]);
|
||
|
|
mCCDBroadPhase[0].removeReference();
|
||
|
|
mCCDBroadPhaseAABB[0].removeReference();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
class UpdateCCDBoundsTask : public Cm::Task
|
||
|
|
{
|
||
|
|
Bp::BoundsArray* mBoundArray;
|
||
|
|
PxsTransformCache* mTransformCache;
|
||
|
|
BodySim** mBodySims;
|
||
|
|
PxU32 mNbToProcess;
|
||
|
|
PxI32* mNumFastMovingShapes;
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
static const PxU32 MaxPerTask = 256;
|
||
|
|
|
||
|
|
UpdateCCDBoundsTask(PxU64 contextID, Bp::BoundsArray* boundsArray, PxsTransformCache* transformCache, BodySim** bodySims, PxU32 nbToProcess, PxI32* numFastMovingShapes) :
|
||
|
|
Cm::Task (contextID),
|
||
|
|
mBoundArray (boundsArray),
|
||
|
|
mTransformCache (transformCache),
|
||
|
|
mBodySims (bodySims),
|
||
|
|
mNbToProcess (nbToProcess),
|
||
|
|
mNumFastMovingShapes(numFastMovingShapes)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual const char* getName() const { return "UpdateCCDBoundsTask";}
|
||
|
|
|
||
|
|
PxIntBool updateSweptBounds(ShapeSim* sim, BodySim* body)
|
||
|
|
{
|
||
|
|
PX_ASSERT(body==sim->getBodySim());
|
||
|
|
|
||
|
|
const PxU32 elementID = sim->getElementID();
|
||
|
|
|
||
|
|
const ShapeCore& shapeCore = sim->getCore();
|
||
|
|
const PxTransform& endPose = mTransformCache->getTransformCache(elementID).transform;
|
||
|
|
|
||
|
|
const PxGeometry& shapeGeom = shapeCore.getGeometry();
|
||
|
|
|
||
|
|
const PxsRigidBody& rigidBody = body->getLowLevelBody();
|
||
|
|
const PxsBodyCore& bodyCore = body->getBodyCore().getCore();
|
||
|
|
PX_ALIGN(16, PxTransform shape2World);
|
||
|
|
Cm::getDynamicGlobalPoseAligned(rigidBody.mLastTransform, shapeCore.getShape2Actor(), bodyCore.getBody2Actor(), shape2World);
|
||
|
|
|
||
|
|
const float ccdThreshold = computeCCDThreshold(shapeGeom);
|
||
|
|
PxBounds3 bounds = Gu::computeBounds(shapeGeom, endPose);
|
||
|
|
PxIntBool isFastMoving;
|
||
|
|
if(1)
|
||
|
|
{
|
||
|
|
// PT: this alternative implementation avoids computing the start bounds for slow moving objects.
|
||
|
|
isFastMoving = (shape2World.p - endPose.p).magnitudeSquared() >= ccdThreshold * ccdThreshold ? 1 : 0;
|
||
|
|
if(isFastMoving)
|
||
|
|
{
|
||
|
|
const PxBounds3 startBounds = Gu::computeBounds(shapeGeom, shape2World);
|
||
|
|
bounds.include(startBounds);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
const PxBounds3 startBounds = Gu::computeBounds(shapeGeom, shape2World);
|
||
|
|
|
||
|
|
isFastMoving = (startBounds.getCenter() - bounds.getCenter()).magnitudeSquared() >= ccdThreshold * ccdThreshold ? 1 : 0;
|
||
|
|
|
||
|
|
if(isFastMoving)
|
||
|
|
bounds.include(startBounds);
|
||
|
|
}
|
||
|
|
|
||
|
|
PX_ASSERT(bounds.minimum.x <= bounds.maximum.x
|
||
|
|
&& bounds.minimum.y <= bounds.maximum.y
|
||
|
|
&& bounds.minimum.z <= bounds.maximum.z);
|
||
|
|
|
||
|
|
mBoundArray->setBounds(bounds, elementID);
|
||
|
|
|
||
|
|
return isFastMoving;
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual void runInternal()
|
||
|
|
{
|
||
|
|
PxU32 activeShapes = 0;
|
||
|
|
const PxU32 nb = mNbToProcess;
|
||
|
|
for(PxU32 i=0; i<nb; i++)
|
||
|
|
{
|
||
|
|
PxU32 isFastMoving = 0;
|
||
|
|
BodySim& bodySim = *mBodySims[i];
|
||
|
|
|
||
|
|
PxU32 nbElems = bodySim.getNbElements();
|
||
|
|
ElementSim** elems = bodySim.getElements();
|
||
|
|
while(nbElems--)
|
||
|
|
{
|
||
|
|
ShapeSim* sim = static_cast<ShapeSim*>(*elems++);
|
||
|
|
if(sim->getFlags() & PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE))
|
||
|
|
{
|
||
|
|
const PxIntBool fastMovingShape = updateSweptBounds(sim, &bodySim);
|
||
|
|
activeShapes += fastMovingShape;
|
||
|
|
|
||
|
|
isFastMoving = isFastMoving | fastMovingShape;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bodySim.getLowLevelBody().getCore().isFastMoving = isFastMoving!=0;
|
||
|
|
}
|
||
|
|
|
||
|
|
PxAtomicAdd(mNumFastMovingShapes, PxI32(activeShapes));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::ccdBroadPhaseAABB(PxBaseTask* continuation)
|
||
|
|
{
|
||
|
|
PX_PROFILE_START_CROSSTHREAD("Sim.ccdBroadPhaseComplete", mContextId);
|
||
|
|
PX_PROFILE_ZONE("Sim.ccdBroadPhaseAABB", mContextId);
|
||
|
|
PX_UNUSED(continuation);
|
||
|
|
|
||
|
|
PxU32 currentPass = mCCDContext->getCurrentCCDPass();
|
||
|
|
|
||
|
|
Cm::FlushPool& flushPool = mLLContext->getTaskPool();
|
||
|
|
|
||
|
|
mNumFastMovingShapes = 0;
|
||
|
|
|
||
|
|
//If we are on the 1st pass or we had some sweep hits previous CCD pass, we need to run CCD again
|
||
|
|
if(currentPass == 0 || mCCDContext->getNumSweepHits())
|
||
|
|
{
|
||
|
|
PxsTransformCache& transformCache = getLowLevelContext()->getTransformCache();
|
||
|
|
for(PxU32 i = 0; i < mCcdBodies.size(); i+= UpdateCCDBoundsTask::MaxPerTask)
|
||
|
|
{
|
||
|
|
const PxU32 nbToProcess = PxMin(UpdateCCDBoundsTask::MaxPerTask, mCcdBodies.size() - i);
|
||
|
|
UpdateCCDBoundsTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(UpdateCCDBoundsTask)), UpdateCCDBoundsTask)(mContextId, mBoundsArray, &transformCache, &mCcdBodies[i], nbToProcess, &mNumFastMovingShapes);
|
||
|
|
task->setContinuation(continuation);
|
||
|
|
task->removeReference();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::ccdBroadPhase(PxBaseTask* continuation)
|
||
|
|
{
|
||
|
|
PX_PROFILE_ZONE("Sim.ccdBroadPhase", mContextId);
|
||
|
|
|
||
|
|
PxU32 currentPass = mCCDContext->getCurrentCCDPass();
|
||
|
|
const PxU32 ccdMaxPasses = mCCDContext->getCCDMaxPasses();
|
||
|
|
mCCDPass = currentPass+1;
|
||
|
|
|
||
|
|
//If we are on the 1st pass or we had some sweep hits previous CCD pass, we need to run CCD again
|
||
|
|
if( (currentPass == 0 || mCCDContext->getNumSweepHits()) && mNumFastMovingShapes != 0)
|
||
|
|
{
|
||
|
|
const PxU32 currIndex = currentPass & 1;
|
||
|
|
const PxU32 nextIndex = 1 - currIndex;
|
||
|
|
//Initialize the CCD task chain unless this is the final pass
|
||
|
|
if(currentPass != (ccdMaxPasses - 1))
|
||
|
|
{
|
||
|
|
mCCDBroadPhase[nextIndex].setContinuation(continuation);
|
||
|
|
mCCDBroadPhaseAABB[nextIndex].setContinuation(&mCCDBroadPhase[nextIndex]);
|
||
|
|
}
|
||
|
|
mPostCCDPass[currIndex].setContinuation(currentPass == ccdMaxPasses-1 ? continuation : &mCCDBroadPhaseAABB[nextIndex]);
|
||
|
|
mUpdateCCDSinglePass3[currIndex].setContinuation(&mPostCCDPass[currIndex]);
|
||
|
|
mUpdateCCDSinglePass2[currIndex].setContinuation(&mUpdateCCDSinglePass3[currIndex]);
|
||
|
|
mUpdateCCDSinglePass[currIndex].setContinuation(&mUpdateCCDSinglePass2[currIndex]);
|
||
|
|
|
||
|
|
//Do the actual broad phase
|
||
|
|
PxBaseTask* continuationTask = &mUpdateCCDSinglePass[currIndex];
|
||
|
|
// const PxU32 numCpuTasks = continuationTask->getTaskManager()->getCpuDispatcher()->getWorkerCount();
|
||
|
|
|
||
|
|
mCCDBp = true;
|
||
|
|
|
||
|
|
setupBroadPhaseFirstAndSecondPassTasks(continuationTask);
|
||
|
|
|
||
|
|
//mAABBManager->updateAABBsAndBP(numCpuTasks, mLLContext->getTaskPool(), &mLLContext->getScratchAllocator(), false, continuationTask, NULL);
|
||
|
|
|
||
|
|
//Allow the CCD task chain to continue
|
||
|
|
mPostCCDPass[currIndex].removeReference();
|
||
|
|
mUpdateCCDSinglePass3[currIndex].removeReference();
|
||
|
|
mUpdateCCDSinglePass2[currIndex].removeReference();
|
||
|
|
mUpdateCCDSinglePass[currIndex].removeReference();
|
||
|
|
if(currentPass != (ccdMaxPasses - 1))
|
||
|
|
{
|
||
|
|
mCCDBroadPhase[nextIndex].removeReference();
|
||
|
|
mCCDBroadPhaseAABB[nextIndex].removeReference();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (currentPass == 0)
|
||
|
|
{
|
||
|
|
PX_PROFILE_STOP_CROSSTHREAD("Sim.ccdBroadPhaseComplete", mContextId);
|
||
|
|
mCCDContext->resetContactManagers();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::updateCCDSinglePass(PxBaseTask* continuation)
|
||
|
|
{
|
||
|
|
PX_PROFILE_ZONE("Sim.updateCCDSinglePass", mContextId);
|
||
|
|
mReportShapePairTimeStamp++; // This will makes sure that new report pairs will get created instead of re-using the existing ones.
|
||
|
|
|
||
|
|
mAABBManager->postBroadPhase(NULL, *getFlushPool());
|
||
|
|
finishBroadPhase(continuation);
|
||
|
|
|
||
|
|
const PxU32 currentPass = mCCDContext->getCurrentCCDPass() + 1; // 0 is reserved for discrete collision phase
|
||
|
|
if(currentPass == 1) // reset the handle map so we only update CCD objects from here on
|
||
|
|
{
|
||
|
|
PxBitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap();
|
||
|
|
//changedAABBMgrActorHandles.clear();
|
||
|
|
for(PxU32 i = 0; i < mCcdBodies.size();i++)
|
||
|
|
{
|
||
|
|
// PT: ### changedMap pattern #1
|
||
|
|
PxU32 nbElems = mCcdBodies[i]->getNbElements();
|
||
|
|
ElementSim** elems = mCcdBodies[i]->getElements();
|
||
|
|
while(nbElems--)
|
||
|
|
{
|
||
|
|
ShapeSim* sim = static_cast<ShapeSim*>(*elems++);
|
||
|
|
if(sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) // TODO: need trigger shape here?
|
||
|
|
changedAABBMgrActorHandles.growAndSet(sim->getElementID());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::updateCCDSinglePassStage2(PxBaseTask* continuation)
|
||
|
|
{
|
||
|
|
PX_PROFILE_ZONE("Sim.updateCCDSinglePassStage2", mContextId);
|
||
|
|
postBroadPhaseStage2(continuation);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::updateCCDSinglePassStage3(PxBaseTask* continuation)
|
||
|
|
{
|
||
|
|
PX_PROFILE_ZONE("Sim.updateCCDSinglePassStage3", mContextId);
|
||
|
|
mReportShapePairTimeStamp++; // This will makes sure that new report pairs will get created instead of re-using the existing ones.
|
||
|
|
|
||
|
|
const PxU32 currentPass = mCCDContext->getCurrentCCDPass() + 1; // 0 is reserved for discrete collision phase
|
||
|
|
finishBroadPhaseStage2(currentPass);
|
||
|
|
PX_PROFILE_STOP_CROSSTHREAD("Sim.ccdBroadPhaseComplete", mContextId);
|
||
|
|
|
||
|
|
//reset thread context in a place we know all tasks possibly accessing it, are in sync with. (see US6664)
|
||
|
|
mLLContext->resetThreadContexts();
|
||
|
|
|
||
|
|
mCCDContext->updateCCD(mDt, continuation, mSimpleIslandManager->getAccurateIslandSim(), (mPublicFlags & PxSceneFlag::eDISABLE_CCD_RESWEEP), mNumFastMovingShapes);
|
||
|
|
}
|
||
|
|
|
||
|
|
static PX_FORCE_INLINE Sc::ShapeInteraction* getSI(PxvContactManagerTouchEvent& evt)
|
||
|
|
{
|
||
|
|
return reinterpret_cast<Sc::ShapeInteraction*>(evt.getCMTouchEventUserData());
|
||
|
|
}
|
||
|
|
|
||
|
|
void Sc::Scene::postCCDPass(PxBaseTask* /*continuation*/)
|
||
|
|
{
|
||
|
|
// - Performs sleep check
|
||
|
|
// - Updates touch flags
|
||
|
|
|
||
|
|
PxU32 currentPass = mCCDContext->getCurrentCCDPass();
|
||
|
|
PX_ASSERT(currentPass > 0); // to make sure changes to the CCD pass counting get noticed. For contact reports, 0 means discrete collision phase.
|
||
|
|
|
||
|
|
PxU32 newTouchCount, lostTouchCount, ccdTouchCount;
|
||
|
|
mLLContext->getManagerTouchEventCount(&newTouchCount, &lostTouchCount, &ccdTouchCount);
|
||
|
|
PX_ALLOCA(newTouches, PxvContactManagerTouchEvent, newTouchCount);
|
||
|
|
PX_ALLOCA(lostTouches, PxvContactManagerTouchEvent, lostTouchCount);
|
||
|
|
PX_ALLOCA(ccdTouches, PxvContactManagerTouchEvent, ccdTouchCount);
|
||
|
|
|
||
|
|
PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs();
|
||
|
|
|
||
|
|
// Note: For contact notifications it is important that the new touch pairs get processed before the lost touch pairs.
|
||
|
|
// This allows to know for sure if a pair of actors lost all touch (see eACTOR_PAIR_LOST_TOUCH).
|
||
|
|
mLLContext->fillManagerTouchEvents(newTouches, newTouchCount, lostTouches, lostTouchCount, ccdTouches, ccdTouchCount);
|
||
|
|
for(PxU32 i=0; i<newTouchCount; ++i)
|
||
|
|
{
|
||
|
|
ShapeInteraction* si = getSI(newTouches[i]);
|
||
|
|
PX_ASSERT(si);
|
||
|
|
mNPhaseCore->managerNewTouch(*si);
|
||
|
|
si->managerNewTouch(currentPass, outputs);
|
||
|
|
if (!si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED))
|
||
|
|
{
|
||
|
|
mSimpleIslandManager->setEdgeConnected(si->getEdgeIndex(), IG::Edge::eCONTACT_MANAGER);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for(PxU32 i=0; i<lostTouchCount; ++i)
|
||
|
|
{
|
||
|
|
ShapeInteraction* si = getSI(lostTouches[i]);
|
||
|
|
PX_ASSERT(si);
|
||
|
|
if (si->managerLostTouch(currentPass, outputs) && !si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED))
|
||
|
|
addToLostTouchList(si->getActor0(), si->getActor1());
|
||
|
|
|
||
|
|
mSimpleIslandManager->setEdgeDisconnected(si->getEdgeIndex());
|
||
|
|
}
|
||
|
|
for(PxU32 i=0; i<ccdTouchCount; ++i)
|
||
|
|
{
|
||
|
|
ShapeInteraction* si = getSI(ccdTouches[i]);
|
||
|
|
PX_ASSERT(si);
|
||
|
|
si->sendCCDRetouch(currentPass, outputs);
|
||
|
|
}
|
||
|
|
checkForceThresholdContactEvents(currentPass);
|
||
|
|
{
|
||
|
|
PxBitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap();
|
||
|
|
|
||
|
|
for (PxU32 i = 0, s = mCcdBodies.size(); i < s; i++)
|
||
|
|
{
|
||
|
|
BodySim*const body = mCcdBodies[i];
|
||
|
|
if(i+8 < s)
|
||
|
|
PxPrefetch(mCcdBodies[i+8], 512);
|
||
|
|
|
||
|
|
PX_ASSERT(body->getBody2World().p.isFinite());
|
||
|
|
PX_ASSERT(body->getBody2World().q.isFinite());
|
||
|
|
|
||
|
|
body->updateCached(&changedAABBMgrActorHandles);
|
||
|
|
}
|
||
|
|
|
||
|
|
ArticulationCore* const* articList = mArticulations.getEntries();
|
||
|
|
for(PxU32 i=0;i<mArticulations.size();i++)
|
||
|
|
articList[i]->getSim()->updateCached(&changedAABBMgrActorHandles);
|
||
|
|
}
|
||
|
|
}
|