Using deformer graph to support cloth sim and morph targets

Hello there,

We are currently using Unreal 5.6 and we found a way to use morph targets with cloth sim at the same time, with the help of a custom DeformerGraph (see Repro steps above) and a small engine fix.

But we want to confirm that the engine modification we used is safe: at first glance, it should not be dangerous to integrate, but who knows.

What do you say ?

Regards,

Thomas

Steps to Reproduce

  • Duplicate the DG_LinearBlendSkin_Morph_Cloth_RecomputeNormals
  • Modify the kernel code in the LinearBlendSkin_Morph_Cloth_PositionOnly node (see below)
  • Also modify the SkeletalRenderGPUSkin.cpp (see below)
  • Import a skeletal mesh with morph targets
  • Paint some clothes
  • Assign the deformer graph
  • Notice that now the Clothes and the MorphTargets can be used on the same mesh section
-- DG_LinearBlendSkin_Morph_Cloth_RecomputeNormals --
-----------------------------------------------------
if (Index >= ReadNumThreads().x) return;

float3 LocalPosition = ReadPosition(Index);

float3 MorphDelta = ReadMorphDelta(Index);
float3 MorphedPosition = LocalPosition + MorphDelta;

float3x4 WeightedBoneMatrix = ReadWeightedBoneMatrix(Index);
float3 SkinnedPosition = mul(WeightedBoneMatrix, float4(MorphedPosition, 1));

float3 ClothWeight = ReadClothWeight(Index);
float3 ClothPosition = ReadClothPosition(Index);
float3 LocalClothPosition = mul(float4(ClothPosition, 1), ReadClothToLocal()).xyz;

// TA
float3x4 BoneMatrix = ReadBoneMatrix(Index, 0);
LocalClothPosition += mul(BoneMatrix, float4(MorphDelta, 0));
// TA


float3 FinalPosition = lerp(SkinnedPosition, LocalClothPosition.xyz, ClothWeight);

WriteOutPosition(Index, FinalPosition);

-- SkeletalRenderGPUSkin.cpp --
-------------------------------

void FSkeletalMeshObjectGPUSkin::ProcessUpdatedDynamicData(FRHICommandList& RHICmdList, FGPUSkinCache* GPUSkinCache, EGPUSkinCacheEntryMode Mode)
{
...
    const bool bSectionUsingCloth = GEnableCloth && ClothVertexFactory != nullptr;
    //TA>
    // const bool bSectionUsingMorph = Mode == EGPUSkinCacheEntryMode::Raster && MorphVertexBuffer && !bSectionUsingCloth && (bHasExternalMorphs || (bHasWeightedActiveMorphs && DynamicData->SectionIdsUseByActiveMorphTargets.Contains(SectionIdx)));
    const bool bSectionUsingMorph = Mode == EGPUSkinCacheEntryMode::Raster && MorphVertexBuffer && (bHasExternalMorphs || (bHasWeightedActiveMorphs && DynamicData->SectionIdsUseByActiveMorphTargets.Contains(SectionIdx)));
    //<TA
    bool bSectionUsingSkinCache = bAllowAddToSkinCache ? Section.MaxBoneInfluences != 0 : false;

    VertexFactory->UpdateMorphState(RHICmdList, bSectionUsingMorph);
...
}

Hey there,

Just wanted to let you know we’ve reached out to the dev on this one, and they’re on vacation; they’ll be back soon to get you an answer.

Dustin

Hey, thanks for reach out! The change should be safe for this use case.

Nice thank you !