void FAnimationRuntime::AccumulateLocalSpaceAdditivePose(FCompactPose& BasePose, const FCompactPose& AdditivePose, FBlendedCurve& BaseCurve, const FBlendedCurve& AdditiveCurve, float Weight) { if (Weight > ZERO_ANIMWEIGHT_THRESH) { if (Weight >= (1.f - ZERO_ANIMWEIGHT_THRESH)) { // fast path, no need to weight additive. for (FCompactPoseBoneIndex BoneIndex : BasePose.ForEachBoneIndex()) { BasePose[BoneIndex].Accumulate(AdditivePose[BoneIndex]); } } else { // Slower path w/ weighting const ScalarRegister VBlendWeight(Weight); for (FCompactPoseBoneIndex BoneIndex : BasePose.ForEachBoneIndex()) { // copy additive, because BlendFromIdentityAndAccumulate modifies it. FTransform Additive = AdditivePose[BoneIndex]; FTransform::BlendFromIdentityAndAccumulate(BasePose[BoneIndex], Additive, VBlendWeight); } } } }
FORCEINLINE void BlendPose(const FCompactPose& SourcePose, FCompactPose& ResultPose, const float BlendWeight) { for (FCompactPoseBoneIndex BoneIndex : SourcePose.ForEachBoneIndex()) { BlendTransform<TRANSFORM_BLEND_MODE>(SourcePose[BoneIndex], ResultPose[BoneIndex], BlendWeight); } }
void FAnimationRuntime::FillWithRetargetBaseRefPose(FCompactPose& OutPose, const USkeletalMesh* Mesh) { // Copy Target Asset's ref pose. if (Mesh) { for (FCompactPoseBoneIndex BoneIndex : OutPose.ForEachBoneIndex()) { FMeshPoseBoneIndex PoseIndex = OutPose.GetBoneContainer().MakeMeshPoseIndex(BoneIndex); OutPose[BoneIndex] = Mesh->RetargetBasePose[PoseIndex.GetInt()]; } } }
void FAnimationRuntime::ConvertPoseToAdditive(FCompactPose& TargetPose, const FCompactPose& BasePose) { for (FCompactPoseBoneIndex BoneIndex : BasePose.ForEachBoneIndex()) { FTransform& TargetTransform = TargetPose[BoneIndex]; const FTransform& BaseTransform = BasePose[BoneIndex]; TargetTransform.SetRotation(TargetTransform.GetRotation() * BaseTransform.GetRotation().Inverse()); TargetTransform.SetTranslation(TargetTransform.GetTranslation() - BaseTransform.GetTranslation()); TargetTransform.SetScale3D(TargetTransform.GetScale3D() * BaseTransform.GetSafeScaleReciprocal(BaseTransform.GetScale3D())); TargetTransform.NormalizeRotation(); } }
void FAnimationRuntime::BlendPosesTogetherPerBoneInMeshSpace(TArray<FCompactPose>& SourcePoses, const TArray<FBlendedCurve>& SourceCurves, const UBlendSpaceBase* BlendSpace, const TArray<FBlendSampleData>& BlendSampleDataCache, FCompactPose& ResultPose, FBlendedCurve& ResultCurve) { FQuat NewRotation; USkeleton* Skeleton = BlendSpace->GetSkeleton(); // all this is going to do is to convert SourcePoses.Rotation to be mesh space, and then once it goes through BlendPosesTogetherPerBone, convert back to local for (FCompactPose& Pose : SourcePoses) { for (const FCompactPoseBoneIndex BoneIndex : Pose.ForEachBoneIndex()) { const FCompactPoseBoneIndex ParentIndex = Pose.GetParentBoneIndex(BoneIndex); if (ParentIndex != INDEX_NONE) { NewRotation = Pose[ParentIndex].GetRotation()*Pose[BoneIndex].GetRotation(); NewRotation.Normalize(); } else { NewRotation = Pose[BoneIndex].GetRotation(); } // now copy back to SourcePoses Pose[BoneIndex].SetRotation(NewRotation); } } // now we have mesh space rotation, call BlendPosesTogetherPerBone BlendPosesTogetherPerBone(SourcePoses, SourceCurves, BlendSpace, BlendSampleDataCache, ResultPose, ResultCurve); // now result atoms has the output with mesh space rotation. Convert back to local space, start from back for (const FCompactPoseBoneIndex BoneIndex : ResultPose.ForEachBoneIndex()) { const FCompactPoseBoneIndex ParentIndex = ResultPose.GetParentBoneIndex(BoneIndex); if (ParentIndex != INDEX_NONE) { FQuat LocalBlendQuat = ResultPose[ParentIndex].GetRotation().Inverse()*ResultPose[BoneIndex].GetRotation(); ResultPose[BoneIndex].SetRotation(LocalBlendQuat); ResultPose[BoneIndex].NormalizeRotation(); } } }
void BlendPosePerBone(const TArray<int32>& PerBoneIndices, const FBlendSampleData& BlendSampleDataCache, FCompactPose& ResultPose, const FCompactPose& SourcePose) { const float BlendWeight = BlendSampleDataCache.GetWeight(); TArray<float> PerBoneBlends; for (int32 i = 0; i < BlendSampleDataCache.PerBoneBlendData.Num(); ++i) { PerBoneBlends.Add(FMath::Clamp<float>(BlendSampleDataCache.PerBoneBlendData[i], 0.f, 1.f)); } for (FCompactPoseBoneIndex BoneIndex : SourcePose.ForEachBoneIndex()) { int32 PerBoneIndex = PerBoneIndices[BoneIndex.GetInt()]; if (PerBoneIndex == INDEX_NONE || !BlendSampleDataCache.PerBoneBlendData.IsValidIndex(PerBoneIndex)) { BlendTransform<TRANSFORM_BLEND_MODE>(SourcePose[BoneIndex], ResultPose[BoneIndex], BlendWeight); } else { BlendTransform<TRANSFORM_BLEND_MODE>(SourcePose[BoneIndex], ResultPose[BoneIndex], PerBoneBlends[PerBoneIndex]); } } }
void FAnimationRuntime::BlendLocalPosesPerBoneWeights( FCompactPose& BasePose, const TArray<FCompactPose>& BlendPoses, struct FBlendedCurve& BaseCurve, const TArray<struct FBlendedCurve>& BlendedCurves, const TArray<FPerBoneBlendWeight> & BoneBlendWeights, ECurveBlendOption::Type CurveBlendOption, /*out*/ FCompactPose& OutPose, /*out*/ struct FBlendedCurve& OutCurve) { check(BasePose.GetNumBones() == BoneBlendWeights.Num()); int32 PoseNum = BlendPoses.Num(); TArray<float> MaxPoseWeights; MaxPoseWeights.AddZeroed(PoseNum); for (FCompactPoseBoneIndex BoneIndex : BasePose.ForEachBoneIndex()) { const int32 PoseIndex = BoneBlendWeights[BoneIndex.GetInt()].SourceIndex; const FTransform& BaseAtom = BasePose[BoneIndex]; const float BlendWeight = FMath::Clamp(BoneBlendWeights[BoneIndex.GetInt()].BlendWeight, 0.f, 1.f); MaxPoseWeights[PoseIndex] = FMath::Max(MaxPoseWeights[PoseIndex], BlendWeight); if (BlendWeight < ZERO_ANIMWEIGHT_THRESH) { OutPose[BoneIndex] = BaseAtom; } else if ((1.0 - BlendWeight) < ZERO_ANIMWEIGHT_THRESH) { OutPose[BoneIndex] = BlendPoses[PoseIndex][BoneIndex]; } else // we want blend here { FTransform BlendAtom = BaseAtom; const FTransform& TargetAtom = BlendPoses[PoseIndex][BoneIndex]; BlendAtom.BlendWith(TargetAtom, BlendWeight); OutPose[BoneIndex] = BlendAtom; } } // time to blend curves // the way we blend curve per bone // is to find out max weight per that pose, and then apply that weight to the curve { TArray<const FBlendedCurve*> SourceCurves; TArray<float> SourceWegihts; SourceCurves.SetNumUninitialized(PoseNum+1); SourceWegihts.SetNumUninitialized(PoseNum+1); SourceCurves[0] = &BaseCurve; SourceWegihts[0] = 1.f; for (int32 Idx=0; Idx<PoseNum; ++Idx) { SourceCurves[Idx+1] = &BlendedCurves[Idx]; SourceWegihts[Idx+1] = MaxPoseWeights[Idx]; } BlendCurves(SourceCurves, SourceWegihts, OutCurve, CurveBlendOption); } }