Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arc System Works game animation support, support for scale keyframes pre-ACL #188

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions CUE4Parse-Conversion/Animations/AnimConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<FVector>();
ranges = reader.Read<FVector>();
}

for (var keyIndex = 0; keyIndex < scaleKeys; keyIndex++)
{
track.KeyScale[keyIndex] = scaleCompressionFormat switch
{
ACF_None => reader.Read<FVector>(),
ACF_Float96NoW => reader.Read<FVector>(),
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;
Expand Down
44 changes: 29 additions & 15 deletions CUE4Parse-Conversion/Animations/UEFormat/UEAnim.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
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;

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];
Expand All @@ -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<FVectorKey>();
var rotationKeys = new List<FQuatKey>();
var scaleKeys = new List<FVectorKey>();
Expand All @@ -43,44 +45,56 @@ 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);
}

var floatCurves = originalSequence.CompressedCurveData.FloatCurves;
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
Expand All @@ -92,9 +106,9 @@ public UEAnim(string name, CAnimSet animSet, int sequenceIndex, ExporterOptions
key.Serialize(curveChunk);
}
}

curveChunk.Serialize(Ar);
}

}
}
}
2 changes: 1 addition & 1 deletion CUE4Parse/FileProvider/AbstractFileProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public abstract class AbstractFileProvider : IFileProvider
public abstract IReadOnlyDictionary<FPackageId, GameFile> 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;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace CUE4Parse.UE4.Assets.Exports.Animation;

public class UAnimCompress_Constant : UAnimCompress
{

}