void FAnimNode_LookAt::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer& RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); FTransform ComponentBoneTransform = MeshBases.GetComponentSpaceTransform(BoneToModify.BoneIndex); // get target location FVector TargetLocationInComponentSpace; if (LookAtBone.IsValid(RequiredBones)) { FTransform LookAtTransform = MeshBases.GetComponentSpaceTransform(LookAtBone.BoneIndex); TargetLocationInComponentSpace = LookAtTransform.GetLocation(); } else { TargetLocationInComponentSpace = SkelComp->ComponentToWorld.InverseTransformPosition(LookAtLocation); } CurrentLookAtLocation = TargetLocationInComponentSpace; // lookat vector FVector LookAtVector = GetAlignVector(ComponentBoneTransform, LookAtAxis); // flip to target vector if it wasnt negative bool bShouldFlip = LookAtAxis == EAxisOption::X_Neg || LookAtAxis == EAxisOption::Y_Neg || LookAtAxis == EAxisOption::Z_Neg; FVector ToTarget = CurrentLookAtLocation - ComponentBoneTransform.GetLocation(); ToTarget.Normalize(); if (bShouldFlip) { ToTarget *= -1.f; } // get delta rotation FQuat DeltaRot = FQuat::FindBetween(LookAtVector, ToTarget); // transform current rotation to delta rotation FQuat CurrentRot = ComponentBoneTransform.GetRotation(); FQuat NewRotation = DeltaRot * CurrentRot; ComponentBoneTransform.SetRotation(NewRotation); OutBoneTransforms.Add( FBoneTransform(BoneToModify.BoneIndex, ComponentBoneTransform) ); }
void FAnimNode_Trail::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer& RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); if( ChainLength < 2 ) { return; } // The incoming BoneIndex is the 'end' of the spline chain. We need to find the 'start' by walking SplineLength bones up hierarchy. // Fail if we walk past the root bone. int32 WalkBoneIndex = TrailBone.BoneIndex; TArray<int32> ChainBoneIndices; ChainBoneIndices.AddZeroed(ChainLength); ChainBoneIndices[ChainLength - 1] = WalkBoneIndex; for (int32 i = 1; i < ChainLength; i++) { // returns to avoid a crash // @TODO : shows an error message why failed if (WalkBoneIndex == 0) { return; } // Get parent bone. WalkBoneIndex = RequiredBones.GetParentBoneIndex(WalkBoneIndex); //Insert indices at the start of array, so that parents are before children in the array. int32 TransformIndex = ChainLength - (i + 1); ChainBoneIndices[TransformIndex] = WalkBoneIndex; } OutBoneTransforms.AddZeroed(ChainLength); // If we have >0 this frame, but didn't last time, record positions of all the bones. // Also do this if number has changed or array is zero. bool bHasValidStrength = (Alpha > 0.f); if(TrailBoneLocations.Num() != ChainLength || (bHasValidStrength && !bHadValidStrength)) { TrailBoneLocations.Empty(); TrailBoneLocations.AddZeroed(ChainLength); for(int32 i=0; i<ChainBoneIndices.Num(); i++) { int32 ChildIndex = ChainBoneIndices[i]; FTransform ChainTransform = MeshBases.GetComponentSpaceTransform(ChildIndex); TrailBoneLocations[i] = ChainTransform.GetTranslation(); } OldLocalToWorld = SkelComp->GetTransformMatrix(); } bHadValidStrength = bHasValidStrength; // transform between last frame and now. FMatrix OldToNewTM = OldLocalToWorld * SkelComp->GetTransformMatrix().InverseFast(); // Add fake velocity if present to all but root bone if(!FakeVelocity.IsZero()) { FVector FakeMovement = -FakeVelocity * ThisTimstep; if (bActorSpaceFakeVel && SkelComp->GetOwner()) { const FTransform BoneToWorld(SkelComp->GetOwner()->GetActorRotation(), SkelComp->GetOwner()->GetActorLocation()); FakeMovement = BoneToWorld.TransformVector(FakeMovement); } FakeMovement = SkelComp->GetTransformMatrix().InverseTransformVector(FakeMovement); // Then add to each bone for(int32 i=1; i<TrailBoneLocations.Num(); i++) { TrailBoneLocations[i] += FakeMovement; } } // Root bone of trail is not modified. int32 RootIndex = ChainBoneIndices[0]; FTransform ChainTransform = MeshBases.GetComponentSpaceTransform(RootIndex); OutBoneTransforms[0] = FBoneTransform(RootIndex, ChainTransform); TrailBoneLocations[0] = ChainTransform.GetTranslation(); // Starting one below head of chain, move bones. for(int32 i=1; i<ChainBoneIndices.Num(); i++) { // Parent bone position in component space. int32 ParentIndex = ChainBoneIndices[i-1]; FVector ParentPos = TrailBoneLocations[i-1]; FVector ParentAnimPos = MeshBases.GetComponentSpaceTransform(ParentIndex).GetTranslation(); // Child bone position in component space. int32 ChildIndex = ChainBoneIndices[i]; FVector ChildPos = OldToNewTM.TransformPosition(TrailBoneLocations[i]); // move from 'last frames component' frame to 'this frames component' frame FVector ChildAnimPos = MeshBases.GetComponentSpaceTransform(ChildIndex).GetTranslation(); // Desired parent->child offset. FVector TargetDelta = (ChildAnimPos - ParentAnimPos); // Desired child position. FVector ChildTarget = ParentPos + TargetDelta; // Find vector from child to target FVector Error = ChildTarget - ChildPos; // Calculate how much to push the child towards its target float Correction = FMath::Clamp<float>(ThisTimstep * TrailRelaxation, 0.f, 1.f); // Scale correction vector and apply to get new world-space child position. TrailBoneLocations[i] = ChildPos + (Error * Correction); // If desired, prevent bones stretching too far. if(bLimitStretch) { float RefPoseLength = TargetDelta.Size(); FVector CurrentDelta = TrailBoneLocations[i] - TrailBoneLocations[i-1]; float CurrentLength = CurrentDelta.Size(); // If we are too far - cut it back (just project towards parent particle). if( (CurrentLength - RefPoseLength > StretchLimit) && CurrentLength > SMALL_NUMBER ) { FVector CurrentDir = CurrentDelta / CurrentLength; TrailBoneLocations[i] = TrailBoneLocations[i-1] + (CurrentDir * (RefPoseLength + StretchLimit)); } } // Modify child matrix OutBoneTransforms[i] = FBoneTransform(ChildIndex, MeshBases.GetComponentSpaceTransform(ChildIndex)); OutBoneTransforms[i].Transform.SetTranslation(TrailBoneLocations[i]); // Modify rotation of parent matrix to point at this one. // Calculate the direction that parent bone is currently pointing. FVector CurrentBoneDir = OutBoneTransforms[i-1].Transform.TransformVector( GetAlignVector(ChainBoneAxis, bInvertChainBoneAxis) ); CurrentBoneDir = CurrentBoneDir.SafeNormal(SMALL_NUMBER); // Calculate vector from parent to child. FVector NewBoneDir = FVector(OutBoneTransforms[i].Transform.GetTranslation() - OutBoneTransforms[i - 1].Transform.GetTranslation()).SafeNormal(SMALL_NUMBER); // Calculate a quaternion that gets us from our current rotation to the desired one. FQuat DeltaLookQuat = FQuat::FindBetween(CurrentBoneDir, NewBoneDir); FTransform DeltaTM( DeltaLookQuat, FVector(0.f) ); // Apply to the current parent bone transform. FTransform TmpMatrix = FTransform::Identity; TmpMatrix.CopyRotationPart(OutBoneTransforms[i - 1].Transform); TmpMatrix = TmpMatrix * DeltaTM; OutBoneTransforms[i - 1].Transform.CopyRotationPart(TmpMatrix); } // For the last bone in the chain, use the rotation from the bone above it. OutBoneTransforms[ChainLength - 1].Transform.CopyRotationPart(OutBoneTransforms[ChainLength - 2].Transform); // Update OldLocalToWorld OldLocalToWorld = SkelComp->GetTransformMatrix(); }
void FAnimNode_LookAt::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); const FCompactPoseBoneIndex ModifyBoneIndex = BoneToModify.GetCompactPoseIndex(BoneContainer); FTransform ComponentBoneTransform = MeshBases.GetComponentSpaceTransform(ModifyBoneIndex); // get target location FVector TargetLocationInComponentSpace; if (LookAtBone.IsValid(BoneContainer)) { const FTransform& LookAtTransform = MeshBases.GetComponentSpaceTransform(LookAtBone.GetCompactPoseIndex(BoneContainer)); TargetLocationInComponentSpace = LookAtTransform.GetLocation(); } else { TargetLocationInComponentSpace = SkelComp->ComponentToWorld.InverseTransformPosition(LookAtLocation); } FVector OldCurrentTargetLocation = CurrentTargetLocation; FVector NewCurrentTargetLocation = TargetLocationInComponentSpace; if ((NewCurrentTargetLocation - OldCurrentTargetLocation).SizeSquared() > InterpolationTriggerThreashold*InterpolationTriggerThreashold) { if (AccumulatedInterpoolationTime >= InterpolationTime) { // reset current Alpha, we're starting to move AccumulatedInterpoolationTime = 0.f; } PreviousTargetLocation = OldCurrentTargetLocation; CurrentTargetLocation = NewCurrentTargetLocation; } else if (InterpolationTriggerThreashold == 0.f) { CurrentTargetLocation = NewCurrentTargetLocation; } if (InterpolationTime > 0.f) { float CurrentAlpha = AccumulatedInterpoolationTime/InterpolationTime; if (CurrentAlpha < 1.f) { float BlendAlpha = AlphaToBlendType(CurrentAlpha, GetInterpolationType()); CurrentLookAtLocation = FMath::Lerp(PreviousTargetLocation, CurrentTargetLocation, BlendAlpha); } } else { CurrentLookAtLocation = CurrentTargetLocation; } if (bEnableDebug) { UWorld* World = SkelComp->GetWorld(); DrawDebugData(World, SkelComp->GetComponentToWorld(), ComponentBoneTransform.GetLocation(), PreviousTargetLocation, FColor(0, 255, 0)); DrawDebugData(World, SkelComp->GetComponentToWorld(), ComponentBoneTransform.GetLocation(), CurrentTargetLocation, FColor(255, 0, 0)); DrawDebugData(World, SkelComp->GetComponentToWorld(), ComponentBoneTransform.GetLocation(), CurrentLookAtLocation, FColor(0, 0, 255)); } // lookat vector FVector LookAtVector = GetAlignVector(ComponentBoneTransform, LookAtAxis); // flip to target vector if it wasnt negative bool bShouldFlip = LookAtAxis == EAxisOption::X_Neg || LookAtAxis == EAxisOption::Y_Neg || LookAtAxis == EAxisOption::Z_Neg; FVector ToTarget = CurrentLookAtLocation - ComponentBoneTransform.GetLocation(); ToTarget.Normalize(); if (bShouldFlip) { ToTarget *= -1.f; } if ( LookAtClamp > ZERO_ANIMWEIGHT_THRESH ) { float LookAtClampInRadians = FMath::DegreesToRadians(LookAtClamp); float DiffAngle = FMath::Acos(FVector::DotProduct(LookAtVector, ToTarget)); if (LookAtClampInRadians > 0.f && DiffAngle > LookAtClampInRadians) { FVector OldToTarget = ToTarget; FVector DeltaTarget = ToTarget-LookAtVector; float Ratio = LookAtClampInRadians/DiffAngle; DeltaTarget *= Ratio; ToTarget = LookAtVector + DeltaTarget; ToTarget.Normalize(); // UE_LOG(LogAnimation, Warning, TEXT("Recalculation required - old target %f, new target %f"), // FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(LookAtVector, OldToTarget))), FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(LookAtVector, ToTarget)))); } } FQuat DeltaRot; // if want to use look up, if (bUseLookUpAxis) { // find look up vector in local space FVector LookUpVector = GetAlignVector(ComponentBoneTransform, LookUpAxis); // project target to the plane FVector NewTarget = FVector::VectorPlaneProject(ToTarget, LookUpVector); NewTarget.Normalize(); DeltaRot = FQuat::FindBetween(LookAtVector, NewTarget); } else { DeltaRot = FQuat::FindBetween(LookAtVector, ToTarget); } // transform current rotation to delta rotation FQuat CurrentRot = ComponentBoneTransform.GetRotation(); FQuat NewRotation = DeltaRot * CurrentRot; ComponentBoneTransform.SetRotation(NewRotation); OutBoneTransforms.Add(FBoneTransform(ModifyBoneIndex, ComponentBoneTransform)); }