void FAnimationRuntime::ConvertPoseToMeshSpace(const TArray<FTransform> & LocalTransforms, TArray<FTransform> & MeshSpaceTransforms, const FBoneContainer& RequiredBones) { const int32 NumBones = RequiredBones.GetNumBones(); // right now all this does is to convert to SpaceBases check( NumBones == LocalTransforms.Num() ); check( NumBones == MeshSpaceTransforms.Num() ); const FTransform* LocalTransformsData = LocalTransforms.GetData(); FTransform* SpaceBasesData = MeshSpaceTransforms.GetData(); const TArray<FBoneIndexType> & RequiredBoneIndexArray = RequiredBones.GetBoneIndicesArray(); // First bone is always root bone, and it doesn't have a parent. { check( RequiredBoneIndexArray[0] == 0 ); MeshSpaceTransforms[0] = LocalTransforms[0]; } const int32 NumRequiredBones = RequiredBoneIndexArray.Num(); for(int32 i=1; i<NumRequiredBones; i++) { const int32 BoneIndex = RequiredBoneIndexArray[i]; FPlatformMisc::Prefetch(SpaceBasesData + BoneIndex); // For all bones below the root, final component-space transform is relative transform * component-space transform of parent. const int32 ParentIndex = RequiredBones.GetParentBoneIndex(BoneIndex); FPlatformMisc::Prefetch(SpaceBasesData + ParentIndex); FTransform::Multiply(SpaceBasesData + BoneIndex, LocalTransformsData + BoneIndex, SpaceBasesData + ParentIndex); checkSlow( MeshSpaceTransforms[BoneIndex].IsRotationNormalized() ); checkSlow( !MeshSpaceTransforms[BoneIndex].ContainsNaN() ); } }
void FAnimationRuntime::FillWithRefPose(TArray<FTransform> & OutAtoms, const FBoneContainer& RequiredBones) { // Copy Target Asset's ref pose. OutAtoms = RequiredBones.GetRefPoseArray(); // If retargeting is disabled, copy ref pose from Skeleton, rather than mesh. // this is only used in editor and for debugging. if( RequiredBones.GetDisableRetargeting() ) { checkSlow( RequiredBones.IsValid() ); // Only do this if we have a mesh. otherwise we're not retargeting animations. if( RequiredBones.GetSkeletalMeshAsset() ) { TArray<int32> const & PoseToSkeletonBoneIndexArray = RequiredBones.GetPoseToSkeletonBoneIndexArray(); TArray<FBoneIndexType> const & RequireBonesIndexArray = RequiredBones.GetBoneIndicesArray(); TArray<FTransform> const & SkeletonRefPose = RequiredBones.GetSkeletonAsset()->GetRefLocalPoses(); for (int32 ArrayIndex = 0; ArrayIndex<RequireBonesIndexArray.Num(); ArrayIndex++) { int32 const & PoseBoneIndex = RequireBonesIndexArray[ArrayIndex]; int32 const & SkeletonBoneIndex = PoseToSkeletonBoneIndexArray[PoseBoneIndex]; // Pose bone index should always exist in Skeleton checkSlow(SkeletonBoneIndex != INDEX_NONE); OutAtoms[PoseBoneIndex] = SkeletonRefPose[SkeletonBoneIndex]; } } } }
void FAnimationRuntime::InitializeTransform(const FBoneContainer& RequiredBones, /*inout*/ FTransformArrayA2 & Atoms) { check( Atoms.Num() == RequiredBones.GetNumBones() ); const TArray<FBoneIndexType> & RequiredBoneIndices = RequiredBones.GetBoneIndicesArray(); for (int32 j = 0; j < RequiredBoneIndices.Num(); ++j) { const int32 BoneIndex = RequiredBoneIndices[j]; Atoms[BoneIndex].SetIdentity(); } }
/** * Scale transforms by Weight. * Result is obviously NOT normalized. */ void FAnimationRuntime::ApplyWeightToTransform(const FBoneContainer& RequiredBones, /*inout*/ FTransformArrayA2& Atoms, float Weight) { const TArray<FBoneIndexType> & RequiredBoneIndices = RequiredBones.GetBoneIndicesArray(); ScalarRegister MultWeight(Weight); for (int32 j = 0; j < RequiredBoneIndices.Num(); ++j) { const int32 BoneIndex = RequiredBoneIndices[j]; Atoms[BoneIndex] *= MultWeight; } }
void FAnimationRuntime::CombineWithAdditiveAnimations(int32 NumAdditivePoses, const FTransformArrayA2** SourceAdditivePoses, const float* SourceAdditiveWeights, const FBoneContainer& RequiredBones, /*inout*/ FTransformArrayA2& Atoms) { const TArray<FBoneIndexType> & RequiredBoneIndices = RequiredBones.GetBoneIndicesArray(); for (int32 PoseIndex = 0; PoseIndex < NumAdditivePoses; ++PoseIndex) { const ScalarRegister VBlendWeight(SourceAdditiveWeights[PoseIndex]); const FTransformArrayA2& SourceAtoms = *SourceAdditivePoses[PoseIndex]; for (int32 j = 0; j < RequiredBoneIndices.Num(); ++j) { const int32 BoneIndex = RequiredBoneIndices[j]; FTransform SourceAtom = SourceAtoms[BoneIndex]; FTransform::BlendFromIdentityAndAccumulate(Atoms[BoneIndex], SourceAtom, VBlendWeight); } } }
void FAnimationRuntime::CreateMaskWeights(TArray<FPerBoneBlendWeight> & BoneBlendWeights, const TArray<FInputBlendPose> &BlendFilters, const FBoneContainer& RequiredBones, const USkeleton* Skeleton) { if ( Skeleton ) { const TArray<FBoneIndexType> & RequiredBoneIndices = RequiredBones.GetBoneIndicesArray(); BoneBlendWeights.Empty(RequiredBoneIndices.Num()); BoneBlendWeights.AddZeroed(RequiredBoneIndices.Num()); // base mask bone for (int32 PoseIndex=0; PoseIndex<BlendFilters.Num(); ++PoseIndex) { const FInputBlendPose& BlendPose = BlendFilters[PoseIndex]; for (int32 BranchIndex=0; BranchIndex<BlendPose.BranchFilters.Num(); ++BranchIndex) { const FBranchFilter& BranchFilter = BlendPose.BranchFilters[BranchIndex]; int32 MaskBoneIndex = RequiredBones.GetPoseBoneIndexForBoneName(BranchFilter.BoneName); // how much weight increase Per depth float MaxWeight = (BranchFilter.BlendDepth > 0) ? 1.f : -1.f; float IncreaseWeightPerDepth = (BranchFilter.BlendDepth != 0) ? (1.f/((float)BranchFilter.BlendDepth)) : 1.f; // go through skeleton tree requiredboneindices for (int32 BoneIndex = 0; BoneIndex<RequiredBoneIndices.Num(); ++BoneIndex) { int32 MeshBoneIndex = RequiredBoneIndices[BoneIndex]; int32 Depth = RequiredBones.GetDepthBetweenBones(MeshBoneIndex, MaskBoneIndex); // if Depth == -1, it's not a child if( Depth != -1 ) { // when you write to buffer, you'll need to match with BasePoses BoneIndex FPerBoneBlendWeight& BoneBlendWeight = BoneBlendWeights[BoneIndex]; BoneBlendWeight.SourceIndex = PoseIndex; float BlendIncrease = IncreaseWeightPerDepth * (float)(Depth + 1); BoneBlendWeight.BlendWeight = FMath::Clamp<float>(BoneBlendWeight.BlendWeight + BlendIncrease, 0.f, 1.f); } } } } } }