美文网首页
UE4物理引擎模块分析

UE4物理引擎模块分析

作者: 蛋求疼 | 来源:发表于2017-08-24 13:59 被阅读0次

    前段时间工作需要,将引擎World中的物理数据导出来,供服务器使用,并且将UE的物理那块移植出来。趁现在还有印象,将物理这块记录下来, 引擎封装了Phyx3.4。
    物理模块相关源码:
    Engine\Source\Runtime\Engine\Classes\PhysicsEngine

    Paste_Image.png

    引擎支持的碰撞体形状有:

    • Sphere
    • Box
    • Capsule
    • Convex Mesh

    UBodyStep包含了碰撞数据shape,FBodyInstance对应一个PhysX的一个actor. 一个UBodyStep实例可以被多个FBodyInstance实例使用。一个UPrimitiveComponent可以包含多个FBodyInstance(UStaticMeshComponent包含一个,USkeletonMeshComponent包含多个)。

    FBodyInstance映射到Physx Scene中

    Engine\Source\Runtime\Engine\Private\PhysicsEngine\BodyInstance.cpptemplate <bool bCompileStatic> struct FInitBodiesHelper负责创建PhysX的actor.

    创建PxRigidActor

        physx::PxRigidActor* CreateActor_PhysX_AssumesLocked(FBodyInstance* Instance, const PxTransform& PTransform) const
        {
            physx::PxRigidDynamic* PNewDynamic = nullptr;
    
            const ECollisionEnabled::Type CollisionType = Instance->GetCollisionEnabled();
            const bool bDisableSim = !CollisionEnabledHasPhysics(CollisionType) && CDisableQueryOnlyActors.GetValueOnGameThread();
    
            if (IsStatic())
            {
                Instance->RigidActorSync = GPhysXSDK->createRigidStatic(PTransform);
    
                if(bDisableSim)
                {
                    ModifyActorFlag_Isolated<PxActorFlag::eDISABLE_SIMULATION>(Instance->RigidActorSync, true);
                }
    
                if (PAsyncScene)
                {
                    Instance->RigidActorAsync = GPhysXSDK->createRigidStatic(PTransform);
    
                    if (bDisableSim)
                    {
                        ModifyActorFlag_Isolated<PxActorFlag::eDISABLE_SIMULATION>(Instance->RigidActorAsync, true);
                    }
                }
            }
            else
            {
                PNewDynamic = GPhysXSDK->createRigidDynamic(PTransform);
                bool bWantsAsyncScene = false;
                if(SpawnParams.DynamicActorScene == EDynamicActorScene::Default)
                {
                    bWantsAsyncScene = Instance->bUseAsyncScene;
                }
                else
                {
                    bWantsAsyncScene = SpawnParams.DynamicActorScene == EDynamicActorScene::UseAsyncScene;
                }
                
                if(bWantsAsyncScene && PhysScene && PhysScene->HasAsyncScene())
                {
                    Instance->RigidActorAsync = PNewDynamic;
                }
                else
                {
                    Instance->RigidActorSync = PNewDynamic;
                }
    
                if(!Instance->ShouldInstanceSimulatingPhysics())
                {
                    ModifyRigidBodyFlag_Isolated<PxRigidBodyFlag::eKINEMATIC>(PNewDynamic, true);
                }
    
                PxActorFlags ActorFlags = PNewDynamic->getActorFlags();
                if(Instance->bGenerateWakeEvents)
                {
                    ModifyActorFlag<PxActorFlag::eSEND_SLEEP_NOTIFIES>(ActorFlags, true);
                }
    
                if(bDisableSim)
                {
                    ModifyActorFlag<PxActorFlag::eDISABLE_SIMULATION>(ActorFlags, true);
                }
    
                PNewDynamic->setActorFlags(ActorFlags);
            }
    
            return PNewDynamic;
        }
    

    设置PxRigidActor的shape属性

        bool CreateShapes_PhysX_AssumesLocked(FBodyInstance* Instance, physx::PxRigidActor* PNewDynamic, bool bKinematicTargetForSQ) const
        {
            UPhysicalMaterial* SimplePhysMat = Instance->GetSimplePhysicalMaterial();
            TArray<UPhysicalMaterial*> ComplexPhysMats = Instance->GetComplexPhysicalMaterials();
    
            PxMaterial* PSimpleMat = SimplePhysMat ? SimplePhysMat->GetPhysXMaterial() : nullptr;
    
            FShapeData ShapeData;
            Instance->GetFilterData_AssumesLocked(ShapeData);
            Instance->GetShapeFlags_AssumesLocked(ShapeData, ShapeData.CollisionEnabled, BodySetup->GetCollisionTraceFlag() == CTF_UseComplexAsSimple);
    
            if (!IsStatic() && PNewDynamic)
            {
                if (!Instance->ShouldInstanceSimulatingPhysics())
                {
                    ModifyRigidBodyFlag<PxRigidBodyFlag::eKINEMATIC>(ShapeData.SyncBodyFlags, true);
                }
                ModifyRigidBodyFlag<PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES>(ShapeData.SyncBodyFlags, bKinematicTargetForSQ);
            }
    
            bool bInitFail = false;
    
            const bool bShapeSharing = Instance->HasSharedShapes(); //If we have a static actor we can reuse the shapes between sync and async scene
            TArray<PxShape*> PSharedShapes;
            if (Instance->RigidActorSync)
            {
                BodySetup->AddShapesToRigidActor_AssumesLocked(Instance, Instance->RigidActorSync, PST_Sync, Instance->Scale3D, PSimpleMat, ComplexPhysMats, ShapeData, FTransform::Identity, bShapeSharing ? &PSharedShapes : nullptr, bShapeSharing);
                bInitFail |= Instance->RigidActorSync->getNbShapes() == 0;
                Instance->RigidActorSync->userData = &Instance->PhysxUserData;
                Instance->RigidActorSync->setName(Instance->CharDebugName.IsValid() ? Instance->CharDebugName->GetData() : nullptr);
    
                check(FPhysxUserData::Get<FBodyInstance>(Instance->RigidActorSync->userData) == Instance);
    
            }
    
            if (Instance->RigidActorAsync)
            {
                check(PAsyncScene);
                if (bShapeSharing)
                {
                    for (PxShape* PShape : PSharedShapes)
                    {
                        Instance->RigidActorAsync->attachShape(*PShape);
                    }
                }
                else
                {
                    BodySetup->AddShapesToRigidActor_AssumesLocked(Instance, Instance->RigidActorAsync, PST_Async, Instance->Scale3D, PSimpleMat, ComplexPhysMats, ShapeData);
                }
    
                bInitFail |= Instance->RigidActorAsync->getNbShapes() == 0;
                Instance->RigidActorAsync->userData = &Instance->PhysxUserData;
                Instance->RigidActorAsync->setName(Instance->CharDebugName.IsValid() ? Instance->CharDebugName->GetData() : nullptr);
    
                check(FPhysxUserData::Get<FBodyInstance>(Instance->RigidActorAsync->userData) == Instance);
            }
    
            return bInitFail;
        }
    

    UBodyStep创建shapes

    void UBodySetup::CreatePhysicsMeshes()
    {
        SCOPE_CYCLE_COUNTER(STAT_CreatePhysicsMeshes);
    
    #if WITH_PHYSX
        // Create meshes from cooked data if not already done
        if(bCreatedPhysicsMeshes)
        {
            return;
        }
    
        // If we don't have any convex/trimesh data we can skip this whole function
        if (bNeverNeedsCookedCollisionData)
        {
            return;
        }
        
        bool bClearMeshes = true;
    
        // Find or create cooked physics data
        static FName PhysicsFormatName(FPlatformProperties::GetPhysicsFormat());
        FByteBulkData* FormatData = GetCookedData(PhysicsFormatName);
        if (FormatData)
        {
            if (FormatData->IsLocked())
            {
                // seems it's being already processed
                return;
            }
    
            FPhysXCookingDataReader CookedDataReader(*FormatData, &UVInfo);
    
            if (GetCollisionTraceFlag() != CTF_UseComplexAsSimple)
            {
                bool bNeedsCooking = bGenerateNonMirroredCollision && CookedDataReader.ConvexMeshes.Num() != AggGeom.ConvexElems.Num();
                bNeedsCooking = bNeedsCooking || (bGenerateMirroredCollision && CookedDataReader.ConvexMeshesNegX.Num() != AggGeom.ConvexElems.Num());
                if (bNeedsCooking)  //Because of bugs it's possible to save with out of sync cooked data. In editor we want to fixup this data
                {
                    InvalidatePhysicsData();
                    CreatePhysicsMeshes();
                    return;
                }
            }
    
            FinishCreatingPhysicsMeshes(CookedDataReader.ConvexMeshes, CookedDataReader.ConvexMeshesNegX, CookedDataReader.TriMeshes);
            bClearMeshes = false;
        }
        else
        {
            if (IsRuntime(this))
            {
                FPhysXCookHelper CookHelper(GetPhysXCookingModule());
                        
                GetCookInfo(CookHelper.CookInfo, GetRuntimeOnlyCookOptimizationFlags());
                if(CookHelper.HasSomethingToCook(CookHelper.CookInfo))
                {
                    if (!IsRuntimeCookingEnabled())
                    {
                        UE_LOG(LogPhysics, Error, TEXT("Attempting to build physics data for %s at runtime, but runtime cooking is disabled (see the RuntimePhysXCooking plugin)."), *GetPathName());
                    }
                    else
                    {
                        CookHelper.CreatePhysicsMeshes_Concurrent();
                        FinishCreatingPhysicsMeshes(CookHelper.OutNonMirroredConvexMeshes, CookHelper.OutMirroredConvexMeshes, CookHelper.OutTriangleMeshes);
                        bClearMeshes = false;
                    }
                }
            }
        }
    
        if(bClearMeshes)
        {
            ClearPhysicsMeshes();
        }
        
        bCreatedPhysicsMeshes = true;
    #endif //WITH_PHYSX
    }
    

    FPhysScene

    FPhysScene对应PhysX的physx::PxScene, 一个UWorld实例拥有一个FPhysScene实例。UWorld提供的LineTrace, Sweep, Overlap查询的功能均由物理引擎提供, 在Engine\Source\Runtime\Engine\Private\Collision\WorldCollision.cpp中实现。
    一个示例
    bool GeomSweepSingle(const UWorld* World, const struct FCollisionShape& CollisionShape, const FQuat& Rot, FHitResult& OutHit, FVector Start, FVector End, ECollisionChannel TraceChannel, const struct FCollisionQueryParams& Params, const struct FCollisionResponseParams& ResponseParams, const struct FCollisionObjectQueryParams& ObjectParams)的实现:

    Paste_Image.png

    物理世界的事件监听

    /** Event callback used to notify engine about various collision events */
    class ENGINE_API FPhysXSimEventCallback : public PxSimulationEventCallback
    {
    public:
        FPhysXSimEventCallback(FPhysScene* InOwningScene, int32 InSceneType) : OwningScene(InOwningScene), SceneType(InSceneType){}
    
        virtual void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) override;
        virtual void onWake(PxActor** actors, PxU32 count) override;
        virtual void onSleep(PxActor** actors, PxU32 count) override;
        virtual void onTrigger(PxTriggerPair* pairs, PxU32 count) override {}
        virtual void onContact(const PxContactPairHeader& PairHeader, const PxContactPair* Pairs, PxU32 NumPairs) override;
        virtual void onAdvance(const PxRigidBody*const* bodyBuffer, const PxTransform* poseBuffer, const PxU32 count) override {}
    
    private:    
        FPhysScene* OwningScene;
        int32 SceneType;
    };
    

    PxFilterFlags PhysXSimFilterShader( PxFilterObjectAttributes attributes0, PxFilterData filterData0, PxFilterObjectAttributes attributes1, PxFilterData filterData1, PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize )用于在simulate中提供碰撞过滤功能。

    备注:关于引擎碰撞通道的配置和代码中的实现主要是通过上述的过滤回调实现的,过滤标记放在PxFilterData数据中。

    PxFilterData CreateObjectQueryFilterData(const bool bTraceComplex, const int32 MultiTrace/*=1 if multi. 0 otherwise*/, const struct FCollisionObjectQueryParams & ObjectParam)
    {
        /**
         * Format for QueryData : 
         *      word0 (meta data - ECollisionQuery. Extendable)
         *
         *      For object queries
         *
         *      word1 (object type queries)
         *      word2 (unused)
         *      word3 (Multi (1) or single (0) (top 8) + Flags (lower 24))
         */
    
        PxFilterData PNewData;
    
        PNewData.word0 = ECollisionQuery::ObjectQuery;
    
        if (bTraceComplex)
        {
            PNewData.word3 |= EPDF_ComplexCollision;
        }
        else
        {
            PNewData.word3 |= EPDF_SimpleCollision;
        }
    
        // get object param bits
        PNewData.word1 = ObjectParam.GetQueryBitfield();
    
        // if 'nothing', then set no bits
        PNewData.word3 |= CreateChannelAndFilter((ECollisionChannel)MultiTrace, ObjectParam.IgnoreMask);
    
        return PNewData;
    }
    
    PxFilterData CreateTraceQueryFilterData(const uint8 MyChannel, const bool bTraceComplex, const FCollisionResponseContainer& InCollisionResponseContainer, const FCollisionQueryParams& Params)
    {
        /**
         * Format for QueryData : 
         *      word0 (meta data - ECollisionQuery. Extendable)
         *
         *      For trace queries
         *
         *      word1 (blocking channels)
         *      word2 (touching channels)
         *      word3 (MyChannel (top 8) as ECollisionChannel + Flags (lower 24))
         */
    
        PxFilterData PNewData;
    
        PNewData.word0 = ECollisionQuery::TraceQuery;
    
        if (bTraceComplex)
        {
            PNewData.word3 |= EPDF_ComplexCollision;
        }
        else
        {
            PNewData.word3 |= EPDF_SimpleCollision;
        }
    
        // word1 encodes 'what i block', word2 encodes 'what i touch'
        for(int32 i=0; i<ARRAY_COUNT(InCollisionResponseContainer.EnumArray); i++)
        {
            if(InCollisionResponseContainer.EnumArray[i] == ECR_Block)
            {
                // if i block, set that in word1
                PNewData.word1 |= CRC_TO_BITFIELD(i);
            }
            else if(InCollisionResponseContainer.EnumArray[i] == ECR_Overlap)
            {
                // if i touch, set that in word2
                PNewData.word2 |= CRC_TO_BITFIELD(i);
            }
        }
        
        // if 'nothing', then set no bits
        PNewData.word3 |= CreateChannelAndFilter((ECollisionChannel)MyChannel, Params.IgnoreMask);
    
        return PNewData;
    }
    

    物理世界的初始化堆栈

    Paste_Image.png

    结尾

    本文比较肤浅地阐述了物理模块的集成,主要是了解一下其集成的方式。后面分析Movement代码需要了解这块知识。

    相关文章

      网友评论

          本文标题:UE4物理引擎模块分析

          本文链接:https://www.haomeiwen.com/subject/djthdxtx.html