void UAnimPreviewInstance::MontagePreview_StepBackward() { if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset)) { bool bWasPlaying = IsPlayingMontage() && (bLooping || bPlaying); // we need to handle non-looped case separately, even if paused during playthrough MontagePreview_SetReverse(true); if (! bWasPlaying) { if (! bLooping) { float StoppedAt = CurrentTime; if (! bWasPlaying) { // play montage but at last known location MontagePreview_Restart(); SetPosition(StoppedAt, false); } int32 LastPreviewSectionIdx = MontagePreview_FindLastSection(MontagePreviewStartSectionIdx); if (FMath::Abs(CurrentTime - (Montage->CompositeSections[LastPreviewSectionIdx].GetTime() + Montage->GetSectionLength(LastPreviewSectionIdx))) <= MontagePreview_CalculateStepLength()) { // special case as we could stop at the end of our last section which is also beginning of following section - we don't want to get stuck there, but be inside of our starting section Montage_JumpToSection(Montage->GetSectionName(LastPreviewSectionIdx)); } else if (FMath::Abs(CurrentTime - Montage->CompositeSections[MontagePreviewStartSectionIdx].GetTime()) <= MontagePreview_CalculateStepLength()) { // we're at the end of playing backward, jump right to the end Montage_JumpToSectionsEnd(Montage->GetSectionName(MontagePreviewStartSectionIdx)); if (! bWasPlaying) { MontagePreview_SetPlaying(false); } return; // can't go further than beginning of first section } } else { MontagePreview_Restart(); } } MontagePreview_SetPlaying(true); // Advance a single frame, leaving it paused afterwards int32 NumFrames = Montage->GetNumberOfFrames(); // Add DELTA to prefer next frame when we're close to the boundary float CurrentFraction = CurrentTime / Montage->SequenceLength + DELTA; float NextFrame = FMath::Clamp<float>(FMath::FloorToFloat(CurrentFraction * NumFrames) - 1.0f, 0, NumFrames); float NewTime = Montage->SequenceLength * (NextFrame / NumFrames); GetSkelMeshComponent()->GlobalAnimRateScale = 1.0f; GetSkelMeshComponent()->TickAnimation(FMath::Abs(NewTime - CurrentTime)); MontagePreview_SetPlaying(false); } }
void UAnimSingleNodeInstance::SetVertexAnimation(UVertexAnimation * NewVertexAnim, bool bIsLooping, float InPlayRate) { if (NewVertexAnim != CurrentVertexAnim) { CurrentVertexAnim = NewVertexAnim; } if (USkeletalMeshComponent * MeshComponent = GetSkelMeshComponent()) { if (MeshComponent->SkeletalMesh == NULL) { // if it does not have SkeletalMesh, we nullify it CurrentVertexAnim = NULL; } else if (CurrentVertexAnim != NULL) { // if we have an anim, make sure their mesh matches, otherwise, null it if (MeshComponent->SkeletalMesh != CurrentVertexAnim->BaseSkelMesh) { // clear asset since we do not have matching skeleton CurrentVertexAnim = NULL; } } } bLooping = bIsLooping; PlayRate = InPlayRate; // reinitialize InitializeAnimation(); }
void UAnimInstance::SequenceEvaluatePose(UAnimSequenceBase* Sequence, FA2Pose& Pose, const FAnimExtractContext & ExtractionContext) { SCOPE_CYCLE_COUNTER(STAT_AnimNativeEvaluatePoses); checkSlow( RequiredBones.IsValid() ); USkeletalMeshComponent* Component = GetSkelMeshComponent(); if(const UAnimSequence* AnimSequence = Cast<const UAnimSequence>(Sequence)) { FAnimationRuntime::GetPoseFromSequence( AnimSequence, RequiredBones, /*out*/ Pose.Bones, ExtractionContext); } else if(const UAnimComposite* Composite = Cast<const UAnimComposite>(Sequence)) { FAnimationRuntime::GetPoseFromAnimTrack( Composite->AnimationTrack, RequiredBones, /*out*/ Pose.Bones, ExtractionContext); } else { #if 0 UE_LOG(LogAnimation, Log, TEXT("FAnimationRuntime::GetPoseFromSequence - %s - No animation data!"), *GetFName()); #endif FAnimationRuntime::FillWithRefPose(Pose.Bones, RequiredBones); } }
void UAnimPreviewInstance::SetKeyImplementation(const FCompactPose& PreControllerInLocalSpace, const FCompactPose& PostControllerInLocalSpace) { #if WITH_EDITOR // evaluate the curve data first UAnimSequence* CurrentSequence = Cast<UAnimSequence>(CurrentAsset); UDebugSkelMeshComponent* Component = Cast<UDebugSkelMeshComponent> (GetSkelMeshComponent()); if(CurrentSequence && CurrentSkeleton && Component && Component->SkeletalMesh) { FScopedTransaction ScopedTransaction(LOCTEXT("SetKey", "Set Key")); CurrentSequence->Modify(true); Modify(); TArray<FName> BonesToModify; // need to get component transform first. Depending on when this gets called, the transform is not up-to-date. // first look at the bonecontrollers, and convert each bone controller to transform curve key // and add new curvebonecontrollers with additive data type // clear bone controller data for(auto& SingleBoneController : BoneControllers) { // find bone name, and just get transform of the bone in local space // and get the additive data // find if this already exists, then just add curve data only FName BoneName = SingleBoneController.BoneToModify.BoneName; // now convert data const FMeshPoseBoneIndex MeshBoneIndex(Component->GetBoneIndex(BoneName)); const FCompactPoseBoneIndex BoneIndex = RequiredBones.MakeCompactPoseIndex(MeshBoneIndex); FTransform LocalTransform = PostControllerInLocalSpace[BoneIndex]; // now we have LocalTransform and get additive data FTransform AdditiveTransform = LocalTransform.GetRelativeTransform(PreControllerInLocalSpace[BoneIndex]); AddKeyToSequence(CurrentSequence, CurrentTime, BoneName, AdditiveTransform); BonesToModify.Add(BoneName); } // see if the bone is selected right now and if that is added - if bone is selected, we should add identity key to it. if ( Component->BonesOfInterest.Num() > 0 ) { // if they're selected, we should add to the modifyBone list even if they're not modified, so that they can key that point. // first make sure those are added // if not added, make sure to set the key for them for (const auto& BoneIndex : Component->BonesOfInterest) { FName BoneName = Component->GetBoneName(BoneIndex); // if it's not on BonesToModify, add identity here. if (!BonesToModify.Contains(BoneName)) { AddKeyToSequence(CurrentSequence, CurrentTime, BoneName, FTransform::Identity); } } } ResetModifiedBone(false); OnSetKeyCompleteDelegate.ExecuteIfBound(); } #endif }
void UAnimSingleNodeInstance::SetAnimationAsset(class UAnimationAsset* NewAsset,bool bIsLooping,float InPlayRate) { if (NewAsset != CurrentAsset) { CurrentAsset = NewAsset; } if (USkeletalMeshComponent * MeshComponent = GetSkelMeshComponent()) { if (MeshComponent->SkeletalMesh == NULL) { // if it does not have SkeletalMesh, we nullify it CurrentAsset = NULL; } else if (CurrentAsset != NULL) { // if we have an asset, make sure their skeleton matches, otherwise, null it if (CurrentSkeleton != CurrentAsset->GetSkeleton()) { // clear asset since we do not have matching skeleton CurrentAsset = NULL; } } } bLooping = bIsLooping; PlayRate = InPlayRate; CurrentTime = 0.f; BlendSpaceInput = FVector::ZeroVector; #if WITH_EDITORONLY_DATA PreviewPoseCurrentTime = 0.0f; #endif UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset); if ( Montage!=NULL ) { ReinitializeSlotNodes(); if ( Montage->SlotAnimTracks.Num() > 0 ) { RegisterSlotNodeWithAnimInstance(Montage->SlotAnimTracks[0].SlotName); } RestartMontage( Montage ); SetPlaying(bPlaying); } else { // otherwise stop all montages StopAllMontages(0.25f); UBlendSpaceBase * BlendSpace = Cast<UBlendSpaceBase>(CurrentAsset); if(BlendSpace) { BlendSpace->InitializeFilter(&BlendFilter); } } }
APawn* UAnimInstance::TryGetPawnOwner() { USkeletalMeshComponent* OwnerComponent = GetSkelMeshComponent(); if (AActor* OwnerActor = OwnerComponent->GetOwner()) { return Cast<APawn>(OwnerActor); } return NULL; }
void UAnimSingleNodeInstance::NativeInitializeAnimation() { CurrentAsset = NULL; CurrentVertexAnim = NULL; #if WITH_EDITORONLY_DATA PreviewPoseCurrentTime = 0.0f; #endif // it's already doing it when evaluate BlendSpaceInput = FVector::ZeroVector; CurrentTime = 0.f; USkeletalMeshComponent* SkelComp = GetSkelMeshComponent(); SkelComp->AnimationData.Initialize(this); }
void UAnimSingleNodeInstance::InternalBlendSpaceEvaluatePose(class UBlendSpaceBase* BlendSpace, TArray<FBlendSampleData>& BlendSampleDataCache, struct FA2Pose& Pose, bool bIsLooping) { USkeletalMeshComponent* Component = GetSkelMeshComponent(); if (BlendSpace->IsValidAdditive()) { FA2Pose BasePose; FA2Pose AdditivePose; BasePose.Bones.AddUninitialized(Pose.Bones.Num()); AdditivePose.Bones.AddUninitialized(Pose.Bones.Num()); #if WITH_EDITORONLY_DATA if (BlendSpace->PreviewBasePose) { BlendSpace->PreviewBasePose->GetBonePose(/*out*/ BasePose.Bones, RequiredBones, FAnimExtractContext(PreviewPoseCurrentTime, bIsLooping)); } else #endif // WITH_EDITORONLY_DATA { // otherwise, get ref pose FAnimationRuntime::FillWithRefPose(BasePose.Bones, RequiredBones); } FAnimationRuntime::GetPoseFromBlendSpace( BlendSpace, BlendSampleDataCache, bIsLooping, RequiredBones, /*out*/ AdditivePose.Bones); if (BlendSpace->IsA(UAimOffsetBlendSpace::StaticClass()) || BlendSpace->IsA(UAimOffsetBlendSpace1D::StaticClass()) ) { BlendRotationOffset(BasePose, AdditivePose, 1.f, Pose); } else { ApplyAdditiveSequence(BasePose, AdditivePose, 1.f, Pose); } } else { FAnimationRuntime::GetPoseFromBlendSpace( BlendSpace, BlendSampleDataCache, bIsLooping, RequiredBones, /*out*/ Pose.Bones); } }
void UAnimSingleNodeInstance::SetAnimationAsset(class UAnimationAsset* NewAsset,bool bIsLooping,float InPlayRate) { if (NewAsset != CurrentAsset) { CurrentAsset = NewAsset; } if (USkeletalMeshComponent * MeshComponent = GetSkelMeshComponent()) { if (MeshComponent->SkeletalMesh == NULL) { // if it does not have SkeletalMesh, we nullify it CurrentAsset = NULL; } else if (CurrentAsset != NULL) { // if we have an asset, make sure their skeleton matches, otherwise, null it if (CurrentSkeleton != CurrentAsset->GetSkeleton()) { // clear asset since we do not have matching skeleton CurrentAsset = NULL; } } } bLooping = bIsLooping; PlayRate = InPlayRate; CurrentTime = 0.f; #if WITH_EDITORONLY_DATA PreviewPoseCurrentTime = 0.0f; #endif UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset); if ( Montage!=NULL ) { ActiveSlotWeights.Empty(); if ( Montage->SlotAnimTracks.Num() > 0 ) { RegisterSlotNode(Montage->SlotAnimTracks[0].SlotName); } RestartMontage( Montage ); } else { // otherwise stop all montages StopAllMontages(0.25f); } }
void UAnimInstance::RecalcRequiredBones() { USkeletalMeshComponent * SkelMeshComp = GetSkelMeshComponent(); check( SkelMeshComp ) if( SkelMeshComp->SkeletalMesh && SkelMeshComp->SkeletalMesh->Skeleton ) { RequiredBones.InitializeTo(SkelMeshComp->RequiredBones, *SkelMeshComp->SkeletalMesh); } else if( CurrentSkeleton != NULL ) { RequiredBones.InitializeTo(SkelMeshComp->RequiredBones, *CurrentSkeleton); } // When RequiredBones mapping has changed, AnimNodes need to update their bones caches. bBoneCachesInvalidated = true; }
void UAnimSingleNodeInstance::InternalBlendSpaceEvaluatePose(class UBlendSpaceBase* BlendSpace, TArray<FBlendSampleData>& BlendSampleDataCache, FPoseContext& OutContext) { USkeletalMeshComponent* Component = GetSkelMeshComponent(); if (BlendSpace->IsValidAdditive()) { FCompactPose& OutPose = OutContext.Pose; FBlendedCurve& OutCurve = OutContext.Curve; FCompactPose AdditivePose; FBlendedCurve AdditiveCurve; AdditivePose.SetBoneContainer(&OutPose.GetBoneContainer()); AdditiveCurve.InitFrom(OutCurve); #if WITH_EDITORONLY_DATA if (BlendSpace->PreviewBasePose) { BlendSpace->PreviewBasePose->GetBonePose(/*out*/ OutPose, /*out*/OutCurve, FAnimExtractContext(PreviewPoseCurrentTime)); } else #endif // WITH_EDITORONLY_DATA { // otherwise, get ref pose OutPose.ResetToRefPose(); } BlendSpace->GetAnimationPose(BlendSampleDataCache, AdditivePose, AdditiveCurve); enum EAdditiveAnimationType AdditiveType = BlendSpace->bRotationBlendInMeshSpace? AAT_RotationOffsetMeshSpace : AAT_LocalSpaceBase; FAnimationRuntime::AccumulateAdditivePose(OutPose, AdditivePose, OutCurve, AdditiveCurve, 1.f, AdditiveType); } else { BlendSpace->GetAnimationPose(BlendSampleDataCache, OutContext.Pose, OutContext.Curve); } }
void UAnimInstance::InitializeAnimation() { // make sure your skeleton is initialized // you can overwrite different skeleton USkeletalMeshComponent* OwnerComponent = GetSkelMeshComponent(); if (OwnerComponent->SkeletalMesh != NULL) { CurrentSkeleton = OwnerComponent->SkeletalMesh->Skeleton; } else { CurrentSkeleton = NULL; } if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Cast<UAnimBlueprintGeneratedClass>(GetClass())) { // Grab a pointer to the root node if (AnimBlueprintClass->RootAnimNodeProperty != NULL) { RootNode = AnimBlueprintClass->RootAnimNodeProperty->ContainerPtrToValuePtr<FAnimNode_Base>(this); } else { RootNode = NULL; } // if no mesh, use Blueprint Skeleton if (CurrentSkeleton == NULL) { CurrentSkeleton = AnimBlueprintClass->TargetSkeleton; } #if WITH_EDITORONLY_DATA if (UAnimBlueprint* Blueprint = Cast<UAnimBlueprint>(AnimBlueprintClass->ClassGeneratedBy)) { if (Blueprint->Status == BS_Error) { RootNode = NULL; } } #endif #if WITH_EDITOR LifeTimer = 0.0; CurrentLifeTimerScrubPosition = 0.0; if (UAnimBlueprint* Blueprint = Cast<UAnimBlueprint>(AnimBlueprintClass->ClassGeneratedBy)) { if (Blueprint->GetObjectBeingDebugged() == this) { // Reset the snapshot buffer AnimBlueprintClass->GetAnimBlueprintDebugData().ResetSnapshotBuffer(); } } #endif } // before initialize, need to recalculate required bone list RecalcRequiredBones(); // Clear cached list, we're about to re-update it. ActiveSlotWeights.Empty(); ClearMorphTargets(); NativeInitializeAnimation(); BlueprintInitializeAnimation(); if (RootNode != NULL) { IncrementContextCounter(); FAnimationInitializeContext InitContext(this); RootNode->Initialize(InitContext); } }
UWorld* UAnimInstance::GetWorld() const { return GetSkelMeshComponent()->GetWorld(); }
USkeletalMeshComponent* UAnimInstance::GetOwningComponent() { return GetSkelMeshComponent(); }
AActor* UAnimInstance::GetOwningActor() { USkeletalMeshComponent* OwnerComponent = GetSkelMeshComponent(); return OwnerComponent->GetOwner(); }
void UAnimInstance::TriggerAnimNotifies(float DeltaSeconds) { TArray<const FAnimNotifyEvent *> NewActiveAnimNotifyState; USkeletalMeshComponent * SkelMeshComp = GetSkelMeshComponent(); // Remove NULL entries. ActiveAnimNotifyState.RemoveSwap(NULL); for (int32 Index=0; Index<AnimNotifies.Num(); Index++) { const FAnimNotifyEvent * AnimNotifyEvent = AnimNotifies[Index]; // AnimNotifyState if( AnimNotifyEvent->NotifyStateClass ) { if( !ActiveAnimNotifyState.RemoveSingleSwap(AnimNotifyEvent) ) { AnimNotifyEvent->NotifyStateClass->NotifyBegin(SkelMeshComp, Cast<UAnimSequence>(AnimNotifyEvent->NotifyStateClass->GetOuter())); } NewActiveAnimNotifyState.Add(AnimNotifyEvent); continue; } // This checking-for-blueprint class is a hack to allow the old UAnimNotify_* notifies to work for a little longer until they are all removed. // Once done, we can remove passing the UAnimNotify to the custom event notifies (since they cant contain custom/user data anyways). // The "is blue print class" can just become a "is notify != null". bool bIsBlueprintNotify = false; if( AnimNotifyEvent->Notify != NULL) { if( !AnimNotifies[Index]->Notify->GetClass()->HasAllClassFlags(CLASS_Native) ) { bIsBlueprintNotify = true; } } if( bIsBlueprintNotify ) { // Blueprint notify: just call Notify. UAnimNotify will forward this to the blueprintable event which will do the work. AnimNotifyEvent->Notify->Notify(SkelMeshComp, Cast<UAnimSequence>(AnimNotifyEvent->Notify->GetOuter())); } else if( AnimNotifyEvent->NotifyName != NAME_None ) { // Custom Event based notifies. These will call a AnimNotify_* function on the AnimInstance. FString FuncName = FString::Printf(TEXT("AnimNotify_%s"), *AnimNotifyEvent->NotifyName.ToString()); FName FuncFName = FName(*FuncName); UFunction* Function = FindFunction(FuncFName); if( Function ) { // if parameter is none, add event if ( Function->NumParms == 0 ) { ProcessEvent( Function, NULL ); } else if ( Function->NumParms == 1 && Cast<UObjectProperty>(Function->PropertyLink) != NULL) { struct FAnimNotifierHandler_Parms { UAnimNotify* Notify; }; FAnimNotifierHandler_Parms Parms; Parms.Notify = AnimNotifyEvent->Notify; ProcessEvent( Function, &Parms ); } else { // Actor has event, but with different parameters. Print warning UE_LOG(LogAnimNotify, Warning, TEXT("Anim notifier named %s, but the parameter number does not match or not of the correct type"), *FuncName); } } } } // Send end notification to AnimNotifyState not active anymore. for(int32 Index=0; Index<ActiveAnimNotifyState.Num(); Index++) { const FAnimNotifyEvent * AnimNotifyEvent = ActiveAnimNotifyState[Index]; AnimNotifyEvent->NotifyStateClass->NotifyEnd(SkelMeshComp, Cast<UAnimSequence>(AnimNotifyEvent->NotifyStateClass->GetOuter())); } // Switch our arrays. ActiveAnimNotifyState = NewActiveAnimNotifyState; // Tick currently active AnimNotifyState for(int32 Index=0; Index<ActiveAnimNotifyState.Num(); Index++) { const FAnimNotifyEvent * AnimNotifyEvent = ActiveAnimNotifyState[Index]; AnimNotifyEvent->NotifyStateClass->NotifyTick(SkelMeshComp, Cast<UAnimSequence>(AnimNotifyEvent->NotifyStateClass->GetOuter()), DeltaSeconds); } }
void UAnimInstance::AnimNotify_Sound(UAnimNotify* AnimNotify) { AnimNotify->Notify(GetSkelMeshComponent(), NULL); }
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; }