可可西

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阶段

image

/*********************************************************************************************************************************************/
/** 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耗时情况

image

image

image

 

相关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

 

posted on 2024-05-29 23:02  可可西  阅读(688)  评论(0)    收藏  举报

导航