void FAnimNode_AimOffsetLookAt::UpdateFromLookAtTarget(FPoseContext& LocalPoseContext) { const FBoneContainer& RequiredBones = LocalPoseContext.Pose.GetBoneContainer(); if (RequiredBones.GetSkeletalMeshAsset()) { const USkeletalMeshSocket* Socket = RequiredBones.GetSkeletalMeshAsset()->FindSocket(SourceSocketName); if (Socket) { const FTransform SocketLocalTransform = Socket->GetSocketLocalTransform(); FBoneReference SocketBoneReference; SocketBoneReference.BoneName = Socket->BoneName; SocketBoneReference.Initialize(RequiredBones); if (SocketBoneReference.IsValid(RequiredBones)) { const FCompactPoseBoneIndex SocketBoneIndex = SocketBoneReference.GetCompactPoseIndex(RequiredBones); FCSPose<FCompactPose> GlobalPose; GlobalPose.InitPose(LocalPoseContext.Pose); USkeletalMeshComponent* Component = LocalPoseContext.AnimInstanceProxy->GetSkelMeshComponent(); AActor* Actor = Component ? Component->GetOwner() : nullptr; if (Component && Actor && BlendSpace) { const FTransform ActorTransform = Actor->GetTransform(); const FTransform BoneTransform = GlobalPose.GetComponentSpaceTransform(SocketBoneIndex); const FTransform SocketWorldTransform = SocketLocalTransform * BoneTransform * Component->ComponentToWorld; // Convert Target to Actor Space const FTransform TargetWorldTransform(LookAtLocation); const FVector DirectionToTarget = ActorTransform.InverseTransformVectorNoScale(TargetWorldTransform.GetLocation() - SocketWorldTransform.GetLocation()).GetSafeNormal(); const FVector CurrentDirection = ActorTransform.InverseTransformVectorNoScale(SocketWorldTransform.GetUnitAxis(EAxis::X)); const FVector AxisX = FVector::ForwardVector; const FVector AxisY = FVector::RightVector; const FVector AxisZ = FVector::UpVector; const FVector2D CurrentCoords = FMath::GetAzimuthAndElevation(CurrentDirection, AxisX, AxisY, AxisZ); const FVector2D TargetCoords = FMath::GetAzimuthAndElevation(DirectionToTarget, AxisX, AxisY, AxisZ); const FVector BlendInput( FRotator::NormalizeAxis(FMath::RadiansToDegrees(TargetCoords.X - CurrentCoords.X)), FRotator::NormalizeAxis(FMath::RadiansToDegrees(TargetCoords.Y - CurrentCoords.Y)), 0.f); // Set X and Y, so ticking next frame is based on correct weights. X = BlendInput.X; Y = BlendInput.Y; // Generate BlendSampleDataCache from inputs. BlendSpace->GetSamplesFromBlendInput(BlendInput, BlendSampleDataCache); if (CVarAimOffsetLookAtDebug.GetValueOnAnyThread() == 1) { DrawDebugLine(Component->GetWorld(), SocketWorldTransform.GetLocation(), TargetWorldTransform.GetLocation(), FColor::Green); DrawDebugLine(Component->GetWorld(), SocketWorldTransform.GetLocation(), SocketWorldTransform.GetLocation() + SocketWorldTransform.GetUnitAxis(EAxis::X) * (TargetWorldTransform.GetLocation() - SocketWorldTransform.GetLocation()).Size(), FColor::Red); DrawDebugCoordinateSystem(Component->GetWorld(), ActorTransform.GetLocation(), ActorTransform.GetRotation().Rotator(), 100.f); FString DebugString = FString::Printf(TEXT("Socket (X:%f, Y:%f), Target (X:%f, Y:%f), Result (X:%f, Y:%f)") , FMath::RadiansToDegrees(CurrentCoords.X) , FMath::RadiansToDegrees(CurrentCoords.Y) , FMath::RadiansToDegrees(TargetCoords.X) , FMath::RadiansToDegrees(TargetCoords.Y) , BlendInput.X , BlendInput.Y); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0.f, FColor::Red, DebugString, false); } } } } } }
void UDebugSkelMeshComponent::RefreshBoneTransforms(FActorComponentTickFunction* TickFunction) { // Run regular update first so we get RequiredBones up to date. Super::RefreshBoneTransforms(NULL); // Pass NULL so we force non threaded work const bool bIsPreviewInstance = (PreviewInstance && PreviewInstance == AnimScriptInstance); BakedAnimationPoses.Empty(); if(bDisplayBakedAnimation && bIsPreviewInstance && PreviewInstance->RequiredBones.IsValid()) { if(UAnimSequence* Sequence = Cast<UAnimSequence>(PreviewInstance->CurrentAsset)) { BakedAnimationPoses.AddUninitialized(PreviewInstance->RequiredBones.GetNumBones()); bool bSavedUseSourceData = AnimScriptInstance->RequiredBones.ShouldUseSourceData(); AnimScriptInstance->RequiredBones.SetUseRAWData(true); AnimScriptInstance->RequiredBones.SetUseSourceData(false); PreviewInstance->EnableControllers(false); GenSpaceBases(BakedAnimationPoses); AnimScriptInstance->RequiredBones.SetUseRAWData(false); AnimScriptInstance->RequiredBones.SetUseSourceData(bSavedUseSourceData); PreviewInstance->EnableControllers(true); } } SourceAnimationPoses.Empty(); if(bDisplaySourceAnimation && bIsPreviewInstance && PreviewInstance->RequiredBones.IsValid()) { if(UAnimSequence* Sequence = Cast<UAnimSequence>(PreviewInstance->CurrentAsset)) { SourceAnimationPoses.AddUninitialized(PreviewInstance->RequiredBones.GetNumBones()); bool bSavedUseSourceData = AnimScriptInstance->RequiredBones.ShouldUseSourceData(); AnimScriptInstance->RequiredBones.SetUseSourceData(true); PreviewInstance->EnableControllers(false); GenSpaceBases(SourceAnimationPoses); AnimScriptInstance->RequiredBones.SetUseSourceData(bSavedUseSourceData); PreviewInstance->EnableControllers(true); } } UncompressedSpaceBases.Empty(); if (bDisplayRawAnimation && AnimScriptInstance && AnimScriptInstance->RequiredBones.IsValid()) { UncompressedSpaceBases.AddUninitialized(AnimScriptInstance->RequiredBones.GetNumBones()); AnimScriptInstance->RequiredBones.SetUseRAWData(true); GenSpaceBases(UncompressedSpaceBases); AnimScriptInstance->RequiredBones.SetUseRAWData(false); } // Non retargeted pose. NonRetargetedSpaceBases.Empty(); if( bDisplayNonRetargetedPose && AnimScriptInstance && AnimScriptInstance->RequiredBones.IsValid() ) { NonRetargetedSpaceBases.AddUninitialized(AnimScriptInstance->RequiredBones.GetNumBones()); AnimScriptInstance->RequiredBones.SetDisableRetargeting(true); GenSpaceBases(NonRetargetedSpaceBases); AnimScriptInstance->RequiredBones.SetDisableRetargeting(false); } // Only works in PreviewInstance, and not for anim blueprint. This is intended. AdditiveBasePoses.Empty(); if( bDisplayAdditiveBasePose && bIsPreviewInstance && PreviewInstance->RequiredBones.IsValid() ) { if (UAnimSequence* Sequence = Cast<UAnimSequence>(PreviewInstance->CurrentAsset)) { if (Sequence->IsValidAdditive()) { FCSPose<FCompactPose> CSAdditiveBasePose; { FCompactPose AdditiveBasePose; FBlendedCurve AdditiveCurve(PreviewInstance); AdditiveBasePose.SetBoneContainer(&PreviewInstance->RequiredBones); Sequence->GetAdditiveBasePose(AdditiveBasePose, AdditiveCurve, FAnimExtractContext(PreviewInstance->CurrentTime)); CSAdditiveBasePose.InitPose(AdditiveBasePose); } for (int32 i = 0; i < AdditiveBasePoses.Num(); ++i) { FCompactPoseBoneIndex CompactIndex = PreviewInstance->RequiredBones.MakeCompactPoseIndex(FMeshPoseBoneIndex(i)); AdditiveBasePoses[i] = CSAdditiveBasePose.GetComponentSpaceTransform(CompactIndex); } } } } }
bool UAnimPreviewInstance::NativeEvaluateAnimation(FPoseContext& Output) { #if WITH_EDITORONLY_DATA if(bForceRetargetBasePose) { USkeletalMeshComponent* MeshComponent = GetSkelMeshComponent(); if(MeshComponent && MeshComponent->SkeletalMesh) { FAnimationRuntime::FillWithRetargetBaseRefPose(Output.Pose, GetSkelMeshComponent()->SkeletalMesh); } else { // ideally we'll return just ref pose, but not sure if this will work with LODs Output.Pose.ResetToRefPose(); } } else #endif // #if WITH_EDITORONLY_DATA { Super::NativeEvaluateAnimation(Output); } if (bEnableControllers) { UDebugSkelMeshComponent* Component = Cast<UDebugSkelMeshComponent>(GetSkelMeshComponent()); if(Component && CurrentSkeleton) { // update curve controllers UpdateCurveController(); // create bone controllers from if(BoneControllers.Num() > 0 || CurveBoneControllers.Num() > 0) { FCompactPose PreController, PostController; // if set key is true, we should save pre controller local space transform // so that we can calculate the delta correctly if(bSetKey) { PreController = Output.Pose; } FCSPose<FCompactPose> OutMeshPose; OutMeshPose.InitPose(&RequiredBones); // apply curve data first ApplyBoneControllers(Component, CurveBoneControllers, OutMeshPose); // and now apply bone controllers data // it is possible they can be overlapping, but then bone controllers will overwrite ApplyBoneControllers(Component, BoneControllers, OutMeshPose); // convert back to local @todo check this OutMeshPose.ConvertToLocalPoses(Output.Pose); if(bSetKey) { // now we have post controller, and calculate delta now PostController = Output.Pose; SetKeyImplementation(PreController, PostController); } } // if any other bone is selected, still go for set key even if nothing changed else if(Component->BonesOfInterest.Num() > 0) { if(bSetKey) { // in this case, pose is same SetKeyImplementation(Output.Pose, Output.Pose); } } } // we should unset here, just in case somebody clicks the key when it's not valid if(bSetKey) { bSetKey = false; } } return true; }