前段時間工作需要,將引擎World中的物理數(shù)據(jù)導(dǎo)出來乎芳,供服務(wù)器使用幽崩,并且將UE的物理那塊移植出來苦始。趁現(xiàn)在還有印象,將物理這塊記錄下來, 引擎封裝了Phyx3.4慌申。
物理模塊相關(guān)源碼:
Engine\Source\Runtime\Engine\Classes\PhysicsEngine
引擎支持的碰撞體形狀有:
- Sphere
- Box
- Capsule
- Convex Mesh
UBodyStep包含了碰撞數(shù)據(jù)shape陌选,F(xiàn)BodyInstance對應(yīng)一個PhysX的一個actor. 一個UBodyStep實(shí)例可以被多個FBodyInstance實(shí)例使用。一個UPrimitiveComponent可以包含多個FBodyInstance(UStaticMeshComponent包含一個,USkeletonMeshComponent包含多個)咨油。
FBodyInstance映射到Physx Scene中
在Engine\Source\Runtime\Engine\Private\PhysicsEngine\BodyInstance.cpp
中 template <bool bCompileStatic> struct FInitBodiesHelper
負(fù)責(zé)創(chuàng)建PhysX的actor.
創(chuàng)建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;
}
設(shè)置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創(chuàng)建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對應(yīng)PhysX的physx::PxScene, 一個UWorld實(shí)例擁有一個FPhysScene實(shí)例您炉。UWorld提供的LineTrace, Sweep, Overlap查詢的功能均由物理引擎提供, 在Engine\Source\Runtime\Engine\Private\Collision\WorldCollision.cpp
中實(shí)現(xiàn)。
一個示例
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)
的實(shí)現(xiàn):
物理世界的事件監(jiān)聽
/** 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中提供碰撞過濾功能役电。
備注:關(guān)于引擎碰撞通道的配置和代碼中的實(shí)現(xiàn)主要是通過上述的過濾回調(diào)實(shí)現(xiàn)的邻吭,過濾標(biāo)記放在PxFilterData數(shù)據(jù)中。
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;
}
物理世界的初始化堆棧
結(jié)尾
本文比較膚淺地闡述了物理模塊的集成宴霸,主要是了解一下其集成的方式囱晴。后面分析Movement代碼需要了解這塊知識。