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 UAnimInstance::SlotEvaluatePose(FName SlotNodeName, const FA2Pose & SourcePose, FA2Pose & BlendedPose, float SlotNodeWeight) { SCOPE_CYCLE_COUNTER(STAT_AnimNativeEvaluatePoses); if ( SlotNodeWeight > ZERO_ANIMWEIGHT_THRESH ) { // final output of slot pose FA2Pose SlotPose; SlotPose.Bones.AddUninitialized(SourcePose.Bones.Num()); /// blend helper variables TArray<FA2Pose> MontagePoses; TArray<float> MontageWeights; TArray<FAnimMontageInstance *> ValidMontageInstances; // first pass we go through collect weights and valid montages. for (auto Iter = MontageInstances.CreateConstIterator(); Iter; ++Iter) { FAnimMontageInstance * MontageInstance = (*Iter); // @todo make this struct? if (MontageInstance->IsValid() && MontageInstance->Montage->IsValidSlot(SlotNodeName)) { int32 NewIndex = MontageWeights.AddUninitialized(1); MontagePoses.AddZeroed(1); ValidMontageInstances.Add(MontageInstance); MontagePoses[NewIndex].Bones.AddUninitialized(BlendedPose.Bones.Num()); MontageWeights[NewIndex] = MontageInstance->Weight; } } // clean up the MontageWeights to see if they're not summing up correctly float TotalSum = 0.f; for (int32 I=0; I<MontageWeights.Num(); ++I) { // normalize I TotalSum += MontageWeights[I]; } // if it has any valid weight if ( TotalSum > ZERO_ANIMWEIGHT_THRESH ) { // but not 1.f. If 1.f it's useless if (FMath::IsNearlyEqual(TotalSum, 1.f) == false) { for (int32 I=0; I<MontageWeights.Num(); ++I) { // normalize I MontageWeights[I] /= TotalSum; } } } else { FAnimationRuntime::FillWithRefPose(BlendedPose.Bones, RequiredBones); // nothing else to do here, there is no weight return; } // if not, something went wrong. It should have something check (ValidMontageInstances.Num() > 0); // second pass, we fill up MontagePoses with valid data to be full pose for (int32 I=0; I<ValidMontageInstances.Num(); ++I) { FAnimMontageInstance * MontageInstance = ValidMontageInstances[I]; const FAnimTrack * AnimTrack = MontageInstance->Montage->GetAnimationData(SlotNodeName); // find out if this is additive animation EAdditiveAnimationType AdditiveAnimType = AAT_None; if (AnimTrack->IsAdditive()) { AdditiveAnimType = AnimTrack->IsRotationOffsetAdditive()? AAT_RotationOffsetMeshSpace : AAT_LocalSpaceBase; } // get the pose data, temporarily use SlotPose FAnimExtractContext ExtractionContext(MontageInstance->Position, false, MontageInstance->Montage->bEnableRootMotionTranslation, MontageInstance->Montage->bEnableRootMotionRotation, MontageInstance->Montage->RootMotionRootLock); FAnimationRuntime::GetPoseFromAnimTrack(*AnimTrack, RequiredBones, SlotPose.Bones, ExtractionContext); // if additive, we should blend with source to make it fullbody if (AdditiveAnimType == AAT_LocalSpaceBase) { ApplyAdditiveSequence(SourcePose,SlotPose,MontageWeights[I],MontagePoses[I]); } else if (AdditiveAnimType == AAT_RotationOffsetMeshSpace) { BlendRotationOffset(SourcePose,SlotPose,MontageWeights[I],MontagePoses[I]); } else { CopyPose(SlotPose, MontagePoses[I]); } } // allocate for blending FTransformArrayA2** BlendingPoses = new FTransformArrayA2*[MontagePoses.Num()]; for (int32 I=0; I<MontagePoses.Num(); ++I) { BlendingPoses[I] = &MontagePoses[I].Bones; } // now time to blend all montages FAnimationRuntime::BlendPosesTogether(MontagePoses.Num(), (const FTransformArrayA2**)BlendingPoses, (const float*)MontageWeights.GetData(), RequiredBones, SlotPose.Bones); // clean up memory delete[] BlendingPoses; // now blend with Source if ( SlotNodeWeight > 1-ZERO_ANIMWEIGHT_THRESH ) { BlendedPose = SlotPose; } else { // normal non-additive animations BlendSequences(SourcePose, SlotPose, SlotNodeWeight, BlendedPose); } } else { BlendedPose = SourcePose; } }
bool UAnimSingleNodeInstance::NativeEvaluateAnimation(FPoseContext& Output) { if (CurrentAsset != NULL) { //@TODO: animrefactor: Seems like more code duplication than we need if (UBlendSpaceBase* BlendSpace = Cast<UBlendSpaceBase>(CurrentAsset)) { InternalBlendSpaceEvaluatePose(BlendSpace, BlendSampleData, Output.Pose); } else if (UAnimSequence* Sequence = Cast<UAnimSequence>(CurrentAsset)) { if (Sequence->IsValidAdditive()) { FA2Pose BasePose; FA2Pose AdditivePose; BasePose.Bones.AddUninitialized(Output.Pose.Bones.Num()); AdditivePose.Bones.AddUninitialized(Output.Pose.Bones.Num()); FAnimExtractContext ExtractionContext(CurrentTime); Sequence->GetAdditiveBasePose(BasePose.Bones, RequiredBones, ExtractionContext); Sequence->GetAnimationPose(AdditivePose.Bones, RequiredBones, ExtractionContext); if (Sequence->AdditiveAnimType == AAT_LocalSpaceBase) { ApplyAdditiveSequence(BasePose, AdditivePose, 1.0f, Output.Pose); } else { BlendRotationOffset(BasePose, AdditivePose, 1.0f, Output.Pose); } } else { // if sekeltalmesh isn't there, we'll need to use skeleton FAnimationRuntime::GetPoseFromSequence(Sequence, RequiredBones, Output.Pose.Bones, FAnimExtractContext(CurrentTime, Sequence->bEnableRootMotion)); } } else if (UAnimComposite* Composite = Cast<UAnimComposite>(CurrentAsset)) { const FAnimTrack& AnimTrack = Composite->AnimationTrack; // find out if this is additive animation EAdditiveAnimationType AdditiveAnimType = AAT_None; if (AnimTrack.IsAdditive()) { FA2Pose AdditivePose; FA2Pose BasePose; BasePose.Bones.AddUninitialized(Output.Pose.Bones.Num()); AdditivePose.Bones.AddUninitialized(Output.Pose.Bones.Num()); AdditiveAnimType = AnimTrack.IsRotationOffsetAdditive()? AAT_RotationOffsetMeshSpace : AAT_LocalSpaceBase; // get base pose - for now we only support ref pose as base FAnimationRuntime::FillWithRefPose(BasePose.Bones, RequiredBones); //get the additive pose FAnimationRuntime::GetPoseFromAnimTrack(AnimTrack, RequiredBones, AdditivePose.Bones, FAnimExtractContext(CurrentTime, RootMotionMode == ERootMotionMode::RootMotionFromEverything)); // if additive, we should blend with source to make it fullbody if (AdditiveAnimType == AAT_LocalSpaceBase) { ApplyAdditiveSequence(BasePose,AdditivePose,1.f,Output.Pose); } else if (AdditiveAnimType == AAT_RotationOffsetMeshSpace) { BlendRotationOffset(BasePose,AdditivePose,1.f,Output.Pose); } } else { //doesn't handle additive yet FAnimationRuntime::GetPoseFromAnimTrack(AnimTrack, RequiredBones, Output.Pose.Bones, FAnimExtractContext(CurrentTime, RootMotionMode == ERootMotionMode::RootMotionFromEverything)); } } else if (UAnimMontage* Montage = Cast<UAnimMontage>(CurrentAsset)) { // for now only update first slot // in the future, add option to see which slot to see if (Montage->SlotAnimTracks.Num() > 0) { FA2Pose SourcePose; SourcePose.Bones.AddUninitialized(Output.Pose.Bones.Num()); if (Montage->IsValidAdditive()) { #if WITH_EDITORONLY_DATA // if montage is additive, we need to have base pose for the slot pose evaluate if (Montage->PreviewBasePose && Montage->SequenceLength > 0.f) { Montage->PreviewBasePose->GetBonePose(SourcePose.Bones, RequiredBones, FAnimExtractContext(CurrentTime)); } else #endif // WITH_EDITORONLY_DATA { FAnimationRuntime::FillWithRefPose(SourcePose.Bones, RequiredBones); } } else { FAnimationRuntime::FillWithRefPose(SourcePose.Bones, RequiredBones); } SlotEvaluatePose(Montage->SlotAnimTracks[0].SlotName, SourcePose, Output.Pose, 1.f); } } } if(CurrentVertexAnim != NULL) { VertexAnims.Add(FActiveVertexAnim(CurrentVertexAnim, 1.f, CurrentTime)); } return true; }