aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/LowLevel/software/src/PxsContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'PhysX_3.4/Source/LowLevel/software/src/PxsContext.cpp')
-rw-r--r--PhysX_3.4/Source/LowLevel/software/src/PxsContext.cpp641
1 files changed, 641 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/LowLevel/software/src/PxsContext.cpp b/PhysX_3.4/Source/LowLevel/software/src/PxsContext.cpp
new file mode 100644
index 00000000..0e18355b
--- /dev/null
+++ b/PhysX_3.4/Source/LowLevel/software/src/PxsContext.cpp
@@ -0,0 +1,641 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 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 "foundation/PxProfiler.h"
+#include "PxvConfig.h"
+#include "PxcContactCache.h"
+#include "PxsRigidBody.h"
+#include "PxsContactManager.h"
+#include "PxsContext.h"
+#include "PxPhysXConfig.h"
+
+#include "CmBitMap.h"
+#include "CmFlushPool.h"
+
+#include "PxsMaterialManager.h"
+#include "PxSceneDesc.h"
+#include "PxsCCD.h"
+#include "PxvGeometry.h"
+#include "PxvManager.h"
+#include "PxsSimpleIslandManager.h"
+
+#if PX_SUPPORT_GPU_PHYSX
+#include "task/PxGpuDispatcher.h"
+#include "PxPhysXGpu.h"
+#endif
+
+#include "PxcNpContactPrepShared.h"
+#include "PxcNpCache.h"
+
+
+using namespace physx;
+using namespace physx::shdfnd;
+
+#define PXS_CONTACTMANAGER_SLABSIZE 1024
+#define PXS_MAX_CONTACTMANAGER_SLABS 64
+
+#define PXS_BODYSHAPE_SLABSIZE 1024
+#define PXS_MAX_BODYSHAPE_SLABS 16
+
+
+void PxsCMUpdateTask::release()
+{
+ // We used to do Task::release(); here before fixing DE1106 (xbox pure virtual crash)
+ // Release in turn causes the dependent tasks to start running
+ // The problem was that between the time release was called and by the time we got to the destructor
+ // The task chain would get all the way to scene finalization code which would reset the allocation pool
+ // And a new task would get allocated at the same address, then we would invoke the destructor on that freshly created task
+ // This could potentially cause any number of other problems, it is suprising that it only manifested itself
+ // as a pure virtual crash
+ PxBaseTask* saveContinuation = mCont;
+ this->~PxsCMUpdateTask();
+ if (saveContinuation)
+ saveContinuation->removeReference();
+}
+
+
+
+PxsContext::PxsContext(const PxSceneDesc& desc, PxTaskManager* taskManager, Cm::FlushPool& taskPool, PxU64 contextID) :
+ mNpThreadContextPool (this),
+ mContactManagerPool ("mContactManagerPool", this, 256, 8192),
+ mManifoldPool ("mManifoldPool", 256),
+ mSphereManifoldPool ("mSphereManifoldPool", 256),
+ mContactModifyCallback (NULL),
+ mNpImplementationContext (NULL),
+ mNpFallbackImplementationContext(NULL),
+ mTaskManager (taskManager),
+ mTaskPool (taskPool),
+ mPCM (desc.flags & PxSceneFlag::eENABLE_PCM),
+ mContactCache (false),
+ mCreateAveragePoint (desc.flags & PxSceneFlag::eENABLE_AVERAGE_POINT),
+ mContextID (contextID)
+{
+ clearManagerTouchEvents();
+ mVisualizationCullingBox.setMaximal();
+
+ PxMemZero(mVisualizationParams, sizeof(PxReal) * PxVisualizationParameter::eNUM_VALUES);
+
+ mNpMemBlockPool.init(desc.nbContactDataBlocks, desc.maxNbContactDataBlocks);
+}
+
+PxsContext::~PxsContext()
+{
+ if(mTransformCache)
+ {
+ mTransformCache->~PxsTransformCache();
+ PX_FREE(mTransformCache);
+ }
+ mTransformCache = NULL;
+
+ mContactManagerPool.destroy(); //manually destroy the contact manager pool, otherwise pool deletion order is random and we can get into trouble with references into other pools needed during destruction.
+}
+
+// =========================== Create methods
+namespace physx
+{
+ bool gEnablePCMCaching[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT] =
+ {
+ //eSPHERE,
+ {
+ false, //eSPHERE
+ false, //ePLANE
+ false, //eCAPSULE
+ false, //eBOX
+ true, //eCONVEXMESH
+ true, //eTRIANGLEMESH
+ true //eHEIGHTFIELD
+ },
+
+ //ePLANE
+ {
+ false, //eSPHERE
+ false, //ePLANE
+ true, //eCAPSULE
+ true, //eBOX
+ true, //eCONVEXMESH
+ false, //eTRIANGLEMESH
+ false //eHEIGHTFIELD
+ },
+
+ //eCAPSULE,
+ {
+ false, //eSPHERE
+ true, //ePLANE
+ false, //eCAPSULE
+ true, //eBOX
+ true, //eCONVEXMESH
+ true, //eTRIANGLEMESH
+ true //eHEIGHTFIELD
+ },
+
+ //eBOX,
+ {
+ false, //eSPHERE
+ true, //ePLANE
+ true, //eCAPSULE
+ true, //eBOX
+ true, //eCONVEXMESH
+ true, //eTRIANGLEMESH
+ true //eHEIGHTFIELD
+ },
+
+ //eCONVEXMESH,
+ {
+ true, //eSPHERE
+ true, //ePLANE
+ true, //eCAPSULE
+ true, //eBOX
+ true, //eCONVEXMESH
+ true, //eTRIANGLEMESH
+ true //eHEIGHTFIELD
+ },
+ //eTRIANGLEMESH,
+ {
+ true, //eSPHERE
+ false, //ePLANE
+ true, //eCAPSULE
+ true, //eBOX
+ true, //eCONVEXMESH
+ false, //eTRIANGLEMESH
+ false //eHEIGHTFIELD
+ },
+
+ //eHEIGHTFIELD,
+ {
+ true, //eSPHERE
+ false, //ePLANE
+ true, //eCAPSULE
+ true, //eBOX
+ true, //eCONVEXMESH
+ false, //eTRIANGLEMESH
+ false //eHEIGHTFIELD
+ }
+ };
+}
+
+void PxsContext::createTransformCache(Ps::VirtualAllocatorCallback& allocatorCallback)
+{
+ mTransformCache = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxsTransformCache), PX_DEBUG_EXP("PxsTransformCache")), PxsTransformCache(allocatorCallback));
+}
+
+PxsContactManager* PxsContext::createContactManager(PxsContactManager* contactManager, const bool useCCD)
+{
+ PxsContactManager* cm = contactManager? contactManager : mContactManagerPool.get();
+
+ if(cm)
+ {
+ PxcNpWorkUnitClearContactState(cm->getWorkUnit());
+ PxcNpWorkUnitClearCachedState(cm->getWorkUnit());
+
+ if (contactManager == NULL)
+ {
+ if (cm->getIndex() >= mActiveContactManager.size())
+ {
+ PxU32 newSize = (2 * cm->getIndex() + 256)&~255;
+ mActiveContactManager.resize(newSize);
+ }
+ mActiveContactManager.set(cm->getIndex());
+
+ if (useCCD)
+ {
+ if (cm->getIndex() >= mActiveContactManagersWithCCD.size())
+ {
+ PxU32 newSize = (2 * cm->getIndex() + 256)&~255;
+ mActiveContactManagersWithCCD.resize(newSize);
+ }
+ mActiveContactManagersWithCCD.set(cm->getIndex());
+ }
+ }
+ }
+ else
+ {
+ PX_WARN_ONCE("Reached limit of contact pairs.");
+ }
+
+ return cm;
+}
+
+void PxsContext::createCache(Gu::Cache& cache, PxsContactManager* cm, PxU8 geomType0, PxU8 geomType1)
+{
+ if(cm)
+ {
+ if(mPCM)
+ {
+ if(gEnablePCMCaching[geomType0][geomType1])
+ {
+ if(geomType0 <= PxGeometryType::eCONVEXMESH &&
+ geomType1 <= PxGeometryType::eCONVEXMESH)
+ {
+ if(geomType0 == PxGeometryType::eSPHERE || geomType1 == PxGeometryType::eSPHERE)
+ {
+ Gu::PersistentContactManifold* manifold = mSphereManifoldPool.allocate();
+ new(manifold) Gu::SpherePersistentContactManifold();
+ cache.setManifold(manifold);
+ }
+ else
+ {
+ Gu::PersistentContactManifold* manifold = mManifoldPool.allocate();
+ new(manifold) Gu::LargePersistentContactManifold();
+ cache.setManifold(manifold);
+
+ }
+ cache.getManifold().clearManifold();
+
+ }
+ else
+ {
+ //ML: raised 1 to indicate the manifold is multiManifold which is for contact gen in mesh/height field
+ //cache.manifold = 1;
+ cache.setMultiManifold(NULL);
+ }
+ }
+ else
+ {
+ //cache.manifold = 0;
+ cache.mCachedData = NULL;
+ cache.mManifoldFlags = 0;
+ }
+ }
+ }
+}
+
+void PxsContext::destroyContactManager(PxsContactManager* cm)
+{
+ const PxU32 idx = cm->getIndex();
+ if (cm->getCCD())
+ mActiveContactManagersWithCCD.growAndReset(idx);
+ mActiveContactManager.growAndReset(idx);
+ mContactManagerTouchEvent.growAndReset(idx);
+ mContactManagerPatchChangeEvent.growAndReset(idx);
+ mContactManagerPool.put(cm);
+}
+
+void PxsContext::destroyCache(Gu::Cache& cache)
+{
+ if(cache.isManifold())
+ {
+ if(!cache.isMultiManifold())
+ {
+ Gu::PersistentContactManifold& manifold = cache.getManifold();
+ if (manifold.mCapacity == GU_SPHERE_MANIFOLD_CACHE_SIZE)
+ {
+ mSphereManifoldPool.deallocate(static_cast<Gu::SpherePersistentContactManifold*>(&manifold));
+ }
+ else
+ {
+ mManifoldPool.deallocate(static_cast<Gu::LargePersistentContactManifold*>(&manifold));
+ }
+ }
+ cache.mCachedData = NULL;
+ cache.mManifoldFlags = 0;
+ }
+}
+
+void PxsContext::setScratchBlock(void* addr, PxU32 size)
+{
+ mScratchAllocator.setBlock(addr, size);
+}
+
+void PxsContext::setContactDistance(Ps::Array<PxReal, Ps::VirtualAllocator>* contactDistance)
+{
+ mContactDistance = contactDistance;
+}
+
+
+void PxsContext::shiftOrigin(const PxVec3& shift)
+{
+ // transform cache
+ mTransformCache->shiftTransforms(-shift);
+
+#if 0
+ if (getContactCacheFlag())
+ {
+ //Iterate all active contact managers
+ Cm::BitMap::Iterator it(mActiveContactManager);
+ PxU32 index = it.getNext();
+ while(index != Cm::BitMap::Iterator::DONE)
+ {
+ PxsContactManager* cm = mContactManagerPool.findByIndexFast(index);
+
+ PxcNpWorkUnit& npwUnit = cm->getWorkUnit();
+
+ // contact cache
+ if(!npwUnit.pairCache.isManifold())
+ {
+ PxU8* contactCachePtr = npwUnit.pairCache.mCachedData;
+ if (contactCachePtr)
+ {
+ PxcLocalContactsCache* lcc;
+ PxU8* contacts = PxcNpCacheRead(npwUnit.pairCache, lcc);
+#ifdef _DEBUG
+ PxcLocalContactsCache testCache;
+ PxU32 testBytes;
+ const PxU8* testPtr = PxcNpCacheRead2(npwUnit.pairCache, testCache, testBytes);
+#endif
+ lcc->mTransform0.p -= shift;
+ lcc->mTransform1.p -= shift;
+
+ const PxU32 nbContacts = lcc->mNbCachedContacts;
+ const bool sameNormal = lcc->mSameNormal;
+ const bool useFaceIndices = lcc->mUseFaceIndices;
+
+ for(PxU32 i=0; i < nbContacts; i++)
+ {
+ if (i != nbContacts-1)
+ Ps::prefetchLine(contacts, 128);
+
+ if(!i || !sameNormal)
+ contacts += sizeof(PxVec3);
+
+ PxVec3* cachedPoint = reinterpret_cast<PxVec3*>(contacts);
+ *cachedPoint -= shift;
+ contacts += sizeof(PxVec3);
+ contacts += sizeof(PxReal);
+
+ if(useFaceIndices)
+ contacts += 2 * sizeof(PxU32);
+ }
+#ifdef _DEBUG
+ PX_ASSERT(contacts == (testPtr + testBytes));
+#endif
+ }
+ }
+
+ index = it.getNext();
+ }
+
+ }
+#endif
+
+ //
+ // adjust visualization culling box
+ //
+ PxBounds3 maximalBounds;
+ maximalBounds.setMaximal();
+ if ((mVisualizationCullingBox.minimum != maximalBounds.minimum) || (mVisualizationCullingBox.maximum != maximalBounds.maximum))
+ {
+ mVisualizationCullingBox.minimum -= shift;
+ mVisualizationCullingBox.maximum -= shift;
+ }
+}
+
+void PxsContext::swapStreams()
+{
+ mNpMemBlockPool.swapNpCacheStreams();
+}
+
+void PxsContext::mergeCMDiscreteUpdateResults(PxBaseTask* /*continuation*/)
+{
+ PX_PROFILE_ZONE("Sim.narrowPhaseMerge", mContextID);
+
+ this->mNpImplementationContext->appendContactManagers();
+
+ //Note: the iterator extracts all the items and returns them to the cache on destruction(for thread safety).
+ PxcThreadCoherentCacheIterator<PxcNpThreadContext, PxcNpContext> threadContextIt(mNpThreadContextPool);
+
+ for(PxcNpThreadContext* threadContext = threadContextIt.getNext(); threadContext; threadContext = threadContextIt.getNext())
+ {
+ mCMTouchEventCount[PXS_LOST_TOUCH_COUNT] += threadContext->getLocalLostTouchCount();
+ mCMTouchEventCount[PXS_NEW_TOUCH_COUNT] += threadContext->getLocalNewTouchCount();
+ mCMTouchEventCount[PXS_PATCH_FOUND_COUNT] += threadContext->getLocalFoundPatchCount();
+ mCMTouchEventCount[PXS_PATCH_LOST_COUNT] += threadContext->getLocalLostPatchCount();
+
+#if PX_ENABLE_SIM_STATS
+ for(PxU32 i=0;i<PxGeometryType::eGEOMETRY_COUNT;i++)
+ {
+ #if PX_DEBUG
+ for(PxU32 j=0; j<i; j++)
+ PX_ASSERT(!threadContext->mDiscreteContactPairs[i][j]);
+ #endif
+ for(PxU32 j=i; j<PxGeometryType::eGEOMETRY_COUNT; j++)
+ {
+ const PxU32 nb = threadContext->mDiscreteContactPairs[i][j];
+ const PxU32 nbModified = threadContext->mModifiedContactPairs[i][j];
+ mSimStats.mNbDiscreteContactPairs[i][j] += nb;
+ mSimStats.mNbModifiedContactPairs[i][j] += nbModified;
+ mSimStats.mNbDiscreteContactPairsTotal += nb;
+ }
+ }
+
+ mSimStats.mNbDiscreteContactPairsWithCacheHits += threadContext->mNbDiscreteContactPairsWithCacheHits;
+ mSimStats.mNbDiscreteContactPairsWithContacts += threadContext->mNbDiscreteContactPairsWithContacts;
+
+ mSimStats.mTotalCompressedContactSize += threadContext->mCompressedCacheSize;
+ //KS - this data is not available yet
+ //mSimStats.mTotalConstraintSize += threadContext->mConstraintSize;
+ threadContext->clearStats();
+#endif
+ mContactManagerTouchEvent.combineInPlace<Cm::BitMap::OR>(threadContext->getLocalChangeTouch());
+ mContactManagerPatchChangeEvent.combineInPlace<Cm::BitMap::OR>(threadContext->getLocalPatchChangeMap());
+ mTotalCompressedCacheSize += threadContext->mTotalCompressedCacheSize;
+ mMaxPatches = PxMax(mMaxPatches, threadContext->mMaxPatches);
+
+ threadContext->mTotalCompressedCacheSize = threadContext->mMaxPatches = 0;
+ }
+}
+
+void PxsContext::setCreateContactStream(bool to)
+{
+ mCreateContactStream = to;
+ PxcThreadCoherentCacheIterator<PxcNpThreadContext, PxcNpContext> threadContextIt(mNpThreadContextPool);
+ for(PxcNpThreadContext* threadContext = threadContextIt.getNext(); threadContext; threadContext = threadContextIt.getNext())
+ {
+ threadContext->setCreateContactStream(to);
+ }
+}
+
+void PxsContext::updateContactManager(PxReal dt, bool hasBoundsArrayChanged, bool hasContactDistanceChanged, PxBaseTask* continuation, PxBaseTask* firstPassContinuation)
+{
+ PX_ASSERT(mNpImplementationContext);
+ mNpImplementationContext->updateContactManager(dt, hasBoundsArrayChanged, hasContactDistanceChanged, continuation, firstPassContinuation);
+}
+
+void PxsContext::secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation)
+{
+ PX_ASSERT(mNpImplementationContext);
+ mNpImplementationContext->secondPassUpdateContactManager(dt, continuation);
+}
+
+void PxsContext::fetchUpdateContactManager()
+{
+ PX_ASSERT(mNpImplementationContext);
+ mNpImplementationContext->fetchUpdateContactManager();
+ mergeCMDiscreteUpdateResults(NULL);
+}
+
+void PxsContext::resetThreadContexts()
+{
+ //Note: the iterator extracts all the items and returns them to the cache on destruction(for thread safety).
+ PxcThreadCoherentCacheIterator<PxcNpThreadContext, PxcNpContext> threadContextIt(mNpThreadContextPool);
+ PxcNpThreadContext* threadContext = threadContextIt.getNext();
+
+ while(threadContext != NULL)
+ {
+ threadContext->reset(mContactManagerTouchEvent.size());
+ threadContext = threadContextIt.getNext();
+ }
+}
+
+bool PxsContext::getManagerTouchEventCount(int* newTouch, int* lostTouch, int* ccdTouch) const
+{
+ if(newTouch)
+ *newTouch = int(mCMTouchEventCount[PXS_NEW_TOUCH_COUNT]);
+
+ if(lostTouch)
+ *lostTouch = int(mCMTouchEventCount[PXS_LOST_TOUCH_COUNT]);
+
+ if(ccdTouch)
+ *ccdTouch = int(mCMTouchEventCount[PXS_CCD_RETOUCH_COUNT]);
+
+ return true;
+}
+
+bool PxsContext::fillManagerTouchEvents(PxvContactManagerTouchEvent* newTouch, PxI32& newTouchCount, PxvContactManagerTouchEvent* lostTouch, PxI32& lostTouchCount,
+ PxvContactManagerTouchEvent* ccdTouch, PxI32& ccdTouchCount)
+{
+ PxU32 index;
+
+ const PxvContactManagerTouchEvent* newTouchStart = newTouch;
+ const PxvContactManagerTouchEvent* lostTouchStart = lostTouch;
+ const PxvContactManagerTouchEvent* ccdTouchStart = ccdTouch;
+
+ const PxvContactManagerTouchEvent* newTouchEnd = newTouch + newTouchCount;
+ const PxvContactManagerTouchEvent* lostTouchEnd = lostTouch + lostTouchCount;
+ const PxvContactManagerTouchEvent* ccdTouchEnd = ccdTouch + ccdTouchCount;
+
+ PX_UNUSED(newTouchEnd);
+ PX_UNUSED(lostTouchEnd);
+ PX_UNUSED(ccdTouchEnd);
+
+ Cm::BitMap::Iterator it(mContactManagerTouchEvent);
+
+ while((index = it.getNext()) != Cm::BitMap::Iterator::DONE)
+ {
+ PxsContactManager* cm = mContactManagerPool.findByIndexFast(index);
+
+ if(cm->getTouchStatus())
+ {
+ if (!cm->getHasCCDRetouch())
+ {
+ PX_ASSERT(newTouch < newTouchEnd);
+ newTouch->manager = cm;
+ newTouch->userData = cm->getUserData();
+ newTouch++;
+ }
+ else
+ {
+ PX_ASSERT(ccdTouch);
+ PX_ASSERT(ccdTouch < ccdTouchEnd);
+ ccdTouch->manager = cm;
+ ccdTouch->userData = cm->getUserData();
+ cm->clearCCDRetouch();
+ ccdTouch++;
+ }
+ }
+ else
+ {
+ PX_ASSERT(lostTouch < lostTouchEnd);
+ lostTouch->manager = cm;
+ lostTouch->userData = cm->getUserData();
+ lostTouch++;
+ }
+ }
+ newTouchCount = PxI32(newTouch - newTouchStart);
+ lostTouchCount = PxI32(lostTouch - lostTouchStart);
+ ccdTouchCount = PxI32(ccdTouch - ccdTouchStart);
+ return true;
+}
+
+
+
+bool PxsContext::fillManagerPatchChangedEvents(PxsContactManager** foundPatch, PxU32& foundPatchCount,
+ PxsContactManager** lostPatch, PxU32& lostPatchCount)
+{
+ Cm::BitMap::Iterator it(mContactManagerPatchChangeEvent);
+
+ PxsContactManagerOutputIterator outputs = mNpImplementationContext->getContactManagerOutputs();
+
+ PxU32 index;
+ PxsContactManager** currFoundPatch = foundPatch;
+ PxsContactManager** currLostPatch = lostPatch;
+ while((index = it.getNext()) != Cm::BitMap::Iterator::DONE)
+ {
+ PxsContactManager* cm = mContactManagerPool.findByIndexFast(index);
+ PxcNpWorkUnit& workUnit = cm->getWorkUnit();
+ PxsContactManagerOutput& output = outputs.getContactManager(workUnit.mNpIndex);
+ if(output.nbPatches > output.prevPatches)
+ {
+ PX_ASSERT(PxU32(currFoundPatch - foundPatch) < foundPatchCount);
+ *currFoundPatch = cm;
+ currFoundPatch++;
+ }
+ else if(output.nbPatches < output.prevPatches)
+ {
+ PX_ASSERT(PxU32(currLostPatch - lostPatch) < lostPatchCount);
+ *currLostPatch = cm;
+ currLostPatch++;
+ }
+ }
+
+ foundPatchCount = PxU32(currFoundPatch - foundPatch);
+ lostPatchCount = PxU32(currLostPatch - lostPatch);
+ return true;
+}
+
+
+void PxsContext::beginUpdate()
+{
+#if PX_ENABLE_SIM_STATS
+ mSimStats.clearAll();
+#endif
+}
+
+
+// Contact manager related
+
+PxReal PxsContext::getVisualizationParameter(PxVisualizationParameter::Enum param) const
+{
+ PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES);
+
+ return mVisualizationParams[param];
+}
+
+void PxsContext::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value)
+{
+ PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES);
+ PX_ASSERT(value >= 0.0f);
+
+ mVisualizationParams[param] = value;
+}
+
+
+
+
+
+