FQuat FAnimNode_RotationMultiplier::MultiplyQuatBasedOnSourceIndex(const FTransform& RefPoseTransform, const FTransform& LocalBoneTransform, const EBoneAxis Axis, float InMultiplier, const FQuat& ReferenceQuat) { // Find delta angle for source bone. FQuat DeltaQuat = ExtractAngle(RefPoseTransform, LocalBoneTransform, Axis); // Turn to Axis and Angle FVector RotationAxis; float RotationAngle; DeltaQuat.ToAxisAndAngle(RotationAxis, RotationAngle); const FVector DefaultAxis = GetAxisVector(Axis); // See if we need to invert angle - shortest path if( (RotationAxis | DefaultAxis) < 0.f ) { RotationAxis = -RotationAxis; RotationAngle = -RotationAngle; } // Make sure it is the shortest angle. RotationAngle = FMath::UnwindRadians(RotationAngle); // New bone rotation FQuat OutQuat = ReferenceQuat * FQuat(RotationAxis, RotationAngle* InMultiplier); // Normalize resulting quaternion. OutQuat.Normalize(); #if 0 //DEBUG_TWISTBONECONTROLLER UE_LOG(LogSkeletalControl, Log, TEXT("\t RefQuat: %s, Rot: %s"), *ReferenceQuat.ToString(), *ReferenceQuat.Rotator().ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t NewQuat: %s, Rot: %s"), *OutQuat.ToString(), *OutQuat.Rotator().ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t RollAxis: %s, RollAngle: %f"), *RotationAxis.ToString(), RotationAngle ); #endif return OutQuat; }
FQuat FAnimNode_RotationMultiplier::ExtractAngle(const TArray<FTransform> & RefPoseTransforms, FA2CSPose & MeshBases, const EBoneAxis Axis, int32 SourceBoneIndex) { // local bone transform const FTransform & LocalBoneTransform = MeshBases.GetLocalSpaceTransform(SourceBoneIndex); // local bone transform with reference rotation FTransform ReferenceBoneTransform = RefPoseTransforms[SourceBoneIndex]; ReferenceBoneTransform.SetTranslation(LocalBoneTransform.GetTranslation()); // find delta angle between the two quaternions X Axis. const FVector RotationAxis = GetAxisVector(Axis); const FVector LocalRotationVector = LocalBoneTransform.GetRotation().RotateVector(RotationAxis); const FVector ReferenceRotationVector = ReferenceBoneTransform.GetRotation().RotateVector(RotationAxis); const FQuat LocalToRefQuat = FQuat::FindBetween(LocalRotationVector, ReferenceRotationVector); checkSlow( LocalToRefQuat.IsNormalized() ); // Rotate parent bone atom from position in local space to reference skeleton // Since our rotation rotates both vectors with shortest arc // we're essentially left with a quaternion that has angle difference with reference skeleton version const FQuat BoneQuatAligned = LocalToRefQuat * LocalBoneTransform.GetRotation(); checkSlow( BoneQuatAligned.IsNormalized() ); // Find that delta angle const FQuat DeltaQuat = (ReferenceBoneTransform.GetRotation().Inverse()) * BoneQuatAligned; checkSlow( DeltaQuat.IsNormalized() ); #if 0 //DEBUG_TWISTBONECONTROLLER UE_LOG(LogSkeletalControl, Log, TEXT("\t ExtractAngle, Bone: %s (%d)"), *SourceBone.BoneName.ToString(), SourceBoneIndex); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t Bone Quat: %s, Rot: %s, AxisX: %s"), *LocalBoneTransform.GetRotation().ToString(), *LocalBoneTransform.GetRotation().Rotator().ToString(), *LocalRotationVector.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t BoneRef Quat: %s, Rot: %s, AxisX: %s"), *ReferenceBoneTransform.GetRotation().ToString(), *ReferenceBoneTransform.GetRotation().Rotator().ToString(), *ReferenceRotationVector.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t LocalToRefQuat Quat: %s, Rot: %s"), *LocalToRefQuat.ToString(), *LocalToRefQuat.Rotator().ToString() ); const FVector BoneQuatAlignedX = LocalBoneTransform.GetRotation().RotateVector(RotationAxis); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t BoneQuatAligned Quat: %s, Rot: %s, AxisX: %s"), *BoneQuatAligned.ToString(), *BoneQuatAligned.Rotator().ToString(), *BoneQuatAlignedX.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaQuat Quat: %s, Rot: %s"), *DeltaQuat.ToString(), *DeltaQuat.Rotator().ToString() ); FTransform BoneAtomAligned(BoneQuatAligned, ReferenceBoneTransform.GetTranslation()); const FQuat DeltaQuatAligned = FQuat::FindBetween(BoneAtomAligned.GetScaledAxis( EAxis::X ), ReferenceBoneTransform.GetScaledAxis( EAxis::X )); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaQuatAligned Quat: %s, Rot: %s"), *DeltaQuatAligned.ToString(), *DeltaQuatAligned.Rotator().ToString() ); FVector DeltaAxis; float DeltaAngle; DeltaQuat.ToAxisAndAngle(DeltaAxis, DeltaAngle); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaAxis: %s, DeltaAngle: %f"), *DeltaAxis.ToString(), DeltaAngle ); #endif return DeltaQuat; }
bool UPrimitiveComponent::ApplyRigidBodyState(const FRigidBodyState& NewState, const FRigidBodyErrorCorrection& ErrorCorrection, FVector& OutDeltaPos, FName BoneName) { bool bRestoredState = true; FBodyInstance* BI = GetBodyInstance(BoneName); if (BI && BI->IsInstanceSimulatingPhysics()) { // failure cases const float QuatSizeSqr = NewState.Quaternion.SizeSquared(); if (QuatSizeSqr < KINDA_SMALL_NUMBER) { UE_LOG(LogPhysics, Warning, TEXT("Invalid zero quaternion set for body. (%s:%s)"), *GetName(), *BoneName.ToString()); return bRestoredState; } else if (FMath::Abs(QuatSizeSqr - 1.f) > KINDA_SMALL_NUMBER) { UE_LOG(LogPhysics, Warning, TEXT("Quaternion (%f %f %f %f) with non-unit magnitude detected. (%s:%s)"), NewState.Quaternion.X, NewState.Quaternion.Y, NewState.Quaternion.Z, NewState.Quaternion.W, *GetName(), *BoneName.ToString() ); return bRestoredState; } FRigidBodyState CurrentState; GetRigidBodyState(CurrentState, BoneName); const bool bShouldSleep = (NewState.Flags & ERigidBodyFlags::Sleeping) != 0; /////// POSITION CORRECTION /////// // Find out how much of a correction we are making const FVector DeltaPos = NewState.Position - CurrentState.Position; const float DeltaMagSq = DeltaPos.SizeSquared(); const float BodyLinearSpeedSq = CurrentState.LinVel.SizeSquared(); // Snap position by default (big correction, or we are moving too slowly) FVector UpdatedPos = NewState.Position; FVector FixLinVel = FVector::ZeroVector; // If its a small correction and velocity is above threshold, only make a partial correction, // and calculate a velocity that would fix it over 'fixTime'. if (DeltaMagSq < ErrorCorrection.LinearDeltaThresholdSq && BodyLinearSpeedSq >= ErrorCorrection.BodySpeedThresholdSq) { UpdatedPos = FMath::Lerp(CurrentState.Position, NewState.Position, ErrorCorrection.LinearInterpAlpha); FixLinVel = (NewState.Position - UpdatedPos) * ErrorCorrection.LinearRecipFixTime; } // Get the linear correction OutDeltaPos = UpdatedPos - CurrentState.Position; /////// ORIENTATION CORRECTION /////// // Get quaternion that takes us from old to new const FQuat InvCurrentQuat = CurrentState.Quaternion.Inverse(); const FQuat DeltaQuat = NewState.Quaternion * InvCurrentQuat; FVector DeltaAxis; float DeltaAng; // radians DeltaQuat.ToAxisAndAngle(DeltaAxis, DeltaAng); DeltaAng = FMath::UnwindRadians(DeltaAng); // Snap rotation by default (big correction, or we are moving too slowly) FQuat UpdatedQuat = NewState.Quaternion; FVector FixAngVel = FVector::ZeroVector; // degrees per second // If the error is small, and we are moving, try to move smoothly to it if (FMath::Abs(DeltaAng) < ErrorCorrection.AngularDeltaThreshold ) { UpdatedQuat = FMath::Lerp(CurrentState.Quaternion, NewState.Quaternion, ErrorCorrection.AngularInterpAlpha); FixAngVel = DeltaAxis.GetSafeNormal() * FMath::RadiansToDegrees(DeltaAng) * (1.f - ErrorCorrection.AngularInterpAlpha) * ErrorCorrection.AngularRecipFixTime; } /////// BODY UPDATE /////// BI->SetBodyTransform(FTransform(UpdatedQuat, UpdatedPos), true); BI->SetLinearVelocity(NewState.LinVel + FixLinVel, false); BI->SetAngularVelocity(NewState.AngVel + FixAngVel, false); // state is restored when no velocity corrections are required bRestoredState = (FixLinVel.SizeSquared() < KINDA_SMALL_NUMBER) && (FixAngVel.SizeSquared() < KINDA_SMALL_NUMBER); /////// SLEEP UPDATE /////// const bool bIsAwake = BI->IsInstanceAwake(); if (bIsAwake && (bShouldSleep && bRestoredState)) { BI->PutInstanceToSleep(); } else if (!bIsAwake) { BI->WakeInstance(); } } return bRestoredState; }
static bool ConvertDeltaRotationsToAxisAngle(const FRotator& StartOrientation, const FRotator& EndOrientation, FVector& OutAxis, float& OutAngle) { FQuat deltaRotation = EndOrientation.Quaternion() * StartOrientation.Quaternion().Inverse(); deltaRotation.ToAxisAndAngle(OutAxis, OutAngle); return true; }