服务器物理引擎,UE4物理引擎模块分析

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

物理模块相关源码:

Engine\Source\Runtime\Engine\Classes\PhysicsEngine

b6cbd7a27d0e

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.cpp中 template 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<:edisable_simulation>(Instance->RigidActorSync, true);

}

if (PAsyncScene)

{

Instance->RigidActorAsync = GPhysXSDK->createRigidStatic(PTransform);

if (bDisableSim)

{

ModifyActorFlag_Isolated<: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<:ekinematic>(PNewDynamic, true);

}

PxActorFlags ActorFlags = PNewDynamic->getActorFlags();

if(Instance->bGenerateWakeEvents)

{

ModifyActorFlag<:esend_sleep_notifies>(ActorFlags, true);

}

if(bDisableSim)

{

ModifyActorFlag<: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 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<:ekinematic>(ShapeData.SyncBodyFlags, true);

}

ModifyRigidBodyFlag<: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 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(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(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)的实现:

b6cbd7a27d0e

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

{

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;

}

物理世界的初始化堆栈

b6cbd7a27d0e

Paste_Image.png

结尾

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值