UE4中PhysX BroadPhase(碰撞检测的粗略阶段)
PhysX的BroadPhase(碰撞检测的粗略阶段),具体是用AABB(轴向包围盒)来做碰撞检测
具体算法有两种:SAP(Sweep And Prune)和MBP(Multi Pruning Box,多个剪枝盒)
SAP(Sweep And Prune)
当场景中有大量的物体(大世界有百万级别)时,即使它们已按AABB的三个轴向xyz做了排序,一个动态物体在移动过程与它们进行AABB判断也是一个很耗时操作
每个物体都需要检查是否和其他的物体是否会overlap 注:第一帧是所有的物体,后面的帧是dirty的物体(如新增,移动,旋转的对象)

TG_EndPhysics的卡顿与BpSAP.UpdateWork直接相关



MBP(Multi Pruning Box,多个剪枝盒)
为了解决与物体判断AABB的次数,可以使用MBP来将整个场景划分成多个块,这样动态物体只用与当前所在块中的物体进行AABB判断,大大减少BroadPhase(碰撞检测的粗略阶段)的耗时
注:在PhysX初始化时,对Physics Scene区域进行静态切分。当往Physics Scene添加物体时,会把物体添加到对应的区域(Region)里面去。然后检测物体相交时,只要检测其和当前区域(Region)的物体就可以。 不需要检测整个Physics Scene

全局Default Broadphase Settings

注1:勾上Use MBPOn Client,并设置MBPBounds和MBPNum Subdivs的数值
注2:MBPBounds的Min、Max缺省为0,表示区域为整个场景
注3:MBPNum Subdivs为2,即将MBPBounds划分成2x2,一共4个块
该配置会保存在DefaultEngine.ini中
[/Script/Engine.PhysicsSettings] DefaultBroadphaseSettings=(bUseMBPOnClient=True,bUseMBPOnServer=False,bUseMBPOuterBounds=False,MBPBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=0),MBPOuterBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=0),MBPNumSubdivs=2)
在地图中可以覆写全局Default Broadphase Settings

具体代码详见:UnrealEngine\Engine\Source\Runtime\Engine\Private\PhysicsEngine\PhysScene_PhysX.cpp
void FPhysScene_PhysX::InitPhysScene(const AWorldSettings* Settings) // 每次进入地图都会调用这个函数

