diff --git a/CUE4Parse-Conversion/Animations/AnimConverter.cs b/CUE4Parse-Conversion/Animations/AnimConverter.cs index c283afc1f..156883dc4 100644 --- a/CUE4Parse-Conversion/Animations/AnimConverter.cs +++ b/CUE4Parse-Conversion/Animations/AnimConverter.cs @@ -522,9 +522,18 @@ private static void ReadKeyLerpData(FArchive reader, UAnimSequence animSequence, var transKeys = compressedData.CompressedTrackOffsets[trackIndex * 4 + 1]; var rotOffset = compressedData.CompressedTrackOffsets[trackIndex * 4 + 2]; var rotKeys = compressedData.CompressedTrackOffsets[trackIndex * 4 + 3]; + var scaleOffset = 0; + var scaleKeys = 0; + + if (compressedData.CompressedScaleOffsets.IsValid()) + { + scaleOffset = compressedData.CompressedScaleOffsets.OffsetData[trackIndex * 2]; + scaleKeys = compressedData.CompressedScaleOffsets.OffsetData[trackIndex * 2 + 1]; + } track.KeyPos = new FVector[transKeys]; track.KeyQuat = new FQuat[rotKeys]; + track.KeyScale = new FVector[scaleKeys]; var mins = FVector.ZeroVector; var ranges = FVector.ZeroVector; @@ -567,6 +576,44 @@ private static void ReadKeyLerpData(FArchive reader, UAnimSequence animSequence, // appNotify("No translation keys!"); } + // read translation keys + if (scaleKeys > 0 && scaleOffset > 0) + { + reader.Position = scaleOffset; + var scaleCompressionFormat = compressedData.ScaleCompressionFormat; + if (scaleKeys == 1) + scaleCompressionFormat = ACF_None; // single key is stored without compression + // read mins/ranges + if (scaleCompressionFormat == ACF_IntervalFixed32NoW) + { + mins = reader.Read(); + ranges = reader.Read(); + } + + for (var keyIndex = 0; keyIndex < scaleKeys; keyIndex++) + { + track.KeyScale[keyIndex] = scaleCompressionFormat switch + { + ACF_None => reader.Read(), + ACF_Float96NoW => reader.Read(), + ACF_IntervalFixed32NoW => reader.ReadVectorIntervalFixed32(mins, ranges), + ACF_Fixed48NoW => reader.ReadVectorFixed48(), + ACF_Identity => FVector.ZeroVector, + _ => throw new ParserException($"Unknown scale compression method: {(int) scaleCompressionFormat} ({scaleCompressionFormat})") + }; + } + + // align to 4 bytes + reader.Position = reader.Position.Align(4); + if (hasTimeTracks) + ReadTimeArray(reader, scaleKeys, out track.KeyScaleTime, animSequence.NumFrames); + } + else + { + // A.KeyScale.Add(FVector.ZeroVector); + // appNotify("No scale keys!"); + } + // read rotation keys reader.Position = rotOffset; var rotationCompressionFormat = compressedData.RotationCompressionFormat; diff --git a/CUE4Parse-Conversion/Animations/UEFormat/UEAnim.cs b/CUE4Parse-Conversion/Animations/UEFormat/UEAnim.cs index e7a81b019..620149738 100644 --- a/CUE4Parse-Conversion/Animations/UEFormat/UEAnim.cs +++ b/CUE4Parse-Conversion/Animations/UEFormat/UEAnim.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using System.Linq; using CUE4Parse_Conversion.Animations.PSA; using CUE4Parse_Conversion.UEFormat; using CUE4Parse_Conversion.UEFormat.Structs; +using CUE4Parse.UE4.Assets.Exports.Animation; using CUE4Parse.UE4.Objects.Core.Math; namespace CUE4Parse_Conversion.Animations.UEFormat; @@ -9,7 +11,7 @@ namespace CUE4Parse_Conversion.Animations.UEFormat; public class UEAnim : UEFormatExport { protected override string Identifier { get; set; } = "UEANIM"; - + public UEAnim(string name, CAnimSet animSet, int sequenceIndex, ExporterOptions options) : base(name, options) { var sequence = animSet.Sequences[sequenceIndex]; @@ -24,10 +26,10 @@ public UEAnim(string name, CAnimSet animSet, int sequenceIndex, ExporterOptions { var boneName = refSkeleton.FinalRefBoneInfo[i].Name.Text; trackChunk.WriteFString(boneName); - + var track = sequence.Tracks[i]; var boneTransform = refSkeleton.FinalRefBonePose[i]; - + var positionKeys = new List(); var rotationKeys = new List(); var scaleKeys = new List(); @@ -43,36 +45,48 @@ public UEAnim(string name, CAnimSet animSet, int sequenceIndex, ExporterOptions { track.GetBoneTransform(frame, sequence.NumFrames, ref rotation, ref translation, ref scale); } - + rotation.Y = -rotation.Y; rotation.W = -rotation.W; translation.Y = -translation.Y; // dupe key reduction, could be better but it works for now - if (prevPos is null || prevPos != translation) + if (prevPos is null || (prevPos != translation && track.KeyPosTime.Contains(frame))) { + if (originalSequence.BoneCompressionCodec?.GetType() == typeof(UAnimCompress_Constant) && prevPos != null) + { + positionKeys.Add(new FVectorKey(frame - 1, (FVector)prevPos)); + } positionKeys.Add(new FVectorKey(frame, translation)); prevPos = translation; } - - if (prevRot is null || prevRot != rotation) + + if (prevRot is null || (prevRot != rotation && track.KeyQuatTime.Contains(frame))) { + if (originalSequence.BoneCompressionCodec?.GetType() == typeof(UAnimCompress_Constant) && prevRot != null) + { + rotationKeys.Add(new FQuatKey(frame - 1, (FQuat)prevRot)); + } rotationKeys.Add(new FQuatKey(frame, rotation)); prevRot = rotation; } - - if (prevScale is null || prevScale != scale) + + if (prevScale is null || (prevScale != scale && track.KeyScaleTime.Contains(frame))) { + if (originalSequence.BoneCompressionCodec?.GetType() == typeof(UAnimCompress_Constant) && prevScale != null) + { + scaleKeys.Add(new FVectorKey(frame - 1, (FVector)prevScale)); + } scaleKeys.Add(new FVectorKey(frame, scale)); prevScale = scale; } } - + trackChunk.WriteArray(positionKeys); trackChunk.WriteArray(rotationKeys); trackChunk.WriteArray(scaleKeys); } - + trackChunk.Serialize(Ar); } @@ -80,7 +94,7 @@ public UEAnim(string name, CAnimSet animSet, int sequenceIndex, ExporterOptions if (floatCurves is not null) { using var curveChunk = new FDataChunk("CURVES", floatCurves.Length); - + foreach (var floatCurve in floatCurves) { // TODO serialize more data for better accuracy @@ -92,9 +106,9 @@ public UEAnim(string name, CAnimSet animSet, int sequenceIndex, ExporterOptions key.Serialize(curveChunk); } } - + curveChunk.Serialize(Ar); } - + } -} \ No newline at end of file +} diff --git a/CUE4Parse/FileProvider/AbstractFileProvider.cs b/CUE4Parse/FileProvider/AbstractFileProvider.cs index 15747292c..40444cf71 100644 --- a/CUE4Parse/FileProvider/AbstractFileProvider.cs +++ b/CUE4Parse/FileProvider/AbstractFileProvider.cs @@ -50,7 +50,7 @@ public abstract class AbstractFileProvider : IFileProvider public abstract IReadOnlyDictionary FilesById { get; } public virtual bool IsCaseInsensitive { get; } // fabian? is this reversed? public bool ReadScriptData { get; set; } - public bool ReadShaderMaps { get; set; } + public bool ReadShaderMaps { get; set; } = true; public bool SkipReferencedTextures { get; set; } public bool UseLazySerialization { get; set; } = true; diff --git a/CUE4Parse/UE4/Assets/Exports/Animation/AnimCompress_Constant.cs b/CUE4Parse/UE4/Assets/Exports/Animation/AnimCompress_Constant.cs new file mode 100644 index 000000000..3ffc42418 --- /dev/null +++ b/CUE4Parse/UE4/Assets/Exports/Animation/AnimCompress_Constant.cs @@ -0,0 +1,6 @@ +namespace CUE4Parse.UE4.Assets.Exports.Animation; + +public class UAnimCompress_Constant : UAnimCompress +{ + +}