private static void FixRotationKeys(CAnimSequence anim) { for (var trackIndex = 0; trackIndex < anim.Tracks.Count; trackIndex++) { if (trackIndex == 0) { continue; // don't fix root track } var track = anim.Tracks[trackIndex]; for (var keyQuatIndex = 0; keyQuatIndex < track.KeyQuat.Length; keyQuatIndex++) { track.KeyQuat[keyQuatIndex].Conjugate(); } } }
public static CAnimSet ConvertAnims(this USkeleton skeleton, UAnimSequence?animSequence) { var animSet = new CAnimSet(skeleton); // Copy bone names var numBones = skeleton.ReferenceSkeleton.FinalRefBoneInfo.Length; Trace.Assert(skeleton.BoneTree.Length == numBones); animSet.TrackBoneNames = new FName[numBones]; animSet.BonePositions = new CSkeletonBonePosition[numBones]; animSet.BoneModes = new EBoneRetargetingMode[numBones]; for (var boneIndex = 0; boneIndex < numBones; boneIndex++) { // Store bone name animSet.TrackBoneNames[boneIndex] = skeleton.ReferenceSkeleton.FinalRefBoneInfo[boneIndex].Name; // Store skeleton's bone transform CSkeletonBonePosition bonePosition; var transform = skeleton.ReferenceSkeleton.FinalRefBonePose[boneIndex]; bonePosition.Orientation = transform.Rotation; bonePosition.Position = transform.Translation; animSet.BonePositions[boneIndex] = bonePosition; // Process bone retargeting mode var boneMode = skeleton.BoneTree[boneIndex].TranslationRetargetingMode switch { EBoneTranslationRetargetingMode.Skeleton => EBoneRetargetingMode.Mesh, EBoneTranslationRetargetingMode.Animation => EBoneRetargetingMode.Animation, EBoneTranslationRetargetingMode.AnimationScaled => EBoneRetargetingMode.AnimationScaled, EBoneTranslationRetargetingMode.AnimationRelative => EBoneRetargetingMode.AnimationRelative, EBoneTranslationRetargetingMode.OrientAndScale => EBoneRetargetingMode.OrientAndScale, _ => EBoneRetargetingMode.OrientAndScale //todo: other modes? }; animSet.BoneModes[boneIndex] = boneMode; } // Check for NULL 'animSequence' only after CAnimSet is created: we're doing ConvertAnims(null) to create an empty AnimSet if (animSequence == null) { return(animSet); } var numTracks = animSequence.GetNumTracks(); // Store UAnimSequence in 'OriginalAnims' array, we just need it from time to time //OriginalAnims.Add(animSequence); // Create CAnimSequence var dst = new CAnimSequence(animSequence); animSet.Sequences.Add(dst); dst.Name = animSequence.Name; dst.NumFrames = animSequence.NumFrames; dst.Rate = animSequence.NumFrames / animSequence.SequenceLength * animSequence.RateScale; dst.bAdditive = animSequence.AdditiveAnimType != AAT_None; // Store information for animation retargeting. // Reference: UAnimSequence::GetRetargetTransforms() FTransform[]? retargetTransforms = null; if (animSequence.RetargetSource.IsNone && animSequence.RetargetSourceAssetReferencePose is { Length : > 0 })
private static void AdjustSequenceBySkeleton(FReferenceSkeleton skeleton, FTransform[] transforms, CAnimSequence anim) { if (skeleton.FinalRefBoneInfo.Length == 0 || skeleton.FinalRefBoneInfo.Length != transforms.Length) { return; } for (var trackIndex = 0; trackIndex < anim.Tracks.Count; trackIndex++) { var track = anim.Tracks[trackIndex]; var boneScale = skeleton.GetBoneScale(transforms, trackIndex); if (Math.Abs(boneScale.X - 1.0f) > 0.001f || Math.Abs(boneScale.Y - 1.0f) > 0.001f || Math.Abs(boneScale.Z - 1.0f) > 0.001f) { for (int keyIndex = 0; keyIndex < track.KeyPos.Length; keyIndex++) { // Scale translation by accumulated bone scale value track.KeyPos[keyIndex].Scale(boneScale); } } } }