PhysX的Scene
/*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\SimulationController\include\ScScene.h **/ /*********************************************************************************************************************************************/ namespace physx { namespace Sc { class Scene : public Ps::UserAllocated { struct SimpleBodyPair { BodySim* body1; BodySim* body2; PxU32 body1ID; PxU32 body2ID; }; PX_NOCOPY(Scene) //--------------------------------------------------------------------------------- // External interface //--------------------------------------------------------------------------------- public: void release(); // Scene释放 PX_FORCE_INLINE void setGravity(const PxVec3& g) { mGravity = g; mBodyGravityDirty = true; } PX_FORCE_INLINE PxVec3 getGravity() const { return mGravity; } PX_FORCE_INLINE void setElapsedTime(const PxReal t) { mDt = t; mOneOverDt = t > 0.0f ? 1.0f/t : 0.0f; } void setBounceThresholdVelocity(const PxReal t); PxReal getBounceThresholdVelocity() const; PX_FORCE_INLINE void setPublicFlags(PxSceneFlags flags) { mPublicFlags = flags; } PX_FORCE_INLINE PxSceneFlags getPublicFlags() const { return mPublicFlags; } void setFrictionType(PxFrictionType::Enum model); PxFrictionType::Enum getFrictionType() const; void setPCM(bool enabled); void setContactCache(bool enabled); void addStatic(StaticCore&, void*const *shapes, PxU32 nbShapes, size_t shapePtrOffset, PxBounds3* uninflatedBounds); void removeStatic(StaticCore&, Ps::InlineArray<const Sc::ShapeCore*,64>& removedShapes, bool wakeOnLostTouch); void addBody(BodyCore&, void*const *shapes, PxU32 nbShapes, size_t shapePtrOffset, PxBounds3* uninflatedBounds); void removeBody(BodyCore&, Ps::InlineArray<const Sc::ShapeCore*,64>& removedShapes, bool wakeOnLostTouch); // Batch insertion API. // the bounds generated here are the uninflated bounds for the shapes, *if* they are trigger or sim shapes. // It's up to the caller to ensure the bounds array is big enough. // Some care is required in handling these since sim and SQ tweak the bounds in slightly different ways. void startBatchInsertion(BatchInsertionState&); void addStatic(PxActor* actor, BatchInsertionState&, PxBounds3* outBounds); void addBody(PxActor* actor, BatchInsertionState&, PxBounds3* outBounds); void finishBatchInsertion(BatchInsertionState&); // Batch remove helpers PX_FORCE_INLINE void setBatchRemove(BatchRemoveState* bs) { mBatchRemoveState = bs; } PX_FORCE_INLINE BatchRemoveState* getBatchRemove() const { return mBatchRemoveState; } void prefetchForRemove(const BodyCore& ) const; void prefetchForRemove(const StaticCore& ) const; void addConstraint(ConstraintCore&, RigidCore*, RigidCore*); void removeConstraint(ConstraintCore&); void addArticulation(ArticulationCore&, BodyCore& root); void removeArticulation(ArticulationCore&); void addArticulationJoint(ArticulationJointCore&, BodyCore& parent, BodyCore& child); void removeArticulationJoint(ArticulationJointCore&); #if PX_USE_PARTICLE_SYSTEM_API void addParticleSystem(ParticleSystemCore&); void removeParticleSystem(ParticleSystemCore&, bool isRelease); PxU32 getNbParticleSystems() const; ParticleSystemCore* const* getParticleSystems(); #endif bool hasParticleSystems() const; #if PX_USE_CLOTH_API bool addCloth(ClothCore&); void removeCloth(ClothCore&); #endif bool hasCloths() const; PxU32 getNbArticulations() const; Sc::ArticulationCore* const* getArticulations(); PxU32 getNbConstraints() const; Sc::ConstraintCore*const * getConstraints(); void initContactsIterator(ContactIterator&, PxsContactManagerOutputIterator&); // Simulation events void setSimulationEventCallback(PxSimulationEventCallback* callback, PxClientID client); PxSimulationEventCallback* getSimulationEventCallback(PxClientID client) const; // Contact modification void setContactModifyCallback(PxContactModifyCallback* callback); PxContactModifyCallback* getContactModifyCallback() const; void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback); PxCCDContactModifyCallback* getCCDContactModifyCallback() const; void setCCDMaxPasses(PxU32 ccdMaxPasses); PxU32 getCCDMaxPasses() const; // Broad-phase callback void setBroadPhaseCallback(PxBroadPhaseCallback* callback, PxClientID client); PxBroadPhaseCallback* getBroadPhaseCallback(PxClientID client) const; // Broad-phase management void finishBroadPhase(const PxU32 ccdPass, PxBaseTask* continuation); void finishBroadPhaseStage2(const PxU32 ccdPass); void preallocateContactManagers(PxBaseTask* continuation); void islandInsertion(PxBaseTask* continuation); void registerContactManagers(PxBaseTask* continuation); void registerInteractions(PxBaseTask* continuation); void registerSceneInteractions(PxBaseTask* continuation); void secondPassNarrowPhase(PxBaseTask* continuation); // Groups void setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance); PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const; void setSolverBatchSize(PxU32 solverBatchSize); PxU32 getSolverBatchSize() const; void setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; void setVisualizationCullingBox(const PxBounds3& box); const PxBounds3& getVisualizationCullingBox() const; // Run void simulate(PxReal timeStep, PxBaseTask* continuation); void advance(PxReal timeStep, PxBaseTask* continuation); void collide(PxReal timeStep, PxBaseTask* continuation); void endSimulation(); void flush(bool sendPendingReports); void fireBrokenConstraintCallbacks(); void fireTriggerCallbacks(); void fireQueuedContactCallbacks(bool asPartOfFlush); void fireOnAdvanceCallback(); const Ps::Array<PxContactPairHeader>& getQueuedContactPairHeaders(); bool fireOutOfBoundsCallbacks(); void prepareOutOfBoundsCallbacks(); void postCallbacksPreSync(); void postReportsCleanup(); void fireCallbacksPostSync(); void syncSceneQueryBounds(SqBoundsSync& sync, SqRefFinder& finder); PxU32 getDefaultContactReportStreamBufferSize() const; PxReal getFrictionOffsetThreshold() const; PX_FORCE_INLINE void setLimits(const PxSceneLimits& limits) { mLimits = limits; } PX_FORCE_INLINE const PxSceneLimits& getLimits() const { return mLimits; } void visualizeStartStep(); void visualizeEndStep(); Cm::RenderBuffer& getRenderBuffer(); void setNbContactDataBlocks(PxU32 blockCount); PxU32 getNbContactDataBlocksUsed() const; PxU32 getMaxNbContactDataBlocksUsed() const; PxU32 getMaxNbConstraintDataBlocksUsed() const; void setScratchBlock(void* addr, PxU32 size); // PX_ENABLE_SIM_STATS void getStats(PxSimulationStatistics& stats) const; PX_FORCE_INLINE SimStats& getStatsInternal() { return *mStats; } // PX_ENABLE_SIM_STATS PX_DEPRECATED void buildActiveTransforms(); PX_DEPRECATED PxActiveTransform* getActiveTransforms(PxU32& nbTransformsOut, PxClientID client); void buildActiveActors(); PxActor** getActiveActors(PxU32& nbActorsOut, PxClientID client); void finalizeContactStreamAndCreateHeader(PxContactPairHeader& header, const ActorPairReport& aPair, ContactStreamManager& cs, PxU32 removedShapeTestMask); PxClientID createClient(); void setClientBehaviorFlags(PxClientID client, PxClientBehaviorFlags clientBehaviorFlags); PxClientBehaviorFlags getClientBehaviorFlags(PxClientID client) const; #if PX_USE_CLOTH_API void setClothInterCollisionDistance(PxF32 distance); PxF32 getClothInterCollisionDistance() const; void setClothInterCollisionStiffness(PxF32 stiffness); PxF32 getClothInterCollisionStiffness() const; void setClothInterCollisionNbIterations(PxU32 nbIterations); PxU32 getClothInterCollisionNbIterations() const; void createClothSolver(); #endif PxSceneGpu* getSceneGpu(bool createIfNeeded = false); PxTaskManager* getTaskManagerPtr() const { return mTaskManager; } void shiftOrigin(const PxVec3& shift); Ps::Pool<SimStateData>* getSimStateDataPool() { return mSimStateDataPool; } //--------------------------------------------------------------------------------- // Miscellaneous //--------------------------------------------------------------------------------- //internal public methods: public: Scene(const PxSceneDesc& desc, PxU64 contextID); // Scene初始化 ~Scene() {} //use release() plz. void preAllocate(PxU32 nbStatics, PxU32 nbBodies, PxU32 nbStaticShapes, PxU32 nbDynamicShapes); PX_FORCE_INLINE PxsContext* getLowLevelContext() { return mLLContext; } PX_FORCE_INLINE const PxsContext* getLowLevelContext() const { return mLLContext; } PX_FORCE_INLINE Dy::Context* getDynamicsContext() { return mDynamicsContext; } PX_FORCE_INLINE const Dy::Context* getDynamicsContext() const { return mDynamicsContext; } PX_FORCE_INLINE PxsSimulationController* getSimulationController() { return mSimulationController; } PX_FORCE_INLINE const PxsSimulationController* getSimulationController() const { return mSimulationController; } PX_FORCE_INLINE Bp::SimpleAABBManager* getAABBManager() { return mAABBManager; } PX_FORCE_INLINE const Bp::SimpleAABBManager*getAABBManager() const { return mAABBManager; } PX_FORCE_INLINE Ps::Array<BodySim*>& getCcdBodies() { return mCcdBodies; } #if PX_USE_PARTICLE_SYSTEM_API PX_FORCE_INLINE Pt::Context* getParticleContext() { return mParticleContext; } #endif /*PX_FORCE_INLINE PxsIslandManager* getIslandManager() { return mIslandManager; } PX_FORCE_INLINE const PxsIslandManager* getIslandManager() const { return mIslandManager; }*/ PX_FORCE_INLINE IG::SimpleIslandManager* getSimpleIslandManager() { return mSimpleIslandManager; } PX_FORCE_INLINE const IG::SimpleIslandManager* getSimpleIslandManager() const { return mSimpleIslandManager; } PX_FORCE_INLINE Sc::SimulationStage::Enum getSimulationStage() const { return mSimulationStage; } PX_FORCE_INLINE void setSimulationStage(Sc::SimulationStage::Enum stage) { mSimulationStage = stage; } void addShape(RigidSim&, ShapeCore&, PxBounds3* uninflatedBounds); void removeShape(ShapeSim&, bool wakeOnLostTouch); void registerShapeInNphase(const ShapeCore& shapeCore); void unregisterShapeFromNphase(const ShapeCore& shapeCore); void notifyNphaseOnUpdateShapeMaterial(const ShapeCore& shapeCore); // Get an array of the active actors. PX_FORCE_INLINE BodyCore*const* getActiveBodiesArray() const { return mActiveBodies.begin(); } PX_FORCE_INLINE PxU32 getNumActiveBodies() const { return mActiveBodies.size(); } PX_FORCE_INLINE PxU32 getNbInteractions(InteractionType::Enum type) const { return mInteractions[type].size(); } PX_FORCE_INLINE PxU32 getNbActiveInteractions(InteractionType::Enum type) const { return mActiveInteractionCount[type]; } // Get all interactions of a certain type PX_FORCE_INLINE Interaction** getInteractions(InteractionType::Enum type) { return mInteractions[type].begin(); } PX_FORCE_INLINE Interaction** getActiveInteractions(InteractionType::Enum type) { return mInteractions[type].begin(); } void registerInteraction(Interaction* interaction, bool active); void unregisterInteraction(Interaction* interaction); void notifyInteractionActivated(Interaction* interaction); void notifyInteractionDeactivated(Interaction* interaction); // for pool management of interaction arrays, a major cause of dynamic allocation void** allocatePointerBlock(PxU32 size); void deallocatePointerBlock(void**, PxU32 size); private: // Get the number of active one-way dominator actors PX_FORCE_INLINE PxU32 getActiveKinematicBodiesCount() const { return mActiveKinematicBodyCount; } // Get an iterator to the active one-way dominator actors PX_FORCE_INLINE BodyCore*const* getActiveKinematicBodies() const { return mActiveBodies.begin(); } // Get the number of active non-kinematic actors PX_FORCE_INLINE PxU32 getActiveDynamicBodiesCount() const { return mActiveBodies.size() - mActiveKinematicBodyCount; } // Get the active non-kinematic actors PX_FORCE_INLINE BodyCore*const* getActiveDynamicBodies() const { return mActiveBodies.begin() + mActiveKinematicBodyCount; } void swapInteractionArrayIndices(PxU32 id1, PxU32 id2, InteractionType::Enum type); public: PX_FORCE_INLINE Cm::BitMap& getDirtyShapeSimMap() { return mDirtyShapeSimMap; } void addToActiveBodyList(BodySim& actor); void removeFromActiveBodyList(BodySim& actor); void swapInActiveBodyList(BodySim& body); // call when an active body gets switched from dynamic to kinematic or vice versa void addBrokenConstraint(Sc::ConstraintCore*); void addActiveBreakableConstraint(Sc::ConstraintSim*, ConstraintInteraction*); void removeActiveBreakableConstraint(Sc::ConstraintSim*); //the Actor should register its top level shapes with these. void removeBody(BodySim&); void raiseSceneFlag(SceneInternalFlag::Enum flag) { mInternalFlags |= flag; } //lists of actors woken up or put to sleep last simulate void onBodyWakeUp(BodySim* body); void onBodySleep(BodySim* body); PX_FORCE_INLINE bool isValid() const { return (mLLContext != NULL); } void addToLostTouchList(BodySim* body1, BodySim* body2); void initDominanceMatrix(); void setCreateContactReports(bool s); // PX_AGGREGATE PxU32 createAggregate(void* userData, bool selfCollisions); void deleteAggregate(PxU32 id); //~PX_AGGREGATE Dy::Articulation* createLLArticulation(Sc::ArticulationSim* sim); void destroyLLArticulation(Dy::Articulation&); Ps::Pool<ConstraintInteraction>* getConstraintInteractionPool() const { return mConstraintInteractionPool; } public: PxBroadPhaseType::Enum getBroadPhaseType() const; bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const; PxU32 getNbBroadPhaseRegions() const; PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const; PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion); bool removeBroadPhaseRegion(PxU32 handle); void** getOutOfBoundsAggregates(); PxU32 getNbOutOfBoundsAggregates(); void clearOutOfBoundsAggregates(); PX_FORCE_INLINE const PxsMaterialManager& getMaterialManager() const { return mMaterialManager; } PX_FORCE_INLINE PxsMaterialManager& getMaterialManager() { return mMaterialManager; } //material management functions to be called from Scb::Scene void registerMaterialInNP(const PxsMaterialCore& materialCore); void updateMaterialInNP(const PxsMaterialCore& materialCore); void unregisterMaterialInNP(const PxsMaterialCore& materialCore); // Collision filtering void setFilterShaderData(const void* data, PxU32 dataSize); PX_FORCE_INLINE const void* getFilterShaderDataFast() const { return mFilterShaderData; } PX_FORCE_INLINE PxU32 getFilterShaderDataSizeFast() const { return mFilterShaderDataSize; } PX_FORCE_INLINE PxSimulationFilterShader getFilterShaderFast() const { return mFilterShader; } PX_FORCE_INLINE PxSimulationFilterCallback* getFilterCallbackFast() const { return mFilterCallback; } PX_FORCE_INLINE PxU32 getTimeStamp() const { return mTimeStamp; } PX_FORCE_INLINE PxU32 getReportShapePairTimeStamp() const { return mReportShapePairTimeStamp; } PX_FORCE_INLINE PxReal getOneOverDt() const { return mOneOverDt; } PX_FORCE_INLINE PxReal getDt() const { return mDt; } PX_FORCE_INLINE const PxVec3& getGravityFast() const { return mGravity; } PX_FORCE_INLINE bool readFlag(SceneInternalFlag::Enum flag) const { return (mInternalFlags & flag) != 0; } PX_FORCE_INLINE bool readPublicFlag(PxSceneFlag::Enum flag) const { return (mPublicFlags & flag); } PX_FORCE_INLINE NPhaseCore* getNPhaseCore() const { return mNPhaseCore; } void checkConstraintBreakage(); PX_FORCE_INLINE Ps::Array<TriggerPairExtraData>& getTriggerBufferExtraData() { return *mTriggerBufferExtraData; } PX_FORCE_INLINE Ps::Array<PxTriggerPair>& getTriggerBufferAPI() { return mTriggerBufferAPI; } void reserveTriggerReportBufferSpace(const PxU32 pairCount, PxTriggerPair*& triggerPairBuffer, TriggerPairExtraData*& triggerPairExtraBuffer); PX_FORCE_INLINE ObjectIDTracker& getRigidIDTracker() { return *mRigidIDTracker; } PX_FORCE_INLINE ObjectIDTracker& getShapeIDTracker() { return *mShapeIDTracker; } PX_FORCE_INLINE void markReleasedBodyIDForLostTouch(PxU32 id) { mLostTouchPairsDeletedBodyIDs.growAndSet(id); } void resizeReleasedBodyIDMaps(PxU32 maxActors, PxU32 numActors); PX_FORCE_INLINE StaticSim& getStaticAnchor() { return *mStaticAnchor; } PX_FORCE_INLINE ConstraintProjectionManager& getProjectionManager() { return *mProjectionManager; } PX_FORCE_INLINE Bp::BoundsArray& getBoundsArray() const { return *mBoundsArray; } PX_FORCE_INLINE void updateContactDistance(PxU32 idx, PxReal distance) { (*mContactDistance)[idx] = distance; mHasContactDistanceChanged = true; } PX_FORCE_INLINE SqBoundsManager& getSqBoundsManager() const { return *mSqBoundsManager; } PX_FORCE_INLINE PxReal getVisualizationScale() const { return mVisualizationScale; } // This is actually redundant but makes checks whether debug visualization is enabled faster PX_FORCE_INLINE BodyCore* const* getSleepBodiesArray(PxU32& count) { count = mSleepBodies.size(); return mSleepBodies.getEntries(); } PX_FORCE_INLINE PxTaskManager& getTaskManager() const { PX_ASSERT(mTaskManager); return *mTaskManager; } Cm::FlushPool* getFlushPool(); PX_FORCE_INLINE bool getStabilizationEnabled() const { return mEnableStabilization; } PX_FORCE_INLINE void setPostSolverVelocityNeeded() { mContactReportsNeedPostSolverVelocity = true; } ObjectIDTracker& getConstraintIDTracker() { return *mConstraintIDTracker; } void* allocateConstraintBlock(PxU32 size); void deallocateConstraintBlock(void* addr, PxU32 size); PX_INLINE ObjectIDTracker& getElementIDPool() { return *mElementIDPool; } PX_FORCE_INLINE Cm::BitMap& getVelocityModifyMap() { return mVelocityModifyMap; } void stepSetupCollide();//This is very important to guarantee thread safty in the collide PX_FORCE_INLINE void addToPosePreviewList(BodySim& b) { PX_ASSERT(!mPosePreviewBodies.contains(&b)); mPosePreviewBodies.insert(&b); } PX_FORCE_INLINE void removeFromPosePreviewList(BodySim& b) { PX_ASSERT(mPosePreviewBodies.contains(&b)); mPosePreviewBodies.erase(&b); } #if PX_DEBUG PX_FORCE_INLINE bool isInPosePreviewList(BodySim& b) const { return mPosePreviewBodies.contains(&b); } #endif PX_FORCE_INLINE void setSpeculativeCCDRigidBody(const PxU32 index) { mSpeculativeCCDRigidBodyBitMap.growAndSet(index); } PX_FORCE_INLINE void resetSpeculativeCCDRigidBody(const PxU32 index) { mSpeculativeCCDRigidBodyBitMap.reset(index); } PX_FORCE_INLINE void setSpeculativeCCDArticulationLink(const PxU32 index) { mSpeculativeCDDArticulationBitMap.growAndSet(index); } PX_FORCE_INLINE void resetSpeculativeCCDArticulationLink(const PxU32 index) { mSpeculativeCDDArticulationBitMap.reset(index); } PX_FORCE_INLINE PxU64 getContextId() const { return mContextId; } PX_FORCE_INLINE bool isUsingGpuRigidBodies() const { return mUseGpuRigidBodies; } //internal private methods: private: void releaseConstraints(bool endOfScene); PX_INLINE void clearBrokenConstraintBuffer(); ///////////////////////////////////////////////////////////// void prepareCollide(); void collideStep(PxBaseTask* continuation); void advanceStep(PxBaseTask* continuation); // subroutines of collideStep/solveStep: void kinematicsSetup(); void stepSetupSolve(); //void stepSetupSimulate(); void fetchPatchEvents(PxBaseTask*); void processNarrowPhaseTouchEvents(); void processNarrowPhaseTouchEventsStage2(PxBaseTask*); void setEdgesConnected(PxBaseTask*); void processNarrowPhaseLostTouchEvents(PxBaseTask*); void processNarrowPhaseLostTouchEventsIslands(PxBaseTask*); void processLostTouchPairs(); void integrateKinematicPose(); void updateKinematicCached(); void beforeSolver(PxBaseTask* continuation); void checkForceThresholdContactEvents(const PxU32 ccdPass); void processCallbacks(bool pendingReportsOnly = false); void endStep(); private: PxBaseTask& scheduleCloth(PxBaseTask& continuation, bool afterBroadPhase); void scheduleClothGpu(PxBaseTask& continuation); PxBaseTask& scheduleParticleShapeGeneration(PxBaseTask& broadPhaseDependent, PxBaseTask& dynamicsCpuDependent); PxBaseTask& scheduleParticleDynamicsCpu(PxBaseTask& continuation); PxBaseTask& scheduleParticleCollisionPrep(PxBaseTask& collisionCpuDependent, PxBaseTask& gpuDependent); PxBaseTask& scheduleParticleCollisionCpu(PxBaseTask& continuation); PxBaseTask& scheduleParticleGpu(PxBaseTask& continuation); void prepareParticleSystems(); void finishParticleSystems(); PX_FORCE_INLINE void putObjectsToSleep(PxU32 infoFlag); PX_FORCE_INLINE void putInteractionsToSleep(PxU32 infoFlag); PX_FORCE_INLINE void wakeObjectsUp(PxU32 infoFlag); PX_FORCE_INLINE void wakeInteractions(PxU32 infoFlag); void collectPostSolverVelocitiesBeforeCCD(); void updateFromVisualizationParameters(); void clearSleepWakeBodies(void); PX_INLINE void cleanUpSleepBodies(); PX_INLINE void cleanUpWokenBodies(); PX_INLINE void cleanUpSleepOrWokenBodies(Ps::CoalescedHashSet<BodyCore*>& bodyList, PxU32 removeFlag, bool& validMarker); //internal variables: private: // Material manager PX_ALIGN(16, PxsMaterialManager mMaterialManager); PxU64 mContextId; Ps::Array<BodyCore*> mActiveBodies; // Sorted: kinematic before dynamic PxU32 mActiveKinematicBodyCount; // Number of active kinematics. This is also the index in mActiveBodies where the active dynamic bodies start. Ps::Array<Interaction*> mInteractions[InteractionType::eTRACKED_IN_SCENE_COUNT]; PxU32 mActiveInteractionCount[InteractionType::eTRACKED_IN_SCENE_COUNT]; // Interactions with id < activeInteractionCount are active template <typename T, PxU32 size> struct Block { PxU8 mem[sizeof(T)*size]; Block() {} // get around VS warning C4345, otherwise useless }; typedef Block<void*, 8> PointerBlock8; typedef Block<void*, 16> PointerBlock16; typedef Block<void*, 32> PointerBlock32; Ps::Pool<PointerBlock8> mPointerBlock8Pool; Ps::Pool<PointerBlock16> mPointerBlock16Pool; Ps::Pool<PointerBlock32> mPointerBlock32Pool; PxsContext* mLLContext; Bp::SimpleAABBManager* mAABBManager; Bp::BroadPhase* mBP; PxsCCDContext* mCCDContext; PxI32 mNumFastMovingShapes; PxU32 mCCDPass; //PxsIslandManager* mIslandManager; IG::SimpleIslandManager* mSimpleIslandManager; Dy::Context* mDynamicsContext; PxsMemoryManager* mMemoryManager; #if PX_SUPPORT_GPU_PHYSX PxsKernelWranglerManager* mGpuWranglerManagers; PxsHeapMemoryAllocatorManager* mHeapMemoryAllocationManager; #endif PxsSimulationController* mSimulationController; PxsSimulationControllerCallback* mSimulationControllerCallback; PxSceneLimits mLimits; PxVec3 mGravity; //!< Gravity vector PxU32 mBodyGravityDirty; // Set to true before body->updateForces() when the mGravity has changed Ps::Array<PxContactPairHeader> mQueuedContactPairHeaders; //time: //constants set with setTiming(): PxReal mDt; //delta time for current step. PxReal mOneOverDt; //inverse of dt. //stepping / counters: PxU32 mTimeStamp; //Counts number of steps. PxU32 mReportShapePairTimeStamp; //Timestamp for refreshing the shape pair report structure. Updated before delayed shape/actor deletion and before CCD passes. //containers: // Those ones contain shape ptrs from Actor, i.e. compound level, not subparts Ps::CoalescedHashSet<Sc::ConstraintCore*> mConstraints; Sc::ConstraintProjectionManager* mProjectionManager; Bp::BoundsArray* mBoundsArray; Ps::Array<PxReal, Ps::VirtualAllocator>* mContactDistance; bool mHasContactDistanceChanged; SqBoundsManager* mSqBoundsManager; Ps::Array<BodySim*> mCcdBodies; Ps::Array<BodySim*> mProjectedBodies; Ps::Array<PxTriggerPair> mTriggerBufferAPI; Ps::Array<TriggerPairExtraData>* mTriggerBufferExtraData; PxU32 mRemovedShapeCountAtSimStart; // counter used to detect whether there have been buffered shape removals Ps::CoalescedHashSet<ArticulationCore*> mArticulations; #if PX_USE_PARTICLE_SYSTEM_API Pt::Context* mParticleContext; Ps::CoalescedHashSet<ParticleSystemCore*> mParticleSystems; Ps::Array<ParticleSystemSim*> mEnabledParticleSystems; // List of enabled particle systems updated every simulation step. #endif #if PX_USE_CLOTH_API Ps::CoalescedHashSet<ClothCore*> mCloths; static const PxU32 mNumClothSolvers = 2; cloth::Solver* mClothSolvers[mNumClothSolvers]; PxBaseTask* mClothTasks[mNumClothSolvers]; // first element unused cloth::Factory* mClothFactories[mNumClothSolvers]; // necessary because we have a context manager per scene #endif Ps::Array<Sc::ConstraintCore*> mBrokenConstraints; Ps::CoalescedHashSet<Sc::ConstraintSim*> mActiveBreakableConstraints; // pools for joint buffers // Fixed joint is 92 bytes, D6 is 364 bytes right now. So these three pools cover all the internal cases typedef Block<PxU8, 128> MemBlock128; typedef Block<PxU8, 256> MemBlock256; typedef Block<PxU8, 384> MemBlock384; Ps::Pool2<MemBlock128, 8192> mMemBlock128Pool; Ps::Pool2<MemBlock256, 8192> mMemBlock256Pool; Ps::Pool2<MemBlock384, 8192> mMemBlock384Pool; // broad phase data: NPhaseCore* mNPhaseCore; // Collision filtering void* mFilterShaderData; PxU32 mFilterShaderDataSize; PxU32 mFilterShaderDataCapacity; PxSimulationFilterShader mFilterShader; PxSimulationFilterCallback* mFilterCallback; Ps::CoalescedHashSet<BodyCore*> mSleepBodies; Ps::CoalescedHashSet<BodyCore*> mWokeBodies; bool mWokeBodyListValid; bool mSleepBodyListValid; bool mEnableStabilization; Ps::Array<Client*> mClients; //an array of transform arrays, one for each client. SimStats* mStats; PxU32 mInternalFlags; //!< Combination of ::SceneFlag PxSceneFlags mPublicFlags; //copy of PxSceneDesc::flags, of type PxSceneFlag ObjectIDTracker* mConstraintIDTracker; ObjectIDTracker* mShapeIDTracker; ObjectIDTracker* mRigidIDTracker; ObjectIDTracker* mElementIDPool; StaticSim* mStaticAnchor; Cm::PreallocatingPool<ShapeSim>* mShapeSimPool; Cm::PreallocatingPool<StaticSim>* mStaticSimPool; Cm::PreallocatingPool<BodySim>* mBodySimPool; Ps::Pool<ConstraintSim>* mConstraintSimPool; LLArticulationPool* mLLArticulationPool; Ps::Pool<ConstraintInteraction>* mConstraintInteractionPool; Ps::Pool<SimStateData>* mSimStateDataPool; BatchRemoveState* mBatchRemoveState; Ps::Array<SimpleBodyPair> mLostTouchPairs; Cm::BitMap mLostTouchPairsDeletedBodyIDs; // Need to know which bodies have been deleted when processing the lost touch pair list. // Can't use the existing rigid object ID tracker class since this map needs to be cleared at // another point in time. Cm::BitMap mVelocityModifyMap; Ps::Array<PxvContactManagerTouchEvent> mTouchFoundEvents; Ps::Array<PxvContactManagerTouchEvent> mTouchLostEvents; Ps::Array<PxsContactManager*> mFoundPatchManagers; Ps::Array<PxsContactManager*> mLostPatchManagers; Ps::Array<PxU32> mOutOfBoundsIDs; Cm::BitMap mDirtyShapeSimMap; PxU32 mDominanceBitMatrix[PX_MAX_DOMINANCE_GROUP]; PxReal mVisualizationScale; // Redundant but makes checks whether debug visualization is enabled faster bool mVisualizationParameterChanged; // statics: PxU32 mNbRigidStatics; PxU32 mNbRigidDynamics; PxU32 mNbGeometries[PxGeometryType::eGEOMETRY_COUNT]; PxU32 mNumDeactivatingNodes[2]; // task decomposition void preBroadPhase(PxBaseTask* continuation); void broadPhase(PxBaseTask* continuation); void postBroadPhase(PxBaseTask* continuation); void preRigidBodyNarrowPhase(PxBaseTask* continuation); void postBroadPhaseStage2(PxBaseTask* continuation); void postBroadPhaseStage3(PxBaseTask* continuation); void rigidBodyNarrowPhase(PxBaseTask* continuation); void unblockNarrowPhase(PxBaseTask* continuation); void islandGen(PxBaseTask* continuation); void processLostSolverPatches(PxBaseTask* continuation); void postIslandGen(PxBaseTask* continuation); void processTriggerInteractions(PxBaseTask* continuation); void solver(PxBaseTask* continuation); void updateBodiesAndShapes(PxBaseTask* continuation); void updateSimulationController(PxBaseTask* continuation); void updateDynamics(PxBaseTask* continuation); void processLostContacts(PxBaseTask*); void processLostContacts2(PxBaseTask*); void processLostContacts3(PxBaseTask*); void destroyManagers(PxBaseTask*); void lostTouchReports(PxBaseTask*); void unregisterInteractions(PxBaseTask*); void postThirdPassIslandGen(PxBaseTask*); void postSolver(PxBaseTask* continuation); void constraintProjection(PxBaseTask* continuation); void afterIntegration(PxBaseTask* continuation); // performs sleep check, for instance void postCCDPass(PxBaseTask* continuation); void ccdBroadPhaseAABB(PxBaseTask* continuation); void ccdBroadPhase(PxBaseTask* continuation); void updateCCDMultiPass(PxBaseTask* continuation); void updateCCDSinglePass(PxBaseTask* continuation); void updateCCDSinglePassStage2(PxBaseTask* continuation); void updateCCDSinglePassStage3(PxBaseTask* continuation); void finalizationPhase(PxBaseTask* continuation); void postNarrowPhase(PxBaseTask* continuation); void particlePostCollPrep(PxBaseTask* continuation); void particlePostShapeGen(PxBaseTask* continuation); void clothPreprocessing(PxBaseTask* continuation); void addShapes(void *const* shapes, PxU32 nbShapes, size_t ptrOffset, RigidSim& sim, PxBounds3* outBounds); void removeShapes(RigidSim& , Ps::InlineArray<Sc::ShapeSim*, 64>& , Ps::InlineArray<const Sc::ShapeCore*, 64>&, bool wakeOnLostTouch); private: void addShapes(void *const* shapes, PxU32 nbShapes, size_t ptrOffset, RigidSim& sim, ShapeSim*& prefetchedShapeSim, PxBounds3* outBounds); Cm::DelegateTask<Sc::Scene, &Sc::Scene::clothPreprocessing> mClothPreprocessing; Cm::DelegateTask<Sc::Scene, &Sc::Scene::secondPassNarrowPhase> mSecondPassNarrowPhase; Cm::DelegateFanoutTask<Sc::Scene, &Sc::Scene::postNarrowPhase> mPostNarrowPhase; Cm::FanoutTask mParticlePostCollPrep; Cm::DelegateFanoutTask<Sc::Scene, &Sc::Scene::particlePostShapeGen> mParticlePostShapeGen; Cm::DelegateFanoutTask<Sc::Scene, &Sc::Scene::finalizationPhase> mFinalizationPhase; Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateCCDMultiPass> mUpdateCCDMultiPass; //multi-pass ccd stuff Ps::Array<Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateCCDSinglePass> > mUpdateCCDSinglePass; Ps::Array<Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateCCDSinglePassStage2> > mUpdateCCDSinglePass2; Ps::Array<Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateCCDSinglePassStage3> > mUpdateCCDSinglePass3; Ps::Array<Cm::DelegateTask<Sc::Scene, &Sc::Scene::ccdBroadPhaseAABB> > mCCDBroadPhaseAABB; Ps::Array<Cm::DelegateTask<Sc::Scene, &Sc::Scene::ccdBroadPhase> > mCCDBroadPhase; Ps::Array<Cm::DelegateTask<Sc::Scene, &Sc::Scene::postCCDPass> > mPostCCDPass; PxU32 mCurrentCCDTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::afterIntegration> mAfterIntegration; Cm::DelegateTask<Sc::Scene, &Sc::Scene::constraintProjection> mConstraintProjection; Cm::DelegateTask<Sc::Scene, &Sc::Scene::postSolver> mPostSolver; Cm::DelegateTask<Sc::Scene, &Sc::Scene::solver> mSolver; Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateBodiesAndShapes> mUpdateBodiesAndShapes; Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateSimulationController> mUpdateSimulationController; Cm::DelegateTask<Sc::Scene, &Sc::Scene::updateDynamics> mUpdateDynamics; Cm::DelegateTask<Sc::Scene, &Sc::Scene::processLostContacts> mProcessLostContactsTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::processLostContacts2> mProcessLostContactsTask2; Cm::DelegateTask<Sc::Scene, &Sc::Scene::processLostContacts3> mProcessLostContactsTask3; Cm::DelegateTask<Sc::Scene, &Sc::Scene::destroyManagers> mDestroyManagersTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::lostTouchReports> mLostTouchReportsTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::unregisterInteractions> mUnregisterInteractionsTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::processNarrowPhaseLostTouchEventsIslands> mProcessNarrowPhaseLostTouchTasks; Cm::DelegateTask<Sc::Scene, &Sc::Scene::processNarrowPhaseLostTouchEvents> mProcessNPLostTouchEvents; Cm::DelegateTask<Sc::Scene, &Sc::Scene::postThirdPassIslandGen> mPostThirdPassIslandGenTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::postIslandGen> mPostIslandGen; Cm::DelegateTask<Sc::Scene, &Sc::Scene::islandGen> mIslandGen; Cm::DelegateTask<Sc::Scene, &Sc::Scene::preRigidBodyNarrowPhase> mPreRigidBodyNarrowPhase; Cm::DelegateTask<Sc::Scene, &Sc::Scene::setEdgesConnected> mSetEdgesConnectedTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::fetchPatchEvents> mFetchPatchEventsTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::processLostSolverPatches> mProcessLostPatchesTask; Cm::DelegateTask<Sc::Scene, &Sc::Scene::rigidBodyNarrowPhase> mRigidBodyNarrowPhase; Cm::DelegateTask<Sc::Scene, &Sc::Scene::unblockNarrowPhase> mRigidBodyNPhaseUnlock; Cm::DelegateTask<Sc::Scene, &Sc::Scene::postBroadPhase> mPostBroadPhase; Cm::DelegateTask<Sc::Scene, &Sc::Scene::postBroadPhaseStage2> mPostBroadPhase2; Cm::DelegateFanoutTask<Sc::Scene, &Sc::Scene::postBroadPhaseStage3> mPostBroadPhase3; Cm::DelegateTask<Sc::Scene, &Sc::Scene::preallocateContactManagers> mPreallocateContactManagers; Cm::DelegateTask<Sc::Scene, &Sc::Scene::islandInsertion> mIslandInsertion; Cm::DelegateTask<Sc::Scene, &Sc::Scene::registerContactManagers> mRegisterContactManagers; Cm::DelegateTask<Sc::Scene, &Sc::Scene::registerInteractions> mRegisterInteractions; Cm::DelegateTask<Sc::Scene, &Sc::Scene::registerSceneInteractions> mRegisterSceneInteractions; Cm::DelegateTask<Sc::Scene, &Sc::Scene::broadPhase> mBroadPhase; Cm::DelegateTask<Sc::Scene, &Sc::Scene::advanceStep> mAdvanceStep; Cm::DelegateTask<Sc::Scene, &Sc::Scene::collideStep> mCollideStep; Cm::FlushPool mTaskPool; PxTaskManager* mTaskManager; bool mContactReportsNeedPostSolverVelocity; bool mUseGpuRigidBodies; SimulationStage::Enum mSimulationStage; ConstraintGroupNode** mTmpConstraintGroupRootBuffer; // temporary list of constraint group roots, used for constraint projection Ps::CoalescedHashSet<const BodySim*> mPosePreviewBodies; // list of bodies that requested early report of the integrated pose (eENABLE_POSE_INTEGRATION_PREVIEW). Ps::Array<PxsContactManager*> mPreallocatedContactManagers; Ps::Array<ShapeInteraction*> mPreallocatedShapeInteractions; Ps::Array<ElementInteractionMarker*> mPreallocatedInteractionMarkers; Ps::Array<OverlapFilterTask*> mOverlapFilterTasks; Ps::Array<PxFilterInfo> mFilterInfo; Cm::BitMap mSpeculativeCCDRigidBodyBitMap; Cm::BitMap mSpeculativeCDDArticulationBitMap; }; } // namespace Sc } /*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\SimulationController\src\ScScene.cpp **/ /*********************************************************************************************************************************************/ Sc::Scene::Scene(const PxSceneDesc& desc, PxU64 contextID) : mContextId (contextID), mActiveBodies (PX_DEBUG_EXP("sceneActiveBodies")), mActiveKinematicBodyCount (0), mPointerBlock8Pool (PX_DEBUG_EXP("scenePointerBlock8Pool")), mPointerBlock16Pool (PX_DEBUG_EXP("scenePointerBlock16Pool")), mPointerBlock32Pool (PX_DEBUG_EXP("scenePointerBlock32Pool")), mLLContext (0), mBodyGravityDirty (true), mDt (0), mOneOverDt (0), mTimeStamp (1), // PT: has to start to 1 to fix determinism bug. I don't know why yet but it works. mReportShapePairTimeStamp (0), mTriggerBufferAPI (PX_DEBUG_EXP("sceneTriggerBufferAPI")), mRemovedShapeCountAtSimStart (0), mArticulations (PX_DEBUG_EXP("sceneArticulations")), #if PX_USE_PARTICLE_SYSTEM_API mParticleContext (NULL), mParticleSystems (PX_DEBUG_EXP("sceneParticleSystems")), mEnabledParticleSystems (PX_DEBUG_EXP("sceneEnabledParticleSystems")), #endif #if PX_USE_CLOTH_API mCloths (PX_DEBUG_EXP("sceneCloths")), #endif mBrokenConstraints (PX_DEBUG_EXP("sceneBrokenConstraints")), mActiveBreakableConstraints (PX_DEBUG_EXP("sceneActiveBreakableConstraints")), mMemBlock128Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock128Pool")), mMemBlock256Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock256Pool")), mMemBlock384Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock384Pool")), mNPhaseCore (NULL), mSleepBodies (PX_DEBUG_EXP("sceneSleepBodies")), mWokeBodies (PX_DEBUG_EXP("sceneWokeBodies")), mEnableStabilization (desc.flags & PxSceneFlag::eENABLE_STABILIZATION), mClients (PX_DEBUG_EXP("sceneClients")), mInternalFlags (SceneInternalFlag::eSCENE_DEFAULT), mPublicFlags (desc.flags), mStaticAnchor (NULL), mBatchRemoveState (NULL), mLostTouchPairs (PX_DEBUG_EXP("sceneLostTouchPairs")), mOutOfBoundsIDs (PX_DEBUG_EXP("sceneOutOfBoundsIds")), mVisualizationScale (0.0f), mVisualizationParameterChanged (false), mNbRigidStatics (0), mNbRigidDynamics (0), mClothPreprocessing (contextID, this, "ScScene.clothPreprocessing"), mSecondPassNarrowPhase (contextID, this, "ScScene.secondPassNarrowPhase"), mPostNarrowPhase (contextID, this, "ScScene.postNarrowPhase"), mParticlePostCollPrep (contextID, "ScScene.particlePostCollPrep"), mParticlePostShapeGen (contextID, this, "ScScene.particlePostShapeGen"), mFinalizationPhase (contextID, this, "ScScene.finalizationPhase"), mUpdateCCDMultiPass (contextID, this, "ScScene.updateCCDMultiPass"), mAfterIntegration (contextID, this, "ScScene.afterIntegration"), mConstraintProjection (contextID, this, "ScScene.constraintProjection"), mPostSolver (contextID, this, "ScScene.postSolver"), mSolver (contextID, this, "ScScene.rigidBodySolver"), mUpdateBodiesAndShapes (contextID, this, "ScScene.updateBodiesAndShapes"), mUpdateSimulationController (contextID, this, "ScScene.updateSimulationController"), mUpdateDynamics (contextID, this, "ScScene.updateDynamics"), mProcessLostContactsTask (contextID, this, "ScScene.processLostContact"), mProcessLostContactsTask2 (contextID, this, "ScScene.processLostContact2"), mProcessLostContactsTask3 (contextID, this, "ScScene.processLostContact3"), mDestroyManagersTask (contextID, this, "ScScene.destroyManagers"), mLostTouchReportsTask (contextID, this, "ScScene.lostTouchReports"), mUnregisterInteractionsTask (contextID, this, "ScScene.unregisterInteractions"), mProcessNarrowPhaseLostTouchTasks(contextID, this, "ScScene.processNpLostTouchTask"), mProcessNPLostTouchEvents (contextID, this, "ScScene.processNPLostTouchEvents"), mPostThirdPassIslandGenTask (contextID, this, "ScScene.postThirdPassIslandGenTask"), mPostIslandGen (contextID, this, "ScScene.postIslandGen"), mIslandGen (contextID, this, "ScScene.islandGen"), mPreRigidBodyNarrowPhase (contextID, this, "ScScene.preRigidBodyNarrowPhase"), mSetEdgesConnectedTask (contextID, this, "ScScene.setEdgesConnectedTask"), mFetchPatchEventsTask (contextID, this, "ScScene.fetchPatchEventsTask"), mProcessLostPatchesTask (contextID, this, "ScScene.processLostSolverPatchesTask"), mRigidBodyNarrowPhase (contextID, this, "ScScene.rigidBodyNarrowPhase"), mRigidBodyNPhaseUnlock (contextID, this, "ScScene.unblockNarrowPhase"), mPostBroadPhase (contextID, this, "ScScene.postBroadPhase"), mPostBroadPhase2 (contextID, this, "ScScene.postBroadPhase2"), mPostBroadPhase3 (contextID, this, "ScScene.postBroadPhase3"), mPreallocateContactManagers (contextID, this, "ScScene.preallocateContactManagers"), mIslandInsertion (contextID, this, "ScScene.islandInsertion"), mRegisterContactManagers (contextID, this, "ScScene.registerContactManagers"), mRegisterInteractions (contextID, this, "ScScene.registerInteractions"), mRegisterSceneInteractions (contextID, this, "ScScene.registerSceneInteractions"), mBroadPhase (contextID, this, "ScScene.broadPhase"), mAdvanceStep (contextID, this, "ScScene.advanceStep"), mCollideStep (contextID, this, "ScScene.collideStep"), mTaskPool (16384), mContactReportsNeedPostSolverVelocity(false), mUseGpuRigidBodies (false), mSimulationStage (SimulationStage::eCOMPLETE), mTmpConstraintGroupRootBuffer (NULL), mPosePreviewBodies (PX_DEBUG_EXP("scenePosePreviewBodies")) { mCCDPass = 0; for (int i=0; i < InteractionType::eTRACKED_IN_SCENE_COUNT; ++i) mActiveInteractionCount[i] = 0; #if PX_USE_CLOTH_API PxMemZero(mClothSolvers, sizeof(mClothSolvers)); PxMemZero(mClothTasks, sizeof(mClothTasks)); PxMemZero(mClothFactories, sizeof(mClothFactories)); #endif mStats = PX_NEW(SimStats); mConstraintIDTracker = PX_NEW(ObjectIDTracker); mShapeIDTracker = PX_NEW(ObjectIDTracker); mRigidIDTracker = PX_NEW(ObjectIDTracker); mElementIDPool = PX_NEW(ObjectIDTracker); mTriggerBufferExtraData = reinterpret_cast<TriggerBufferExtraData*>(PX_ALLOC(sizeof(TriggerBufferExtraData), "ScScene::TriggerBufferExtraData")); new(mTriggerBufferExtraData) TriggerBufferExtraData(PX_DEBUG_EXP("ScScene::TriggerPairExtraData")); mStaticSimPool = PX_NEW(PreallocatingPool<StaticSim>)(64, "StaticSim"); mBodySimPool = PX_NEW(PreallocatingPool<BodySim>)(64, "BodySim"); mShapeSimPool = PX_NEW(PreallocatingPool<ShapeSim>)(64, "ShapeSim"); mConstraintSimPool = PX_NEW(Ps::Pool<ConstraintSim>)(PX_DEBUG_EXP("ScScene::ConstraintSim")); mConstraintInteractionPool = PX_NEW(Ps::Pool<ConstraintInteraction>)(PX_DEBUG_EXP("ScScene::ConstraintInteraction")); mLLArticulationPool = PX_NEW(LLArticulationPool); mSimStateDataPool = PX_NEW(Ps::Pool<SimStateData>)(PX_DEBUG_EXP("ScScene::SimStateData")); mClients.pushBack(PX_NEW(Client)()); mProjectionManager = PX_NEW(ConstraintProjectionManager)(); mSqBoundsManager = PX_NEW(SqBoundsManager); mTaskManager = physx::PxTaskManager::createTaskManager(Ps::getFoundation().getErrorCallback(), desc.cpuDispatcher, desc.gpuDispatcher); for(PxU32 i=0; i<PxGeometryType::eGEOMETRY_COUNT; i++) mNbGeometries[i] = 0; bool useGpuDynamics = false; bool useGpuBroadphase = false; #if PX_SUPPORT_GPU_PHYSX if (desc.flags & PxSceneFlag::eENABLE_GPU_DYNAMICS) { useGpuDynamics = true; if (mTaskManager->getGpuDispatcher() == NULL) { shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "GPU solver pipeline failed, switching to software"); useGpuDynamics = false; } else if (!mTaskManager->getGpuDispatcher()->getCudaContextManager()->supportsArchSM30()) useGpuDynamics = false; } if (desc.broadPhaseType & PxBroadPhaseType::eGPU) { useGpuBroadphase = true; if (mTaskManager->getGpuDispatcher() == NULL) { shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "GPU Bp pipeline failed, switching to software"); useGpuBroadphase = false; } else if (!mTaskManager->getGpuDispatcher()->getCudaContextManager()->supportsArchSM30()) useGpuBroadphase = false; } #endif mUseGpuRigidBodies = useGpuBroadphase || useGpuDynamics; mLLContext = PX_NEW(PxsContext)(desc, mTaskManager, mTaskPool, contextID); if (mLLContext == 0) { Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Failed to create context!"); return; } mLLContext->setMaterialManager(&getMaterialManager()); mMemoryManager = NULL; #if PX_SUPPORT_GPU_PHYSX mHeapMemoryAllocationManager = NULL; mGpuWranglerManagers = NULL; if (useGpuBroadphase || useGpuDynamics) { mMemoryManager = PxvGetPhysXGpu(true)->createGpuMemoryManager(mLLContext->getTaskManager().getGpuDispatcher(), NULL); mGpuWranglerManagers = PxvGetPhysXGpu(true)->createGpuKernelWranglerManager(mLLContext->getTaskManager().getGpuDispatcher(), getFoundation().getErrorCallback(),desc.gpuComputeVersion); mHeapMemoryAllocationManager = PxvGetPhysXGpu(true)->createGpuHeapMemoryAllocatorManager(desc.gpuDynamicsConfig.heapCapacity, mMemoryManager, desc.gpuComputeVersion); } else #endif { mMemoryManager = createMemoryManager(); } // Broad phase阶段,寻找潜在的碰撞对,有两种方案SAP和MBP //Note: broadphase should be independent of AABBManager. MBP uses it to call getBPBounds but it has //already been passed all bounds in BroadPhase::update() so should use that instead. if(!useGpuBroadphase) { PxBroadPhaseType::Enum broadPhaseType = desc.broadPhaseType; if (broadPhaseType == PxBroadPhaseType::eGPU) broadPhaseType = PxBroadPhaseType::eSAP; mBP = Bp::BroadPhase::create( broadPhaseType, desc.limits.maxNbRegions, desc.limits.maxNbBroadPhaseOverlaps, desc.limits.maxNbStaticShapes, desc.limits.maxNbDynamicShapes, contextID); } else { #if PX_SUPPORT_GPU_PHYSX mBP = PxvGetPhysXGpu(true)->createGpuBroadPhase ( mGpuWranglerManagers, mLLContext->getTaskManager().getGpuDispatcher(), NULL, desc.gpuComputeVersion, desc.gpuDynamicsConfig, mHeapMemoryAllocationManager); #endif } //create allocator Ps::VirtualAllocatorCallback* allocatorCallback = mMemoryManager->createHostMemoryAllocator(desc.gpuComputeVersion); Ps::VirtualAllocator allocator(allocatorCallback); typedef Ps::Array<PxReal, Ps::VirtualAllocator> ContactDistArray; mBoundsArray = PX_NEW(Bp::BoundsArray)(allocator); //mBoundsArray = PX_NEW(Bp::BoundsArray); mContactDistance = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ContactDistArray), PX_DEBUG_EXP("ContactDistance")), ContactDistArray)(allocator); mHasContactDistanceChanged = false; const bool useEnhancedDeterminism = getPublicFlags() & PxSceneFlag::eENABLE_ENHANCED_DETERMINISM; const bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; mSimpleIslandManager = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(IG::SimpleIslandManager), PX_DEBUG_EXP("SimpleIslandManager")), IG::SimpleIslandManager)(useEnhancedDeterminism, contextID); if (!useGpuDynamics) { mDynamicsContext = createDynamicsContext (&mLLContext->getNpMemBlockPool(), mLLContext->getScratchAllocator(), mLLContext->getTaskPool(), mLLContext->getSimStats(), &mLLContext->getTaskManager(), allocatorCallback, &getMaterialManager(), &mSimpleIslandManager->getAccurateIslandSim(), contextID, mEnableStabilization, useEnhancedDeterminism, useAdaptiveForce, desc.maxBiasCoefficient); mLLContext->setNphaseImplementationContext(createNphaseImplementationContext(*mLLContext, &mSimpleIslandManager->getAccurateIslandSim())); mSimulationControllerCallback = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ScSimulationControllerCallback), PX_DEBUG_EXP("ScSimulationControllerCallback")), ScSimulationControllerCallback(this)); mSimulationController = createSimulationController(mSimulationControllerCallback); mAABBManager = PX_NEW(Bp::SimpleAABBManager)(*mBP, *mBoundsArray, *mContactDistance, desc.limits.maxNbAggregates, desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes, allocator, contextID); } else { #if PX_SUPPORT_GPU_PHYSX mDynamicsContext = PxvGetPhysXGpu(true)->createGpuDynamicsContext(mLLContext->getTaskPool(), mGpuWranglerManagers, mLLContext->getTaskManager().getGpuDispatcher(), NULL, desc.gpuDynamicsConfig, &mSimpleIslandManager->getAccurateIslandSim(), desc.gpuMaxNumPartitions, mEnableStabilization, useEnhancedDeterminism, useAdaptiveForce, desc.maxBiasCoefficient, desc.gpuComputeVersion, mLLContext->getSimStats(), mHeapMemoryAllocationManager); void* contactStreamBase = NULL; void* patchStreamBase = NULL; void* forceAndIndiceStreamBase = NULL; mDynamicsContext->getDataStreamBase(contactStreamBase, patchStreamBase, forceAndIndiceStreamBase); PxvNphaseImplementationContextUsableAsFallback* cpuNphaseImplementation = createNphaseImplementationContext(*mLLContext, &mSimpleIslandManager->getAccurateIslandSim()); mLLContext->setNphaseFallbackImplementationContext(cpuNphaseImplementation); PxvNphaseImplementationContext* gpuNphaseImplementation = PxvGetPhysXGpu(true)->createGpuNphaseImplementationContext(*mLLContext, mGpuWranglerManagers, cpuNphaseImplementation, desc.gpuDynamicsConfig, contactStreamBase, patchStreamBase, forceAndIndiceStreamBase, getBoundsArray().getBounds(), &mSimpleIslandManager->getAccurateIslandSim(), mDynamicsContext, desc.gpuComputeVersion, mHeapMemoryAllocationManager); mSimulationControllerCallback = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxgSimulationControllerCallback), PX_DEBUG_EXP("PxgSimulationControllerCallback")), PxgSimulationControllerCallback(this)); mSimulationController = PxvGetPhysXGpu(true)->createGpuSimulationController(mGpuWranglerManagers, mLLContext->getTaskManager().getGpuDispatcher(), NULL, mDynamicsContext, gpuNphaseImplementation, mBP, useGpuBroadphase, mSimpleIslandManager, mSimulationControllerCallback, desc.gpuComputeVersion, mHeapMemoryAllocationManager); mSimulationController->setBounds(mBoundsArray); mDynamicsContext->setSimulationController(mSimulationController); mLLContext->setNphaseImplementationContext(gpuNphaseImplementation); mLLContext->mContactStreamPool = &mDynamicsContext->getContactStreamPool(); mLLContext->mPatchStreamPool = &mDynamicsContext->getPatchStreamPool(); mLLContext->mForceAndIndiceStreamPool = &mDynamicsContext->getForceStreamPool(); Ps::VirtualAllocator tAllocator(mHeapMemoryAllocationManager->mMappedMemoryAllocators); mAABBManager = PX_NEW(Bp::SimpleAABBManager)(*mBP, *mBoundsArray, *mContactDistance, desc.limits.maxNbAggregates, desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes, tAllocator, contextID); #endif } //Construct the bitmap of updated actors required as input to the broadphase update if(desc.limits.maxNbBodies) { // PT: TODO: revisit this. Why do we handle the added/removed and updated bitmaps entirely differently, in different places? And what is this weird formula here? mAABBManager->getChangedAABBMgActorHandleMap().resize((2*desc.limits.maxNbBodies + 256) & ~255); } //mLLContext->createTransformCache(mDynamicsContext->getAllocatorCallback()); mLLContext->createTransformCache(*allocatorCallback); mLLContext->setContactDistance(mContactDistance); mCCDContext = physx::PxsCCDContext::create(mLLContext, mDynamicsContext->getThresholdStream(), *mLLContext->getNphaseImplementationContext()); setSolverBatchSize(desc.solverBatchSize); mDynamicsContext->setFrictionOffsetThreshold(desc.frictionOffsetThreshold); mDynamicsContext->setCCDSeparationThreshold(desc.ccdMaxSeparation); const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); mDynamicsContext->setCorrelationDistance(0.025f * scale.length); mLLContext->setMeshContactMargin(0.01f * scale.length); mLLContext->setToleranceLength(scale.length); // the original descriptor uses // bounce iff impact velocity > threshold // but LL use // bounce iff separation velocity < -threshold // hence we negate here. mDynamicsContext->setBounceThreshold(-desc.bounceThresholdVelocity); StaticCore* anchorCore = PX_NEW(StaticCore)(PxTransform(PxIdentity)); mStaticAnchor = mStaticSimPool->construct(*this, *anchorCore); mNPhaseCore = PX_NEW(NPhaseCore)(*this, desc); initDominanceMatrix(); // DeterminismDebugger::begin(); mWokeBodyListValid = true; mSleepBodyListValid = true; //load from desc: setLimits(desc.limits); // Create broad phase setBroadPhaseCallback(desc.broadPhaseCallback, PX_DEFAULT_CLIENT); setGravity(desc.gravity); setFrictionType(desc.frictionType); setPCM(desc.flags & PxSceneFlag::eENABLE_PCM); setContactCache(!(desc.flags & PxSceneFlag::eDISABLE_CONTACT_CACHE)); setSimulationEventCallback(desc.simulationEventCallback, PX_DEFAULT_CLIENT); setContactModifyCallback(desc.contactModifyCallback); setCCDContactModifyCallback(desc.ccdContactModifyCallback); setCCDMaxPasses(desc.ccdMaxPasses); PX_ASSERT(mNPhaseCore); // refactor paranoia PX_ASSERT( ((desc.filterShaderData) && (desc.filterShaderDataSize > 0)) || (!(desc.filterShaderData) && (desc.filterShaderDataSize == 0)) ); if (desc.filterShaderData) { mFilterShaderData = PX_ALLOC(desc.filterShaderDataSize, sFilterShaderDataMemAllocId); PxMemCopy(mFilterShaderData, desc.filterShaderData, desc.filterShaderDataSize); mFilterShaderDataSize = desc.filterShaderDataSize; mFilterShaderDataCapacity = desc.filterShaderDataSize; } else { mFilterShaderData = NULL; mFilterShaderDataSize = 0; mFilterShaderDataCapacity = 0; } mFilterShader = desc.filterShader; mFilterCallback = desc.filterCallback; #if PX_USE_CLOTH_API createClothSolver(); #endif // PX_USE_CLOTH_API #if PX_USE_PARTICLE_SYSTEM_API mParticleContext = Pt::createParticleContext(mTaskManager , mLLContext->getTaskPool() ); #endif // PX_USE_PARTICLE_SYSTEM_API }
PhysX的BroadPhase阶段

/*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\LowLevelAABB\src\BpBroadPhase.cpp **/ /*********************************************************************************************************************************************/ BroadPhase* BroadPhase::create( const PxBroadPhaseType::Enum bpType, const PxU32 maxNbRegions, const PxU32 maxNbBroadPhaseOverlaps, const PxU32 maxNbStaticShapes, const PxU32 maxNbDynamicShapes, PxU64 contextID) { PX_ASSERT(bpType==PxBroadPhaseType::eMBP || bpType == PxBroadPhaseType::eSAP); if(bpType==PxBroadPhaseType::eMBP) return PX_NEW(BroadPhaseMBP)(maxNbRegions, maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); // 使用MBP else return PX_NEW(BroadPhaseSap)(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); // 使用SAP }
PhysX的SAP
/*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\LowLevelAABB\src\BpBroadPhaseSap.h **/ /*********************************************************************************************************************************************/ namespace physx { // 。。。 。。。 namespace Gu { class Axes; } namespace Bp { // 。。。 。。。 class BroadPhaseBatchUpdateWorkTask: public Cm::Task { public: BroadPhaseBatchUpdateWorkTask(PxU64 contextId=0) : Cm::Task(contextId), mSap(NULL), mAxis(0xffffffff), mPairs(NULL), mPairsSize(0), mPairsCapacity(0) { } virtual void runInternal() { mPairsSize=0; mSap->batchUpdate(mAxis, mPairs, mPairsSize, mPairsCapacity); } virtual const char* getName() const { return "BpBroadphaseSap.batchUpdate"; } void set(class BroadPhaseSap* sap, const PxU32 axis) {mSap = sap; mAxis = axis;} BroadPhasePair* getPairs() const {return mPairs;} PxU32 getPairsSize() const {return mPairsSize;} PxU32 getPairsCapacity() const {return mPairsCapacity;} void setPairs(BroadPhasePair* pairs, const PxU32 pairsCapacity) {mPairs = pairs; mPairsCapacity = pairsCapacity;} void setNumPairs(const PxU32 pairsSize) {mPairsSize=pairsSize;} private: class BroadPhaseSap* mSap; PxU32 mAxis; BroadPhasePair* mPairs; PxU32 mPairsSize; PxU32 mPairsCapacity; }; // 。。。 。。。 class BroadPhaseSap : public BroadPhase, public Ps::UserAllocated { PX_NOCOPY(BroadPhaseSap) public: friend class BroadPhaseBatchUpdateWorkTask; friend class SapUpdateWorkTask; friend class SapPostUpdateWorkTask; BroadPhaseSap(const PxU32 maxNbBroadPhaseOverlaps, const PxU32 maxNbStaticShapes, const PxU32 maxNbDynamicShapes, PxU64 contextID); virtual ~BroadPhaseSap(); virtual void destroy(); virtual PxBroadPhaseType::Enum getType() const { return PxBroadPhaseType::eSAP; } virtual void update(const PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* continuation, physx::PxBaseTask* narrowPhaseUnblockTask); virtual void fetchBroadPhaseResults(physx::PxBaseTask*) {} virtual PxU32 getNbCreatedPairs() const { return mCreatedPairsSize; } virtual BroadPhasePairReport* getCreatedPairs() { return mCreatedPairsArray; } virtual PxU32 getNbDeletedPairs() const { return mDeletedPairsSize; } virtual BroadPhasePairReport* getDeletedPairs() { return mDeletedPairsArray; } virtual void resizeBuffers(); virtual void freeBuffers(); virtual void shiftOrigin(const PxVec3& shift); //~BroadPhase #if PX_CHECKED virtual bool isValid(const BroadPhaseUpdateData& updateData) const; #endif virtual BroadPhasePair* getBroadPhasePairs() const {return mPairs.mActivePairs;} virtual void deletePairs(); private: PxcScratchAllocator* mScratchAllocator; SapUpdateWorkTask mSapUpdateWorkTask; SapPostUpdateWorkTask mSapPostUpdateWorkTask; //Data passed in from updateV. const BpHandle* mCreated; PxU32 mCreatedSize; const BpHandle* mRemoved; PxU32 mRemovedSize; const BpHandle* mUpdated; PxU32 mUpdatedSize; const PxBounds3* mBoxBoundsMinMax; const BpHandle* mBoxGroups; const PxReal* mContactDistance; PxU32 mBoxesCapacity; //Boxes. SapBox1D* mBoxEndPts[3]; //Position of box min/max in sorted arrays of end pts (needs to have mBoxesCapacity). //End pts (endpts of boxes sorted along each axis). ValType* mEndPointValues[3]; //Sorted arrays of min and max box coords BpHandle* mEndPointDatas[3]; //Corresponding owner id and isMin/isMax for each entry in the sorted arrays of min and max box coords. PxU8* mBoxesUpdated; BpHandle* mSortedUpdateElements; BroadPhaseActivityPocket* mActivityPockets; BpHandle* mListNext; BpHandle* mListPrev; PxU32 mBoxesSize; //Number of sorted boxes + number of unsorted (new) boxes PxU32 mBoxesSizePrev; //Number of sorted boxes PxU32 mEndPointsCapacity; //Capacity of sorted arrays. //Default maximum number of overlap pairs PxU32 mDefaultPairsCapacity; //Box-box overlap pairs created or removed each update. BpHandle* mData; PxU32 mDataSize; PxU32 mDataCapacity; //All current box-box overlap pairs. SapPairManager mPairs; //Created and deleted overlap pairs reported back through api. BroadPhasePairReport* mCreatedPairsArray; PxU32 mCreatedPairsSize; PxU32 mCreatedPairsCapacity; BroadPhasePairReport* mDeletedPairsArray; PxU32 mDeletedPairsSize; PxU32 mDeletedPairsCapacity; PxU32 mActualDeletedPairSize; bool setUpdateData(const BroadPhaseUpdateData& updateData); void update(physx::PxBaseTask* continuation); void postUpdate(physx::PxBaseTask* continuation); //Batch create/remove/update. void batchCreate(); void batchRemove(); void batchUpdate(); void batchUpdate(const PxU32 Axis, BroadPhasePair*& pairs, PxU32& pairsSize, PxU32& pairsCapacity); void batchUpdateFewUpdates(const PxU32 Axis, BroadPhasePair*& pairs, PxU32& pairsSize, PxU32& pairsCapacity); void performBoxPruning(const Gu::Axes axes); BroadPhaseBatchUpdateWorkTask mBatchUpdateTasks[3]; PxU64 mContextID; #if PX_DEBUG bool isSelfOrdered() const; bool isSelfConsistent() const; #endif }; } //namespace Bp } //namespace physx /*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\LowLevelAABB\src\BpSAPTasks.h **/ /*********************************************************************************************************************************************/ namespace physx { namespace Bp { class BroadPhaseSap; class SapUpdateWorkTask: public Cm::Task { public: SapUpdateWorkTask(PxU64 contextId) : Cm::Task(contextId) { } void setBroadPhase(BroadPhaseSap* sap) { mSAP = sap; } void set(const PxU32 numCpuTasks) { mNumCpuTasks = numCpuTasks; } virtual void runInternal() { mSAP->update(getContinuation()); } virtual const char* getName() const { return "BpSAP.updateWork"; } private: BroadPhaseSap* mSAP; PxU32 mNumCpuTasks; }; class SapPostUpdateWorkTask: public Cm::Task { public: SapPostUpdateWorkTask(PxU64 contextId) : Cm::Task(contextId) { } void setBroadPhase(BroadPhaseSap* sap) { mSAP = sap; } void set(const PxU32 numCpuTasks) { mNumCpuTasks=numCpuTasks; } virtual void runInternal() { mSAP->postUpdate(getContinuation()); #ifdef DUMP_TOTAL_SAP_TIME PxU64 endTime = shdfnd::Time::getCurrentCounterValue(); printf("SAP Time: %" PX_PRIu64 "\n", endTime - gStartTime); #endif } virtual const char* getName() const { return "BpSAP.postUpdateWork"; } private: BroadPhaseSap* mSAP; PxU32 mNumCpuTasks; }; } //namespace Bp } //namespace physx /*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\LowLevelAABB\src\BpBroadPhaseSap.cpp **/ /*********************************************************************************************************************************************/ // SAP算法是宽阶段筛选: 快速排除不可能碰撞的物体对,大幅减少窄阶段检测的计算量 // SAP算法在三个独立坐标轴(X、Y、Z)上分别执行扫描和剪枝 // SAP算法的核心是通过端点排序和扫描线技术,在三个独立坐标轴上分别检测可能重叠的物体对,然后进行2D重叠验证。 // 这种设计充分利用了并行性,每个轴的任务可以独立执行,通过算法优化(哨兵边界、索引跳跃、内存局部性)实现了O(n log n)时间复杂度的宽阶段碰撞检测。 // 时间复杂度: O(n log n) - 主要开销在端点排序 // 空间复杂度: O(n) - 存储端点列表和活跃集合 void BroadPhaseSap::update(PxBaseTask* continuation) { PX_UNUSED(continuation); PX_PROFILE_ZONE("BroadPhase.SapUpdate", mContextID); batchRemove(); // 批量移除已删除的物体 // 验证三个轴上的重叠对已重置 //Check that the overlap pairs per axis have been reset. PX_ASSERT(0==mBatchUpdateTasks[0].getPairsSize()); PX_ASSERT(0==mBatchUpdateTasks[1].getPairsSize()); PX_ASSERT(0==mBatchUpdateTasks[2].getPairsSize()); // 并行执行三个轴的碰撞检测 mBatchUpdateTasks[0].runInternal(); // 检测在X轴方向可能重叠的物体对 mBatchUpdateTasks[1].runInternal(); // 检测在Y轴方向可能重叠的物体对 mBatchUpdateTasks[2].runInternal(); // 检测在Z轴方向可能重叠的物体对 } void BroadPhaseSap::batchUpdate (const PxU32 Axis, BroadPhasePair*& pairs, PxU32& pairsSize, PxU32& pairsCapacity) { //Nothin updated so don't do anything if(mUpdatedSize == 0) // 如果没有更新的物体,直接返回 return; //If number updated is sufficiently fewer than number of boxes (say less than 20%) if((mUpdatedSize*5) < mBoxesSize) // 如果更新的物体数量少于总物体数的20%,使用batchUpdateFewUpdates优化版本,避免对大量未更新的物体进行不必要的处理 { batchUpdateFewUpdates(Axis, pairs, pairsSize, pairsCapacity); return; } PxU32 numPairs=0; PxU32 maxNumPairs=pairsCapacity; const PxBounds3* PX_RESTRICT boxMinMax3D = mBoxBoundsMinMax; SapBox1D* boxMinMax2D[6]={mBoxEndPts[1],mBoxEndPts[2],mBoxEndPts[2],mBoxEndPts[0],mBoxEndPts[0],mBoxEndPts[1]}; const SapBox1D* PX_RESTRICT boxMinMax0=boxMinMax2D[2*Axis+0]; // 当前轴的min端点 坐标轴映射: 通过boxMinMax2D数组实现三个坐标轴的统一处理 利用内存局部性原理,连续访问端点数据,提高缓存命中率 const SapBox1D* PX_RESTRICT boxMinMax1=boxMinMax2D[2*Axis+1]; // 当前轴的max端点 #if BP_SAP_TEST_GROUP_ID_CREATEUPDATE const BpHandle* PX_RESTRICT asapBoxGroupIds=mBoxGroups; #endif SapBox1D* PX_RESTRICT asapBoxes=mBoxEndPts[Axis]; ValType* PX_RESTRICT asapEndPointValues=mEndPointValues[Axis]; // 三个坐标轴上已排序的端点值数组 BpHandle* PX_RESTRICT asapEndPointDatas=mEndPointDatas[Axis]; // 对应的端点数据(包含物体ID和端点类型信息) ValType* const PX_RESTRICT BaseEPValues = asapEndPointValues; BpHandle* const PX_RESTRICT BaseEPDatas = asapEndPointDatas; PxU8* PX_RESTRICT updated = mBoxesUpdated; //KS - can we lazy create these inside the loop? Might benefit us //There are no extents, jus the sentinels, so exit early. if(isSentinel(BaseEPDatas[1])) return; //We are going to skip the 1st element in the array (the sublist will be sorted) //but we must first update its value if it has moved //const PxU32 startIsMax = isMax(BaseEPDatas[1]); PX_ASSERT(!isMax(BaseEPDatas[1])); const BpHandle startHandle = getOwner(BaseEPDatas[1]); //KS - in theory, we should just be able to grab the min element but there's some issue where a body's max < min (i.e. an invalid extents) that //appears in a unit test // ValType ThisValue_ = boxMinMax3D[startHandle].getMin(Axis); ValType ThisValue_ = encodeMin(boxMinMax3D[startHandle], Axis, mContactDistance[startHandle]); BaseEPValues[1] = ThisValue_; PxU32 updateCounter = mUpdatedSize*2; updateCounter -= updated[startHandle]; //We'll never overlap with this sentinel but it just ensures that we don't need to branch to see if //there's a pocket that we need to test against // 活动口袋(Activity Pockets) 局部更新: 只对发生变化的端点区域进行重新排序 增量处理: 避免对整个端点列表进行完全重排序 BroadPhaseActivityPocket* PX_RESTRICT currentPocket = mActivityPockets; currentPocket->mEndIndex = 0; currentPocket->mStartIndex = 0; // 端点排序和扫描 专门处理端点移动时的实时碰撞检测。该算法通过维护排序的端点列表,在端点位置变化时高效地检测和更新碰撞对。 // 算法维护每个轴上已排序的端点列表,使用扫描线技术: // 维护每个轴上已排序的端点列表(min和max端点),使用扫描线算法检测重叠 // min端点: 当扫描线遇到min端点时,将对应物体加入活跃集合 // max端点: 当扫描线遇到max端点时,将对应物体从活跃集合移除 // 活跃集合中的物体两两之间进行重叠检测 BpHandle ind = 2; PxU8 wasUpdated = updated[startHandle]; for(; !isSentinel(BaseEPDatas[ind]); ++ind) // 哨兵边界(Sentinels):在端点列表的开始和结束位置插入哨兵值,避免复杂的边界条件检查 { BpHandle ThisData = BaseEPDatas[ind]; const BpHandle handle = getOwner(ThisData); if(updated[handle] || wasUpdated) // 端点需要更新的处理逻辑 { wasUpdated = updated[handle]; updateCounter -= wasUpdated; BpHandle ThisIndex = ind; const BpHandle startIsMax = isMax(ThisData); //Access and write back the updated values. TODO - can we avoid this when we're walking through inactive nodes? //BPValType ThisValue = boxMinMax1D[Axis][twoHandle+startIsMax]; //BPValType ThisValue = startIsMax ? boxMinMax3D[handle].getMax(Axis) : boxMinMax3D[handle].getMin(Axis); //ValType ThisValue = boxMinMax3D[handle].getExtent(startIsMax, Axis); ValType ThisValue = startIsMax ? encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]) : encodeMin(boxMinMax3D[handle], Axis, mContactDistance[handle]); // 接触距离支持: 通过mContactDistance支持可变碰撞距离。将浮点坐标转换为整数表示,提高比较效率 BaseEPValues[ThisIndex] = ThisValue; PX_ASSERT(handle!=BP_INVALID_BP_HANDLE); //We always iterate back through the list... // 索引跳跃优化 链表结构: 使用mListPrev和mListNext数组维护端点的排序关系 快速定位: 通过链表指针快速定位插入位置,减少比较次数 BpHandle CurrentIndex = mListPrev[ThisIndex]; ValType CurrentValue = BaseEPValues[CurrentIndex]; //PxBpHandle CurrentData = BaseEPDatas[CurrentIndex]; if(CurrentValue > ThisValue) { wasUpdated = 1; //Get the bounds of the curr aabb. //Get the box1d of the curr aabb. /*const SapBox1D* PX_RESTRICT Object=&asapBoxes[handle]; PX_ASSERT(Object->mMinMax[0]!=BP_INVALID_BP_HANDLE); PX_ASSERT(Object->mMinMax[1]!=BP_INVALID_BP_HANDLE);*/ // const ValType boxMax=boxMinMax3D[handle].getMax(Axis); const ValType boxMax=encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]); PxU32 endIndex = ind; PxU32 startIndex = ind; #if BP_SAP_TEST_GROUP_ID_CREATEUPDATE ValType group = asapBoxGroupIds[handle]; #endif if(!isMax(ThisData)) // min端点向左移动 { do { BpHandle CurrentData = BaseEPDatas[CurrentIndex]; const BpHandle IsMax = isMax(CurrentData); #if PERFORM_COMPARISONS if(IsMax) // 遇到max端点,可能开始重叠 注:只检查max端点,因为min-min不会产生重叠 { const BpHandle ownerId=getOwner(CurrentData); SapBox1D* PX_RESTRICT id1 = asapBoxes + ownerId; // Our min passed a max => start overlap // 是否满足碰撞检测条件 if( BaseEPValues[id1->mMinMax[0]] < boxMax && //2D intersection test using up-to-date values Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], // 2D重叠检测函数 boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) #if BP_SAP_TEST_GROUP_ID_CREATEUPDATE && (group!=asapBoxGroupIds[ownerId]) #else && handle!=ownerId #endif ) { if(numPairs==maxNumPairs) { const PxU32 newMaxNumPairs=maxNumPairs*2; pairs = reinterpret_cast<BroadPhasePair*>(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); maxNumPairs=newMaxNumPairs; } PX_ASSERT(numPairs<maxNumPairs); // 碰撞对生成 pairs[numPairs].mVolA=BpHandle(PxMax(handle, ownerId)); // 有序存储: 确保每个碰撞对以一致的顺序存储(大ID在前,小ID在后) pairs[numPairs].mVolB=BpHandle(PxMin(handle, ownerId)); // 有序存储: 确保每个碰撞对以一致的顺序存储(大ID在前,小ID在后) numPairs++; //AddPair(handle, getOwner(*CurrentMinData), mPairs, mData, mDataSize, mDataCapacity); } } #endif startIndex--; // 继续向左扫描 CurrentIndex = mListPrev[CurrentIndex]; CurrentValue = BaseEPValues[CurrentIndex]; } while(ThisValue < CurrentValue); } else // max端点向左移动 { // Max is moving left: do { BpHandle CurrentData = BaseEPDatas[CurrentIndex]; const BpHandle IsMax = isMax(CurrentData); #if PERFORM_COMPARISONS if(!IsMax) // 遇到min端点,可能结束重叠 注:因为max-max不会结束重叠 { // Our max passed a min => stop overlap const BpHandle ownerId=getOwner(CurrentData); #if 1 if( #if BP_SAP_USE_OVERLAP_TEST_ON_REMOVES Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) #endif #if BP_SAP_TEST_GROUP_ID_CREATEUPDATE && (group!=asapBoxGroupIds[ownerId]) #else && handle!=ownerId #endif ) #endif { if(numPairs==maxNumPairs) { const PxU32 newMaxNumPairs=maxNumPairs*2; pairs = reinterpret_cast<BroadPhasePair*>(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); maxNumPairs=newMaxNumPairs; } PX_ASSERT(numPairs<maxNumPairs); pairs[numPairs].mVolA=BpHandle(PxMin(handle, ownerId)); // 有序存储: 确保每个碰撞对以一致的顺序存储(大ID在前,小ID在后) pairs[numPairs].mVolB=BpHandle(PxMax(handle, ownerId)); // 有序存储: 确保每个碰撞对以一致的顺序存储(大ID在前,小ID在后) numPairs++; //RemovePair(handle, getOwner(*CurrentMaxData), mPairs, mData, mDataSize, mDataCapacity); } } #endif startIndex--; // 继续向左扫描 CurrentIndex = mListPrev[CurrentIndex]; CurrentValue = BaseEPValues[CurrentIndex]; } while(ThisValue < CurrentValue); } // 双向链表:维护mListPrev和mListNext数组 //This test is unnecessary. If we entered the outer loop, we're doing the swap in here { // 从旧位置解除链接 //Unlink from old position and re-link to new position BpHandle oldNextIndex = mListNext[ThisIndex]; BpHandle oldPrevIndex = mListPrev[ThisIndex]; BpHandle newNextIndex = mListNext[CurrentIndex]; BpHandle newPrevIndex = CurrentIndex; //Unlink this node mListNext[oldPrevIndex] = oldNextIndex; mListPrev[oldNextIndex] = oldPrevIndex; // 链接到新位置 //Link it to it's new place in the list mListNext[ThisIndex] = newNextIndex; mListPrev[ThisIndex] = newPrevIndex; mListPrev[newNextIndex] = ThisIndex; mListNext[newPrevIndex] = ThisIndex; } // 更新活动口袋边界 // 局部更新:只标记发生变化的连续区域 // 边界合并:相邻的变化区域合并为单个口袋 // 增量排序:后续只对活动口袋内的端点进行重排序 //There is a sentinel with 0 index, so we don't need //to worry about walking off the array while(startIndex < currentPocket->mStartIndex) { currentPocket--; } //If our start index > currentPocket->mEndIndex, then we don't overlap so create a new pocket if(currentPocket == mActivityPockets || startIndex > (currentPocket->mEndIndex+1)) { currentPocket++; currentPocket->mStartIndex = startIndex; } currentPocket->mEndIndex = endIndex; }// update max //ind++; } else if (updateCounter == 0) //We've updated all the bodies and neither this nor the previous body was updated, so we're done break; }// updated aabbs pairsSize=numPairs; pairsCapacity=maxNumPairs; // 活动口袋处理算法 时间复杂度: O(k log k),其中k是变化区域的大小 //struct BroadPhaseActivityPocket { // PxU32 mStartIndex; // 活动区域的起始索引 // PxU32 mEndIndex; // 活动区域的结束索引 //}; BroadPhaseActivityPocket* pocket = mActivityPockets+1; // 活动口袋: 表示端点列表中发生变化的连续区域 局部更新: 只对变化区域进行重排序,避免全量排 while(pocket <= currentPocket) { // 建立映射表: 初始化mListPrev数组,建立原始索引到新位置的映射关系,为后续的重排序操作建立基础数据结构 for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) { mListPrev[a] = BpHandle(a); } // 重排序 //Now copy all the data to the array, updating the remap table PxU32 CurrIndex = pocket->mStartIndex-1; for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) // 通过mListNext链表按排序顺序遍历端点 { CurrIndex = mListNext[CurrIndex]; PxU32 origIndex = CurrIndex; BpHandle remappedIndex = mListPrev[origIndex]; // 索引映射: 使用mListPrev查找原始索引对应的新位置 // 当端点需要移动时,交换端点的值和数据 if(origIndex != a) { const BpHandle ownerId=getOwner(BaseEPDatas[remappedIndex]); const BpHandle IsMax = isMax(BaseEPDatas[remappedIndex]); ValType tmp = BaseEPValues[a]; BpHandle tmpHandle = BaseEPDatas[a]; BaseEPValues[a] = BaseEPValues[remappedIndex]; BaseEPDatas[a] = BaseEPDatas[remappedIndex]; BaseEPValues[remappedIndex] = tmp; BaseEPDatas[remappedIndex] = tmpHandle; // 映射更新: 维护索引映射关系的正确性 mListPrev[remappedIndex] = mListPrev[a]; //Write back remap index (should be an immediate jump to original index) mListPrev[mListPrev[a]] = remappedIndex; // 更新盒子端点索引 asapBoxes[ownerId].mMinMax[IsMax] = BpHandle(a); } } // 链表重置: 重置mListPrev和mListNext数组,恢复连续链表结构 为下一次更新操作准备数据结构 ////Reset next and prev ptrs back for(PxU32 a = pocket->mStartIndex-1; a <= pocket->mEndIndex; ++a) { mListPrev[a+1] = BpHandle(a); mListNext[a] = BpHandle(a+1); } pocket++; } mListPrev[0] = 0; }
PhysX的MBP
/*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\LowLevelAABB\src\BpBroadPhaseMBP.h **/ /*********************************************************************************************************************************************/ namespace physx { namespace Bp { // 。。。 。。。 class BroadPhaseMBP : public BroadPhase, public Ps::UserAllocated { PX_NOCOPY(BroadPhaseMBP) public: BroadPhaseMBP(PxU32 maxNbRegions, PxU32 maxNbBroadPhaseOverlaps, PxU32 maxNbStaticShapes, PxU32 maxNbDynamicShapes, PxU64 contextID ); virtual ~BroadPhaseMBP(); // BroadPhaseBase virtual bool getCaps(PxBroadPhaseCaps& caps) const; virtual PxU32 getNbRegions() const; virtual PxU32 getRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; virtual PxU32 addRegion(const PxBroadPhaseRegion& region, bool populateRegion); virtual bool removeRegion(PxU32 handle); virtual PxU32 getNbOutOfBoundsObjects() const; virtual const PxU32* getOutOfBoundsObjects() const; //~BroadPhaseBase // BroadPhase virtual PxBroadPhaseType::Enum getType() const { return PxBroadPhaseType::eMBP; } virtual void destroy(); virtual void update(const PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* continuation, physx::PxBaseTask* narrowPhaseUnblockTask); virtual void fetchBroadPhaseResults(physx::PxBaseTask*) {} virtual PxU32 getNbCreatedPairs() const; virtual BroadPhasePairReport* getCreatedPairs(); virtual PxU32 getNbDeletedPairs() const; virtual BroadPhasePairReport* getDeletedPairs(); virtual void freeBuffers(); virtual void shiftOrigin(const PxVec3& shift); #if PX_CHECKED virtual bool isValid(const BroadPhaseUpdateData& updateData) const; #endif virtual BroadPhasePair* getBroadPhasePairs() const {return NULL;} //KS - TODO - implement this!!! virtual void deletePairs(){} //KS - TODO - implement this!!! //~BroadPhase MBPUpdateWorkTask mMBPUpdateWorkTask; MBPPostUpdateWorkTask mMBPPostUpdateWorkTask; MBP* mMBP; // PT: TODO: aggregate MBP_Handle* mMapping; PxU32 mCapacity; Ps::Array<BroadPhasePairReport> mCreated; Ps::Array<BroadPhasePairReport> mDeleted; const BpHandle* mGroups; // ### why are those 'handles'? void setUpdateData(const BroadPhaseUpdateData& updateData); void update(physx::PxBaseTask* continuation); void postUpdate(physx::PxBaseTask* continuation); void allocateMappingArray(PxU32 newCapacity); }; } /*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\LowLevelAABB\src\BpMBPTasks.h **/ /*********************************************************************************************************************************************/ namespace physx { // 。。。 。。。。 class MBPTask : public Cm::Task, public shdfnd::UserAllocated { public: MBPTask(PxU64 contextId) : Cm::Task (contextId), mMBP (NULL), mNumCpuTasks (0) {} PX_FORCE_INLINE void setBroadphase(Bp::BroadPhaseMBP* mbp) { mMBP = mbp; } PX_FORCE_INLINE void setScratchAllocator(PxcScratchAllocator* sa) { mScratchAllocator = sa; } PX_FORCE_INLINE void setNumCpuTasks(const PxU32 numCpuTasks) { mNumCpuTasks = numCpuTasks; } protected: Bp::BroadPhaseMBP* mMBP; PxU32 mNumCpuTasks; PxcScratchAllocator* mScratchAllocator; private: MBPTask& operator=(const MBPTask&); }; // PT: this is the main 'update' task doing the actual box pruning work. class MBPUpdateWorkTask : public MBPTask { public: MBPUpdateWorkTask(PxU64 contextId); ~MBPUpdateWorkTask(); // PxBaseTask virtual const char* getName() const { return "BpMBP.updateWork"; } //~PxBaseTask // Cm::Task virtual void runInternal() { mMBP->update(getContinuation()); } //~Cm::Task private: MBPUpdateWorkTask& operator=(const MBPUpdateWorkTask&); }; // PT: this task runs after MBPUpdateWorkTask. This is where MBP_PairManager::removeMarkedPairs is called, to finalize // the work and come up with created/removed lists. This is single-threaded. class MBPPostUpdateWorkTask : public MBPTask { public: MBPPostUpdateWorkTask(PxU64 contextId); // PxBaseTask virtual const char* getName() const { return "BpMBP.postUpdateWork"; } //~PxBaseTask // Cm::Task virtual void runInternal() { mMBP->postUpdate(getContinuation()); } //~Cm::Task private: MBPPostUpdateWorkTask& operator=(const MBPPostUpdateWorkTask&); }; } /*********************************************************************************************************************************************/ /** UnrealEngine\Engine\Source\ThirdParty\PhysX3\PhysX_3.4\Source\LowLevelAABB\src\BpBroadPhaseMBP.cpp **/ /*********************************************************************************************************************************************/ void BroadPhaseMBP::update(physx::PxBaseTask* /*continuation*/) { #ifndef USE_SINGLE_THREADED_REFERENCE_CODE #ifdef CHECK_NB_OVERLAPS gNbOverlaps = 0; #endif mMBP->findOverlapsMT(mGroups); #ifdef CHECK_NB_OVERLAPS printf("PPU: %d overlaps\n", gNbOverlaps); #endif #endif } void MBP::findOverlapsMT(const BpHandle* PX_RESTRICT groups) { PxU32 nb = mNbRegions; const RegionData* PX_RESTRICT regions = mRegions.begin(); const MBP_Object* objects = mMBP_Objects.begin(); for(PxU32 i=0;i<nb;i++) { if(regions[i].mBP) regions[i].mBP->findOverlapsMT(mPairManager, groups, objects); } } // 游戏物理模拟中的典型用例 // 角色移动(动态 vs 动态):玩家角色之间的碰撞 // 角色与环境(动态 vs 静态):玩家与墙壁、地面的碰撞 // 物体交互(动态 vs 动态):抛出的物体之间的碰撞 // 物体与环境(动态 vs 静态):抛出的物体与环境的碰撞 void Region::findOverlapsMT(MBP_PairManager& pairManager, const BpHandle* PX_RESTRICT groups, const MBP_Object* PX_RESTRICT mbpObjects) { PX_ASSERT(!mNeedsSorting); if(!mNbUpdatedBoxes) // 没有更新物体,无需检测 return; // doCompleteBoxPruning使用三阶段碰撞检测 // 阶段一:更新的动态物体 vs 休眠的动态物体 // 遍历所有更新的动态盒子 // 对每个盒子,在休眠动态盒子数组中查找可能重叠的盒子 // 使用 MBP_OVERLAP_TEST 宏进行精确的2D重叠检测 // 阶段二:休眠的动态物体 vs 更新的动态物体 // 反向遍历,确保所有可能的碰撞对都被检测到 // 使用相同的重叠检测逻辑 // 阶段三:更新的动态物体之间的自碰撞 // 检测所有更新的动态物体之间的相互碰撞 // 避免重复检测(只检测 index0 < index1 的对) if(mInput.mNeeded) // 只有需要时才调用相应函数 doCompleteBoxPruning(&pairManager, mInput, groups, mbpObjects); // 专门处理动态物体间碰撞 里面有3段逻辑:①更新的动态物体 vs 休眠的动态物体 ②休眠的动态物体 vs 更新的动态物体 ③更新的动态物体之间的自碰撞 if(mInput.mBIPInput.mNeeded) doBipartiteBoxPruning(&pairManager, mInput.mBIPInput, groups, mbpObjects); // 专门处理动态-静态物体碰撞(如物体与环境) ① 采用二分剪枝策略提高效率 ② 通过X轴排序和索引跳跃减少比较次数 mNbUpdatedBoxes = 0; }
调用堆栈
FRunnableThreadPThread::_ThreadProc(void*) FRunnableThreadPThread::Run() FTaskThreadAnyThread::Run() FTaskThreadAnyThread::ProcessTasksUntilQuit(int) FTaskThreadAnyThread::ProcessTasks() TGraphTask<FPhysXTask>::ExecuteTask(TArray<FBaseGraphTask*, TSizedDefaultAllocator<32> >&, ENamedThreads::Type) FPhysXTask::DoTask(ENamedThreads::Type, TRefCountPtr<FGraphEvent> const&) MBP::findOverlapsMT(unsigned int const*) Region::findOverlapsMT(MBP_PairManager&, unsigned int const*, MBP_Object const*)
MBP耗时情况



相关CVar变量
| p.ForceMbpClient | Forces all created scenes to use MBP on client builds |
| p.ForceMbpServer | Forces all created scenes to use MBP on server builds |
| p.ForceNoKKPairs | Disables kinematic-kinematic pairs. This is required when using APEX destruction to correctly generate chunk pairs - when not using destruction this speeds up the broadphase by early rejecting KK pairs. |
| p.ForceNoKSPairs | Disables kinematic-static pairs. This makes converting from static to dynamic a little slower - but provides better broadphase performance because we early reject those pairs. |
| p.OverrideMbpNumSubdivisionsClient | Override for number of subdivisions to perform when building MBP regions on a client, note regions are only generated when a scene is created - this will not update the scene if it's already running (0 = No override, 1>16 - Override number) |
| p.OverrideMbpNumSubdivisionsServer | Override for number of subdivisions to perform when building MBP regions on a server, note regions are only generated when a scene is created - this will not update the scene if it's already running (0 = No override, 1>16 - Override number) |
参考
Dive Into PhysX Broad Phase In UE4
浙公网安备 33010602011771号