Beispiel #1
0
        public AnimExporter(USkeleton skeleton, UAnimSequence?animSequence = null)
        {
            AnimSequences = new List <Anim>();
            AnimName      = animSequence?.Owner?.Name ?? animSequence?.Name ?? skeleton.Owner?.Name ?? skeleton.Name;

            var anim = skeleton.ConvertAnims(animSequence);

            if (anim.Sequences.Count == 0)
            {
                // empty CAnimSet
                return;
            }

            // Determine if CAnimSet will save animations as separate psa files, or all at once
            var originalAnim = anim.GetPrimaryAnimObject();

            if (originalAnim == anim.OriginalAnim || anim.Sequences.Count == 1)
            {
                // Export all animations in a single file
                DoExportPsa(anim, 0);
            }
            else
            {
                //var skeleton = (USkeleton) anim.OriginalAnim;

                // Export animations separately, this will happen only when CAnimSet has
                // a few sequences (but more than one)
                var tempAnimSet = new CAnimSet();
                tempAnimSet.CopyAllButSequences(anim);
                // Now we have a copy of AnimSet, let's set up Sequences array to a single
                // item and export one-by-one
                for (int animIndex = 0; animIndex < anim.Sequences.Count; animIndex++)
                {
                    var seq = anim.Sequences[animIndex];
                    tempAnimSet.Sequences.Clear();
                    tempAnimSet.Sequences.Add(seq);
                    // Do the export, pass UAnimSequence as the "main" object, so it will be
                    // used as psa file name.
                    DoExportPsa(tempAnimSet, animIndex);
                }
                // Ensure TempAnimSet destructor will not release Sequences as they are owned by Anim object
                tempAnimSet.Sequences.Clear();
            }
        }
Beispiel #2
0
        public static bool TryConvert(this USkeleton originalSkeleton, out List <CSkelMeshBone> bones)
        {
            bones = new List <CSkelMeshBone>();
            for (var i = 0; i < originalSkeleton.ReferenceSkeleton.FinalRefBoneInfo.Length; i++)
            {
                var skeletalMeshBone = new CSkelMeshBone
                {
                    Name        = originalSkeleton.ReferenceSkeleton.FinalRefBoneInfo[i].Name,
                    ParentIndex = originalSkeleton.ReferenceSkeleton.FinalRefBoneInfo[i].ParentIndex,
                    Position    = originalSkeleton.ReferenceSkeleton.FinalRefBonePose[i].Translation,
                    Orientation = originalSkeleton.ReferenceSkeleton.FinalRefBonePose[i].Rotation,
                };

                if (i >= 1) // fix skeleton; all bones but 0
                {
                    skeletalMeshBone.Orientation.Conjugate();
                }

                bones.Add(skeletalMeshBone);
            }
            return(true);
        }
Beispiel #3
0
        public MeshExporter(USkeleton originalSkeleton)
        {
            MeshLods = new List <Mesh>();
            MeshName = originalSkeleton.Owner?.Name ?? originalSkeleton.Name;

            if (!originalSkeleton.TryConvert(out var bones) || bones.Count == 0)
            {
                Log.Logger.Warning($"Skeleton '{MeshName}' has no bone");
                return;
            }

            using var Ar = new FArchiveWriter();

            var mainHdr = new VChunkHeader {
                TypeFlag = Constants.PSK_VERSION
            };

            Ar.SerializeChunkHeader(mainHdr, "ACTRHEAD");
            ExportSkeletonData(Ar, bones);

            MeshLods.Add(new Mesh($"{MeshName}.psk", Ar.GetBuffer(), new List <MaterialExporter>()));
        }
Beispiel #4
0
        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 })