void UAnimGraphNode_SkeletalControlBase::ConvertToComponentSpaceTransform(const USkeletalMeshComponent* SkelComp, const FTransform & InTransform, FTransform & OutCSTransform, int32 BoneIndex, EBoneControlSpace Space) const { USkeleton * Skeleton = SkelComp->SkeletalMesh->Skeleton; switch (Space) { case BCS_WorldSpace: { OutCSTransform = InTransform; OutCSTransform.SetToRelativeTransform(SkelComp->ComponentToWorld); } break; case BCS_ComponentSpace: { // Component Space, no change. OutCSTransform = InTransform; } break; case BCS_ParentBoneSpace: if (BoneIndex != INDEX_NONE) { const int32 ParentIndex = Skeleton->GetReferenceSkeleton().GetParentIndex(BoneIndex); if (ParentIndex != INDEX_NONE) { const int32 MeshParentIndex = Skeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkelComp->SkeletalMesh, ParentIndex); if (MeshParentIndex != INDEX_NONE) { const FTransform ParentTM = SkelComp->GetBoneTransform(MeshParentIndex); OutCSTransform = InTransform * ParentTM; } else { OutCSTransform = InTransform; } } } break; case BCS_BoneSpace: if (BoneIndex != INDEX_NONE) { const int32 MeshBoneIndex = Skeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkelComp->SkeletalMesh, BoneIndex); if (MeshBoneIndex != INDEX_NONE) { const FTransform BoneTM = SkelComp->GetBoneTransform(MeshBoneIndex); OutCSTransform = InTransform * BoneTM; } else { OutCSTransform = InTransform; } } break; default: if (SkelComp->SkeletalMesh) { UE_LOG(LogAnimation, Warning, TEXT("ConvertToComponentSpaceTransform: Unknown BoneSpace %d for Mesh: %s"), (uint8)Space, *SkelComp->SkeletalMesh->GetFName().ToString()); } else { UE_LOG(LogAnimation, Warning, TEXT("ConvertToComponentSpaceTransform: Unknown BoneSpace %d for Skeleton: %s"), (uint8)Space, *Skeleton->GetFName().ToString()); } break; } }
void FMovieScene3DTransformSectionRecorder::FinalizeSection() { FScopedSlowTask SlowTask(4.0f, NSLOCTEXT("SequenceRecorder", "ProcessingTransforms", "Processing Transforms")); bRecording = false; // if we have a valid animation recorder, we need to build our transforms from the animation // so we properly synchronize our keyframes if(AnimRecorder.IsValid()) { check(BufferedTransforms.Num() == 0); UAnimSequence* AnimSequence = AnimRecorder->GetAnimSequence(); USkeletalMeshComponent* SkeletalMeshComponent = AnimRecorder->GetSkeletalMeshComponent(); if (SkeletalMeshComponent) { USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->MasterPoseComponent != nullptr ? SkeletalMeshComponent->MasterPoseComponent->SkeletalMesh : SkeletalMeshComponent->SkeletalMesh; if (AnimSequence && SkeletalMesh) { // find the root bone int32 RootIndex = INDEX_NONE; USkeleton* AnimSkeleton = AnimSequence->GetSkeleton(); for (int32 TrackIndex = 0; TrackIndex < AnimSequence->RawAnimationData.Num(); ++TrackIndex) { // verify if this bone exists in skeleton int32 BoneTreeIndex = AnimSequence->GetSkeletonIndexFromRawDataTrackIndex(TrackIndex); if (BoneTreeIndex != INDEX_NONE) { int32 BoneIndex = AnimSkeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkeletalMesh, BoneTreeIndex); int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex); if (ParentIndex == INDEX_NONE) { // found root RootIndex = BoneIndex; break; } } } check(RootIndex != INDEX_NONE); const float StartTime = MovieSceneSection->GetStartTime(); // we may need to offset the transform here if the animation was not recorded on the root component FTransform InvComponentTransform = AnimRecorder->GetComponentTransform().Inverse(); FRawAnimSequenceTrack& RawTrack = AnimSequence->RawAnimationData[RootIndex]; const int32 KeyCount = FMath::Max(FMath::Max(RawTrack.PosKeys.Num(), RawTrack.RotKeys.Num()), RawTrack.ScaleKeys.Num()); for (int32 KeyIndex = 0; KeyIndex < KeyCount; KeyIndex++) { FTransform Transform; if (RawTrack.PosKeys.IsValidIndex(KeyIndex)) { Transform.SetTranslation(RawTrack.PosKeys[KeyIndex]); } else if (RawTrack.PosKeys.Num() > 0) { Transform.SetTranslation(RawTrack.PosKeys[0]); } if (RawTrack.RotKeys.IsValidIndex(KeyIndex)) { Transform.SetRotation(RawTrack.RotKeys[KeyIndex]); } else if (RawTrack.RotKeys.Num() > 0) { Transform.SetRotation(RawTrack.RotKeys[0]); } if (RawTrack.ScaleKeys.IsValidIndex(KeyIndex)) { Transform.SetScale3D(RawTrack.ScaleKeys[KeyIndex]); } else if (RawTrack.ScaleKeys.Num() > 0) { Transform.SetScale3D(RawTrack.ScaleKeys[0]); } BufferedTransforms.Add(FBufferedTransformKey(InvComponentTransform * Transform, StartTime + AnimSequence->GetTimeAtFrame(KeyIndex))); } } } } SlowTask.EnterProgressFrame(); // Try to 're-wind' rotations that look like axis flips // We need to do this as a post-process because the recorder cant reliably access 'wound' rotations: // - Net quantize may use quaternions. // - Scene components cache transforms as quaternions. // - Gameplay is free to clamp/fmod rotations as it sees fit. int32 TransformCount = BufferedTransforms.Num(); for(int32 TransformIndex = 0; TransformIndex < TransformCount - 1; TransformIndex++) { FRotator& Rotator = BufferedTransforms[TransformIndex].WoundRotation; FRotator& NextRotator = BufferedTransforms[TransformIndex + 1].WoundRotation; FMath::WindRelativeAnglesDegrees(Rotator.Pitch, NextRotator.Pitch); FMath::WindRelativeAnglesDegrees(Rotator.Yaw, NextRotator.Yaw); FMath::WindRelativeAnglesDegrees(Rotator.Roll, NextRotator.Roll); } SlowTask.EnterProgressFrame(); // never unwind rotations const bool bUnwindRotation = false; // If we are syncing to an animation, use linear interpolation to avoid foot sliding etc. // Otherwise use cubic for better quality (much better for projectiles etc.) const EMovieSceneKeyInterpolation Interpolation = AnimRecorder.IsValid() ? EMovieSceneKeyInterpolation::Linear : EMovieSceneKeyInterpolation::Auto; // add buffered transforms for(const FBufferedTransformKey& BufferedTransform : BufferedTransforms) { const FVector Translation = BufferedTransform.Transform.GetTranslation(); const FVector Rotation = BufferedTransform.WoundRotation.Euler(); const FVector Scale = BufferedTransform.Transform.GetScale3D(); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Translation, EAxis::X, Translation.X, bUnwindRotation), Interpolation); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Translation, EAxis::Y, Translation.Y, bUnwindRotation), Interpolation); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Translation, EAxis::Z, Translation.Z, bUnwindRotation), Interpolation); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, Rotation.X, bUnwindRotation), Interpolation); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, Rotation.Y, bUnwindRotation), Interpolation); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, Rotation.Z, bUnwindRotation), Interpolation); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Scale, EAxis::X, Scale.X, bUnwindRotation), Interpolation); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Scale, EAxis::Y, Scale.Y, bUnwindRotation), Interpolation); MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Scale, EAxis::Z, Scale.Z, bUnwindRotation), Interpolation); } BufferedTransforms.Empty(); SlowTask.EnterProgressFrame(); // now remove linear keys TPair<FRichCurve*, float> CurvesAndTolerances[] = { TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetTranslationCurve(EAxis::X), KINDA_SMALL_NUMBER), TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetTranslationCurve(EAxis::Y), KINDA_SMALL_NUMBER), TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetTranslationCurve(EAxis::Z), KINDA_SMALL_NUMBER), TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetRotationCurve(EAxis::X), KINDA_SMALL_NUMBER), TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetRotationCurve(EAxis::Y), KINDA_SMALL_NUMBER), TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetRotationCurve(EAxis::Z), KINDA_SMALL_NUMBER), TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetScaleCurve(EAxis::X), KINDA_SMALL_NUMBER), TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetScaleCurve(EAxis::Y), KINDA_SMALL_NUMBER), TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetScaleCurve(EAxis::Z), KINDA_SMALL_NUMBER), }; for(TPair<FRichCurve*, float>& CurveAndTolerance : CurvesAndTolerances) { CurveAndTolerance.Key->RemoveRedundantKeys(CurveAndTolerance.Value); } // we cant remove redundant tracks if we were attached as the playback relies on update order of // transform tracks. Without this track, relative transforms would accumulate. if(!bWasAttached) { // now we have reduced our keys, if we dont have any, remove the section as it is redundant if( MovieSceneSection->GetTranslationCurve(EAxis::X).Keys.Num() == 0 && MovieSceneSection->GetTranslationCurve(EAxis::Y).Keys.Num() == 0 && MovieSceneSection->GetTranslationCurve(EAxis::Z).Keys.Num() == 0 && MovieSceneSection->GetRotationCurve(EAxis::X).Keys.Num() == 0 && MovieSceneSection->GetRotationCurve(EAxis::Y).Keys.Num() == 0 && MovieSceneSection->GetRotationCurve(EAxis::Z).Keys.Num() == 0 && MovieSceneSection->GetScaleCurve(EAxis::X).Keys.Num() == 0 && MovieSceneSection->GetScaleCurve(EAxis::Y).Keys.Num() == 0 && MovieSceneSection->GetScaleCurve(EAxis::Z).Keys.Num() == 0) { if(DefaultTransform.Equals(FTransform::Identity)) { MovieScene->RemoveTrack(*MovieSceneTrack.Get()); } } } SlowTask.EnterProgressFrame(); }