Open ElusiveFluffy opened 1 year ago
This problem is also apparent if you use Global Time Dealation. You get more bouncing when using GTD for a slomo effect since this plugin calculates on each frame instead of based on the Game Time delta between frames. I'm not comfortable enough in C++ yet to try to fix this right now. :(
Just found that the damping also has framerate dependency even though the velocity gets multiplied by delta time. I found a better way of removing most of the framerate dependency (still there a bit, and may not work as well above fps higher than 200. There probably is a better way to remove the framerate dependency but I can't think of it). It affects the damping, I just needed to increase the value by 0.05 in all nodes. I just edited the file in Source/KawaiiPhysics/Private/AnimNode_KawaiiPhysics.cpp in the SimulateModifyBones function (Not the full function below, the function is around line 504) (For anyone with a blueprint only project, using my fix temporarily, to recompile it just delete the binaries and intermediate folders in the plugin folder then open your project and you'll be asked if you want to recompile the plugin (think also need visual studio with C++))
const USkeletalMeshComponent* SkelComp = Output.AnimInstanceProxy->GetSkelMeshComponent();
const UWorld* World = SkelComp ? SkelComp->GetWorld() : nullptr;
FSceneInterface* Scene = World && World->Scene ? World->Scene : nullptr;
const float Exponent = TargetFramerate * DeltaTime;
//Scale dampening based on framerate
FVector2D InputRange = FVector2D(0.1f, 1.3f);
FVector2D OutputRange = FVector2D(0.5f, 1.15f);
//transform gravity to component space
FVector GravityCS = ComponentTransform.InverseTransformVector(Gravity);
for (int i = 0; i < ModifyBones.Num(); ++i)
{
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_SimulatemodifyBone);
auto& Bone = ModifyBones[i];
if (Bone.BoneRef.BoneIndex < 0 && !Bone.bDummy)
{
continue;
}
if (Bone.ParentIndex < 0)
{
Bone.PrevLocation = Bone.Location;
Bone.Location = Bone.PoseLocation;
continue;
}
auto& ParentBone = ModifyBones[Bone.ParentIndex];
FVector BonePoseLocation = Bone.PoseLocation;
FVector ParentBonePoseLocation = ParentBone.PoseLocation;
float scaleDamping = DeltaTime / (1.0f / TargetFramerate);
// Move using Velocity( = movement amount in pre frame ) and Damping
{
FVector Velocity = (Bone.Location - Bone.PrevLocation) / DeltaTimeOld;
Bone.PrevLocation = Bone.Location;
Velocity *= (1.0f - (Bone.PhysicsSettings.Damping * FMath::GetMappedRangeValueClamped(InputRange, OutputRange, scaleDamping)));
// wind
if (bEnableWind && Scene)
{
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_Wind);
Scene->GetWindParameters_GameThread(ComponentTransform.TransformPosition(Bone.PoseLocation), WindDirection, WindSpeed, WindMinGust, WindMaxGust);
WindDirection = ComponentTransform.Inverse().TransformVector(WindDirection);
FVector WindVelocity = WindDirection * WindSpeed * WindScale;
// TODO:Migrate if there are more good method (Currently copying AnimDynamics implementation)
WindVelocity *= FMath::FRandRange(0.0f, 2.0f);
Velocity += WindVelocity * TargetFramerate;
}
Bone.Location += Velocity * DeltaTime;
}
// Follow Translation
Bone.Location += SkelCompMoveVector * ((1.0f - Bone.PhysicsSettings.WorldDampingLocation) * scaleDamping);
// Follow Rotation
Bone.Location += (SkelCompMoveRotation.RotateVector(Bone.PrevLocation) - Bone.PrevLocation)
* ((1.0f - Bone.PhysicsSettings.WorldDampingRotation) * scaleDamping);
// Gravity
// TODO:Migrate if there are more good method (Currently copying AnimDynamics implementation)
if (CVarEnableOldPhysicsMethodGrayity.GetValueOnAnyThread() == 0)
{
Bone.Location += 0.5 * GravityCS * DeltaTime * DeltaTime;
}
else
{
Bone.Location += GravityCS * DeltaTime;
}
@ElusiveFluffy Tested this out. It doesn't really fix the problem, but side by side I think your variant works a little better for my purposes. It seems at lower speed time dealation you get less jiggle, but this is preferable to having excessive movement in slow-mo.
Both variants still get a big jiggle if you get a sudden frame drop/stutter.
Anyway thanks for sharing!
Also you have a typo at the end:
if (CVarEnableOldPhysicsMethodGra**y**ity.GetValueOnAnyThread() == 0)
↓
if (CVarEnableOldPhysicsMethodGra**v**ity.GetValueOnAnyThread() == 0)
Yeah, it doesn't fully fix the issues with it, but overall it works a bit better at different framerates, I'm still want to try some other things.
I don't think I've noticed the big jiggle from that, but I also haven't tested that much.
Oh, I didn't notice that or edit that line, that was how it originally was in the plugin
Have you solved the problem of performance being affected by frame rate now?
@zjw1996
Have you solved the problem of performance being affected by frame rate now?
I never really figured out how to 100% solve it, may need a complete rewrite or something. I did find a weird way that mostly fixes it but at really low and high fps (like below 20fps, and above around 240 I think) it doesn't work as well. I've tried multiple different things using delta time and this is the best I got
This function is in the file Source/KawaiiPhysics/Private/AnimNode_KawaiiPhysics.cpp, the SimulateModifyBones function its around line 504, just replace the whole function with this and recompile
void FAnimNode_KawaiiPhysics::SimulateModifyBones(FComponentSpacePoseContext& Output, const FBoneContainer& BoneContainer, FTransform& ComponentTransform)
{
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_SimulatemodifyBones);
if (DeltaTime <= 0.0f)
{
return;
}
// for wind
FVector WindDirection;
float WindSpeed;
float WindMinGust;
float WindMaxGust;
const USkeletalMeshComponent* SkelComp = Output.AnimInstanceProxy->GetSkelMeshComponent();
const UWorld* World = SkelComp ? SkelComp->GetWorld() : nullptr;
FSceneInterface* Scene = World && World->Scene ? World->Scene : nullptr;
const float Exponent = TargetFramerate * DeltaTime;
//Clamp Min Max
float DampingMin = 0.15f;
float DampingMax = 1.0f;
//transform gravity to component space
FVector GravityCS = ComponentTransform.InverseTransformVector(Gravity);
for (int32 i = 0; i < ModifyBones.Num(); ++i)
{
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_SimulatemodifyBone);
auto& Bone = ModifyBones[i];
if (Bone.BoneRef.BoneIndex < 0 && !Bone.bDummy)
{
continue;
}
if (Bone.ParentIndex < 0)
{
Bone.PrevLocation = Bone.Location;
Bone.Location = Bone.PoseLocation;
continue;
}
auto& ParentBone = ModifyBones[Bone.ParentIndex];
FVector BonePoseLocation = Bone.PoseLocation;
FVector ParentBonePoseLocation = ParentBone.PoseLocation;
float scaleDamping = DeltaTime / (1.0f / TargetFramerate);
float dampingMultiplyer = (0.1581f * FMath::Pow(scaleDamping, 3)) - (0.9996 * FMath::Pow(scaleDamping, 2)) + (1.6943f * scaleDamping) + 0.0958f;
dampingMultiplyer = FMath::Clamp(dampingMultiplyer, DampingMin, DampingMax);
// Move using Velocity( = movement amount in pre frame ) and Damping
{
FVector Velocity = (Bone.Location - Bone.PrevLocation) / DeltaTimeOld;
Bone.PrevLocation = Bone.Location;
Velocity *= (1.0f - (Bone.PhysicsSettings.Damping * dampingMultiplyer));
// wind
if (bEnableWind && Scene)
{
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_Wind);
Scene->GetWindParameters_GameThread(ComponentTransform.TransformPosition(Bone.PoseLocation), WindDirection, WindSpeed, WindMinGust, WindMaxGust);
WindDirection = ComponentTransform.Inverse().TransformVector(WindDirection);
FVector WindVelocity = WindDirection * WindSpeed * WindScale;
// TODO:Migrate if there are more good method (Currently copying AnimDynamics implementation)
WindVelocity *= FMath::FRandRange(0.0f, 2.0f);
Velocity += WindVelocity * TargetFramerate;
}
Bone.Location += Velocity * DeltaTime;
}
// Follow Translation
Bone.Location += SkelCompMoveVector * ((1.0f - Bone.PhysicsSettings.WorldDampingLocation) * scaleDamping);
// Follow Rotation
Bone.Location += (SkelCompMoveRotation.RotateVector(Bone.PrevLocation) - Bone.PrevLocation)
* ((1.0f - Bone.PhysicsSettings.WorldDampingRotation) * scaleDamping);
// Gravity
// TODO:Migrate if there are more good method (Currently copying AnimDynamics implementation)
if (CVarEnableOldPhysicsMethodGravity.GetValueOnAnyThread() == 0)
{
Bone.Location += 0.5 * GravityCS * DeltaTime * DeltaTime;
}
else
{
Bone.Location += GravityCS * DeltaTime;
}
// Pull to Pose Location
FVector BaseLocation = ParentBone.Location + (BonePoseLocation - ParentBonePoseLocation);
Bone.Location += (BaseLocation - Bone.Location) *
(1.0f - FMath::Pow(1.0f - Bone.PhysicsSettings.Stiffness, Exponent));
{
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_AdjustBone);
// Adjust by each collisions
AdjustBySphereCollision(Bone, SphericalLimits);
AdjustBySphereCollision(Bone, SphericalLimitsData);
AdjustByCapsuleCollision(Bone, CapsuleLimits);
AdjustByCapsuleCollision(Bone, CapsuleLimitsData);
AdjustByPlanerCollision(Bone, PlanarLimits);
AdjustByPlanerCollision(Bone, PlanarLimitsData);
if (bAllowWorldCollision)
{
SCOPE_CYCLE_COUNTER(STAT_KawaiiPhysics_WorldCollision);
AdjustByWorldCollision(Bone, SkelComp, BoneContainer);
}
// Adjust by angle limit
AdjustByAngleLimit(Output, BoneContainer, ComponentTransform, Bone, ParentBone);
// Adjust by Planar Constraint
AdjustByPlanarConstraint(Bone, ParentBone);
}
// Restore Bone Length
float BoneLength = (BonePoseLocation - ParentBonePoseLocation).Size();
Bone.Location = (Bone.Location - ParentBone.Location).GetSafeNormal() * BoneLength + ParentBone.Location;
}
DeltaTimeOld = DeltaTime;
}
The world damping values get run every frame, so the higher the framerate the more dampening that happens (most noticeable with long bone chains). At lower FPS it will be more bouncy, but as you increase the max FPS it becomes less bouncy and just goes straight, making the physics not look as good. Heres a video example showing what I mean, can see it the most in the tail
https://user-images.githubusercontent.com/102465188/210066329-c313f65a-ca11-4574-a579-743ee70355ec.mp4
I have found a possible fix but isn't the best implementation to target how it looks at a specific FPS. This is what I did to make it consistent across all framerate unless its lower than target fps, the edit is in "AnimNode_KawaiiPhysics.cpp" (and "AnimNode_KawaiiPhysics.h" to add cumulativeDeltaTime to each bone, had issues if not all bones had their own value for some reason), for some reason though this code randomly stops working for some physics nodes, maybe because I don't know much about C++. (I tried using delta time but couldn't figure out how to get it to work well)