Description

There are 3 issues

  • Each particles don't restore its previous position. Which is for calculating velocity by verlet iteration.
  • The previous transform is cleared by current transform. It provides zero velocity in this tick.
  • The ClearInertia function clears its LinearVelocity and AngularVelocity.
    The simulation calculates an inertia from PreviousLinearVelocity and CurrentLinearVelocity.
    The cleared LinearVelocity generates a big inertia. So it shakes a cloth.

I found a solution.  But it needs additinal functions in NvCloth. 

[Link Removed]

 

[Link Removed]

 

// code placeholder

void FClothingSimulationNv::UpdateLod(int32 InPredictedLod, const FTransform& ComponentToWorld, const TArray<FTransform>& CSTransforms, bool bForceNoRemap, bool bForceActorChecks)
...
if(bOldLodMapped && !bForceNoRemap)
{
FClothingActorNv::FActorLodData& CurrLodData = Actor.LodData[OldClothingLod];

// the number of LODs we've passed through, we can only reskin the incoming mesh if we've stepped 1 LOD
const int32 NumLodsPassed = FMath::Abs(OldClothingLod - PredictedClothingLod);

const uint32 NumOldParticles = CurrLodData.Cloth->getNumParticles();
nv::cloth::Range<const physx::PxVec4> OldLodParticles = nv::cloth::readCurrentParticles(*CurrLodData.Cloth);
#if 1 //fix part 1 : restore previous particle locations
nv::cloth::Range<const physx::PxVec4> OldLodPreviousParticles = nv::cloth::readPreviousParticles(*CurrLodData.Cloth);
#endif

// Remove the old LOD from the solver
Solver->removeCloth(Actor.LodData[OldClothingLod].Cloth);

nv::cloth::Range<physx::PxVec4> OldAccelerations = CurrLodData.Cloth->getParticleAccelerations();

Solver->addCloth(Actor.LodData[PredictedClothingLod].Cloth);

if(NumLodsPassed == 1)
{
// Reposition particles skinned to outgoing LOD
bool bLodTransitionUp = OldClothingLod < PredictedClothingLod;
FClothLODData& NewLodAssetData = Actor.AssetCreatedFrom->LodData[PredictedClothingLod];
TArray<FMeshToMeshVertData>& SkinData = bLodTransitionUp ? NewLodAssetData.TransitionUpSkinData : NewLodAssetData.TransitionDownSkinData;

for(int32 ParticleIndex = 0; ParticleIndex < NumNewParticles; ++ParticleIndex)
{
// Do some simple skinning, we only care about positions for this as particles are just
// positions inside the solver.
FMeshToMeshVertData& VertData = SkinData[ParticleIndex];

const FVector A = P2UVector(OldLodParticles[VertData.SourceMeshVertIndices[0]]);
const FVector B = P2UVector(OldLodParticles[VertData.SourceMeshVertIndices[1]]);
const FVector C = P2UVector(OldLodParticles[VertData.SourceMeshVertIndices[2]]);

#if 1
const FVector PA = P2UVector(OldLodPreviousParticles[VertData.SourceMeshVertIndices[0]]);
const FVector PB = P2UVector(OldLodPreviousParticles[VertData.SourceMeshVertIndices[1]]);
const FVector PC = P2UVector(OldLodPreviousParticles[VertData.SourceMeshVertIndices[2]]);
#endif
// CurrentNormals still contains the normals from the old LOD, which will have been
// calculated at the end of the last simulation step.
const FVector& NA = Actor.CurrentNormals[VertData.SourceMeshVertIndices[0]];
const FVector& NB = Actor.CurrentNormals[VertData.SourceMeshVertIndices[1]];
const FVector& NC = Actor.CurrentNormals[VertData.SourceMeshVertIndices[2]];

const physx::PxVec4& AA = OldAccelerations[VertData.SourceMeshVertIndices[0]];
const physx::PxVec4& AB = OldAccelerations[VertData.SourceMeshVertIndices[1]];
const physx::PxVec4& AC = OldAccelerations[VertData.SourceMeshVertIndices[2]];

#if 1
const FVector FinalPosition = VertData.PositionBaryCoordsAndDist.X * A + NA * VertData.PositionBaryCoordsAndDist.W
+ VertData.PositionBaryCoordsAndDist.Y * B + NB * VertData.PositionBaryCoordsAndDist.W
+ VertData.PositionBaryCoordsAndDist.Z * C + NC * VertData.PositionBaryCoordsAndDist.W;

const FVector FinalPreviousPosition = VertData.PositionBaryCoordsAndDist.X * PA + NA * VertData.PositionBaryCoordsAndDist.W
+ VertData.PositionBaryCoordsAndDist.Y * PB + NB * VertData.PositionBaryCoordsAndDist.W
+ VertData.PositionBaryCoordsAndDist.Z * PC + NC * VertData.PositionBaryCoordsAndDist.W;
#else
FVector FinalPosition = VertData.PositionBaryCoordsAndDist.X * A + NA * VertData.PositionBaryCoordsAndDist.W
+ VertData.PositionBaryCoordsAndDist.Y * B + NB * VertData.PositionBaryCoordsAndDist.W
+ VertData.PositionBaryCoordsAndDist.Z * C + NC * VertData.PositionBaryCoordsAndDist.W;
#endif

physx::PxVec4 FinalAcceleration = VertData.PositionBaryCoordsAndDist.X * AA + VertData.PositionBaryCoordsAndDist.Y * AB + VertData.PositionBaryCoordsAndDist.Z * AC;

NewLodParticles[ParticleIndex] = physx::PxVec4(U2PVector(FinalPosition), NewLodParticles[ParticleIndex].w);
#if 1
NewLodPrevParticles[ParticleIndex] = physx::PxVec4(U2PVector(FinalPreviousPosition), NewLodParticles[ParticleIndex].w);
#else
NewLodPrevParticles[ParticleIndex] = physx::PxVec4(U2PVector(FinalPosition), NewLodParticles[ParticleIndex].w);
#endif
NewAccelerations[ParticleIndex] = FinalAcceleration;
}
}
else
{
// We've passed more than one LOD, and we don't have transition data for all permutations, just use ref pose
for(int32 ParticleIndex = 0; ParticleIndex < NumNewParticles; ++ParticleIndex)
{
NewLodParticles[ParticleIndex] = NewLodData.Px_RestPositions[ParticleIndex];
NewLodPrevParticles[ParticleIndex] = NewLodData.Px_RestPositions[ParticleIndex];
NewAccelerations[ParticleIndex] = physx::PxVec4(0.0f);
}
}

#if 1 //fix part 2 : store transform from a last cloth instance instead current transform
NewLodData.Cloth->setTranslation(CurrLodData.Cloth->getTranslation());
NewLodData.Cloth->setRotation(CurrLodData.Cloth->getRotation());
#else
FTransform SimRootTransform = CSTransforms[Actor.AssetCreatedFrom->ReferenceBoneIndex] * ComponentToWorld;
NewLodData.Cloth->setTranslation(U2PVector(SimRootTransform.GetTranslation()));
NewLodData.Cloth->setRotation(U2PQuat(SimRootTransform.GetRotation()));
#endif
NewLodData.Cloth->clearInertia();
#if 1 //fix part 3 : Add new functions in NvCloth and then call them for restorng LinearVelocity cleared by clearInertica function.
NewLodData.Cloth->setLinearVelocity(CurrLodData.Cloth->getLinearVelocity());
NewLodData.Cloth->setAngularVelocity(CurrLodData.Cloth->getAngularVelocity());
#endif
Actor.CurrentLodIndex = PredictedClothingLod;
}
else









Steps to Reproduce
  1. Open attached project  [Link Removed] on UE4.22.3 form epic launcher 
  2. Open ClothLodTest level
  3. Play and observe the clothes.

Changing cloth LoD with moving skeletal mesh is trigger.
If skeletal mesh has no velocity, This issue will not happen.

[Link Removed]

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-77100 in the post.

3
Login to Vote

Duplicate
CreatedJul 11, 2019
ResolvedJul 19, 2019
UpdatedJun 30, 2020
View Jira Issue