Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Damping and world damping is framerate dependent #81

Open
ElusiveFluffy opened this issue Dec 30, 2022 · 6 comments
Open

Damping and world damping is framerate dependent #81

ElusiveFluffy opened this issue Dec 30, 2022 · 6 comments

Comments

@ElusiveFluffy
Copy link

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

Editor.77.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)
image

@blueblur22
Copy link

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. :(

@ElusiveFluffy
Copy link
Author

ElusiveFluffy commented Jan 28, 2023

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.
image
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 ElusiveFluffy changed the title World damping is framerate dependent Damping and world damping is framerate dependent Jan 28, 2023
@blueblur22
Copy link

@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)

@ElusiveFluffy
Copy link
Author

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

@zjw1996
Copy link

zjw1996 commented May 2, 2024

Have you solved the problem of performance being affected by frame rate now?

@ElusiveFluffy
Copy link
Author

ElusiveFluffy commented May 2, 2024

@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

(This is different from the code above that I sent before)

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;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants