Chaos物理学习笔记一:Chaos物理碰撞体结构

对于一个StaticMesh来说,它的物理相关的数据都存在它的BodySetup的成员变量中。
对于每个StaticMesh来说,碰撞分为简单碰撞和复杂碰撞。


简单碰撞

简单碰撞相关的数据为UBodySetup的AggGeom成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class UBodySetup : public UBodySetupCore  
{
...
/** Simplified collision representation of this */
UPROPERTY(EditAnywhere, Category = BodySetup, meta=(DisplayName = "Primitives", NoResetToDefault))
struct FKAggregateGeom AggGeom;
...
}

USTRUCT()
struct ENGINE_API FKAggregateGeom
{
GENERATED_USTRUCT_BODY()

UPROPERTY(EditAnywhere, editfixedsize, Category = "Aggregate Geometry", meta = (DisplayName = "Spheres"))
TArray<FKSphereElem> SphereElems;

UPROPERTY(EditAnywhere, editfixedsize, Category = "Aggregate Geometry", meta = (DisplayName = "Boxes"))
TArray<FKBoxElem> BoxElems;

UPROPERTY(EditAnywhere, editfixedsize, Category = "Aggregate Geometry", meta = (DisplayName = "Capsules"))
TArray<FKSphylElem> SphylElems;

UPROPERTY(EditAnywhere, editfixedsize, Category = "Aggregate Geometry", meta = (DisplayName = "Convex Elements"))
TArray<FKConvexElem> ConvexElems;

UPROPERTY(EditAnywhere, editfixedsize, Category = "Aggregate Geometry", meta = (DisplayName = "Tapered Capsules"))
TArray<FKTaperedCapsuleElem> TaperedCapsuleElems;

UPROPERTY(EditAnywhere, editfixedsize, Category = "Aggregate Geometry", meta = (DisplayName = "Level Sets"))
TArray<FKLevelSetElem> LevelSetElems;

...
}

从上述代码不难看出简单碰撞类型有很多,包括:盒状、球状、胶囊体、凸包等。并且一个模型可以由多个简单碰撞体组成。


复杂碰撞

  • 复杂碰撞的数据在UBodySetup里面为成员变量ChaosTriMeshes。在Cook的时候会序列化一份ChaosTriMeshes数据,然后运行时通过CreatePhysicsMeshes这个函数从序列化的数据中构建ChaosTriMeshes。
  • 通常我们可以通过设置StaticMesh的Complex Collision Mesh来将别的模型设置为StaticMesh的用于复杂碰撞的模型(如果没有,则以LOD For Collision的LOD等级的原模型作为复杂碰撞的模型)
  • 观察StaticMesh的代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class UStaticMesh : public UStreamableRenderAsset, public IInterface_CollisionDataProvider, public IInterface_AssetUserData, public IInterface_AsyncCompilation  
    {
    ...
    public:
    #if WITH_EDITORONLY_DATA
    UPROPERTY(Instanced)
    TObjectPtr<class UObject> EditableMesh_DEPRECATED;

    UPROPERTY(EditAnywhere, Category = Collision)
    TObjectPtr<class UStaticMesh> ComplexCollisionMesh;
    #endif
    ...
    public:
    ENGINE_API static void CheckLightMapUVs( UStaticMesh* InStaticMesh, TArray< FString >& InOutAssetsWithMissingUVSets, TArray< FString >& InOutAssetsWithBadUVSets, TArray< FString >& InOutAssetsWithValidUVSets, bool bInVerbose = true );

    //~ Begin Interface_CollisionDataProvider Interface
    ENGINE_API virtual bool GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData) override;
    ENGINE_API virtual bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const override;
    ENGINE_API virtual bool PollAsyncPhysicsTriMeshData(bool InUseAllTriData) const override;
    ENGINE_API virtual bool GetTriMeshSizeEstimates(struct FTriMeshCollisionDataEstimates& OutTriMeshEstimates, bool bInUseAllTriData) const override;

    private:
    bool GetPhysicsTriMeshDataCheckComplex(struct FTriMeshCollisionData* CollisionData, bool bInUseAllTriData, bool bInCheckComplexCollisionMesh);
    bool ContainsPhysicsTriMeshDataCheckComplex(bool InUseAllTriData, bool bInCheckComplexCollisionMesh) const;
    ...
    }
    我们会发现ComplexCollisionMesh是一个只在编辑器下保存的数据。正如上文所说,复杂碰撞的数据会在Cook的时候被序列化下来。
    其中UBodySetup通过GetPhysicsTriMeshData来获取生成复杂碰撞需要的数据也就是FTriMeshCollisionData* CollisionData(通过这个数据我们也可以知道复杂碰撞准确的面数,当然这个数据仅限于编辑器下是准确的)
  • 另外,还有一个比较有意思的点,虚幻引擎无论是模型窗口还是关卡窗口,都有显示碰撞体的选项 但实际上会发现,如果设置了复杂碰撞(为了体现这一点,我还专门把模型的碰撞类型改为了 复杂碰撞用于简单),这两个窗口的显示是不一样的 那么到底哪个显示是正确的呢?答案是模型窗口下的显示是正确的
    原因是关卡窗口下的显示通过ShowFlag让StaticMesh的动态绘制路径中提供碰撞相关的数据可视化要用的MeshBatch,然而在这里StaticMesh只是提供了自身lod的渲染数据当作复杂碰撞的数据。
    而在模型窗口下,”Complex Collision”按钮则比较正确的通过GetPhysicsTriMeshData获取生成复杂碰撞需要的数据,然后再通过DrawLine画出复杂碰撞的线框。