From c9a98d7163c269c071d3607c44a275811282114a Mon Sep 17 00:00:00 2001 From: Perfare Date: Sat, 8 Sep 2018 10:01:44 +0800 Subject: [PATCH] compressed AnimationClip supported --- AssetStudio/Classes/AnimationClip.cs | 152 ++++++++++++++++++-- AssetStudio/StudioClasses/ModelConverter.cs | 70 ++++++--- AssetStudioUtility/Imported.cs | 9 +- 3 files changed, 195 insertions(+), 36 deletions(-) diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index e9f2ad4..a74c175 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -71,7 +71,7 @@ namespace AssetStudio } } - public class PackedBitVector + public class PackedFloatVector { public uint m_NumItems { get; set; } public float m_Range { get; set; } @@ -79,7 +79,7 @@ namespace AssetStudio public byte[] m_Data { get; set; } public byte m_BitSize { get; set; } - public PackedBitVector(EndianBinaryReader reader) + public PackedFloatVector(EndianBinaryReader reader) { m_NumItems = reader.ReadUInt32(); m_Range = reader.ReadSingle(); @@ -92,15 +92,53 @@ namespace AssetStudio m_BitSize = reader.ReadByte(); reader.AlignStream(4); } + + public float[] UnpackFloats(int itemCountInChunk, int chunkStride, int start = 0, int numChunks = -1) + { + int bitPos = m_BitSize * start; + int indexPos = bitPos / 8; + bitPos %= 8; + + float scale = 1.0f / m_Range; + if (numChunks == -1) + numChunks = (int)m_NumItems / itemCountInChunk; + var end = chunkStride * numChunks / 4; + var data = new float[end]; + for (var index = 0; index != end; index += chunkStride / 4) + { + for (int i = 0; i < itemCountInChunk; ++i) + { + uint x = 0; + + int bits = 0; + while (bits < m_BitSize) + { + x |= (uint)((m_Data[indexPos] >> bitPos) << bits); + int num = Math.Min(m_BitSize - bits, 8 - bitPos); + bitPos += num; + bits += num; + if (bitPos == 8) + { + indexPos++; + bitPos = 0; + } + } + x &= (uint)(1 << m_BitSize) - 1u; + data[index + i] = x / (scale * ((1 << m_BitSize) - 1)) + m_Start; + } + } + + return data; + } } - public class PackedBitVector2 + public class PackedIntVector { public uint m_NumItems { get; set; } public byte[] m_Data { get; set; } public byte m_BitSize { get; set; } - public PackedBitVector2(EndianBinaryReader reader) + public PackedIntVector(EndianBinaryReader reader) { m_NumItems = reader.ReadUInt32(); @@ -111,14 +149,40 @@ namespace AssetStudio m_BitSize = reader.ReadByte(); reader.AlignStream(4); } + + public int[] UnpackInts() + { + var data = new int[m_NumItems]; + int indexPos = 0; + int bitPos = 0; + for (int i = 0; i < m_NumItems; i++) + { + int bits = 0; + data[i] = 0; + while (bits < m_BitSize) + { + data[i] |= (m_Data[indexPos] >> bitPos) << bits; + int num = Math.Min(m_BitSize - bits, 8 - bitPos); + bitPos += num; + bits += num; + if (bitPos == 8) + { + indexPos++; + bitPos = 0; + } + } + data[i] &= (1 << m_BitSize) - 1; + } + return data; + } } - public class PackedBitVector3 + public class PackedQuatVector { public uint m_NumItems { get; set; } public byte[] m_Data { get; set; } - public PackedBitVector3(EndianBinaryReader reader) + public PackedQuatVector(EndianBinaryReader reader) { m_NumItems = reader.ReadUInt32(); @@ -127,23 +191,87 @@ namespace AssetStudio reader.AlignStream(4); } + + public Quaternion[] UnpackQuats() + { + var data = new Quaternion[m_NumItems]; + int indexPos = 0; + int bitPos = 0; + + for (int i = 0; i < m_NumItems; i++) + { + uint flags = 0; + + int bits = 0; + while (bits < 3) + { + flags |= (uint)((m_Data[indexPos] >> bitPos) << bits); + int num = Math.Min(3 - bits, 8 - bitPos); + bitPos += num; + bits += num; + if (bitPos == 8) + { + indexPos++; + bitPos = 0; + } + } + flags &= 7; + + + var q = new Quaternion(); + float sum = 0; + for (int j = 0; j < 4; j++) + { + if ((flags & 3) != j) + { + int bitSize = ((flags & 3) + 1) % 4 == j ? 9 : 10; + uint x = 0; + + bits = 0; + while (bits < bitSize) + { + x |= (uint)((m_Data[indexPos] >> bitPos) << bits); + int num = Math.Min(bitSize - bits, 8 - bitPos); + bitPos += num; + bits += num; + if (bitPos == 8) + { + indexPos++; + bitPos = 0; + } + } + x &= (uint)((1 << bitSize) - 1); + q[j] = x / (0.5f * ((1 << bitSize) - 1)) - 1; + sum += q[j] * q[j]; + } + } + + int lastComponent = (int)(flags & 3); + q[lastComponent] = (float)Math.Sqrt(1 - sum); + if ((flags & 4) != 0u) + q[lastComponent] = -q[lastComponent]; + data[i] = q; + } + + return data; + } } public class CompressedAnimationCurve { public string m_Path { get; set; } - public PackedBitVector2 m_Times { get; set; } - public PackedBitVector3 m_Values { get; set; } - public PackedBitVector m_Slopes { get; set; } + public PackedIntVector m_Times { get; set; } + public PackedQuatVector m_Values { get; set; } + public PackedFloatVector m_Slopes { get; set; } public int m_PreInfinity { get; set; } public int m_PostInfinity { get; set; } public CompressedAnimationCurve(EndianBinaryReader reader) { m_Path = reader.ReadAlignedString(); - m_Times = new PackedBitVector2(reader); - m_Values = new PackedBitVector3(reader); - m_Slopes = new PackedBitVector(reader); + m_Times = new PackedIntVector(reader); + m_Values = new PackedQuatVector(reader); + m_Slopes = new PackedFloatVector(reader); m_PreInfinity = reader.ReadInt32(); m_PostInfinity = reader.ReadInt32(); } diff --git a/AssetStudio/StudioClasses/ModelConverter.cs b/AssetStudio/StudioClasses/ModelConverter.cs index 0f9f26a..5ab061a 100644 --- a/AssetStudio/StudioClasses/ModelConverter.cs +++ b/AssetStudio/StudioClasses/ModelConverter.cs @@ -658,17 +658,35 @@ namespace AssetStudio iAnim.TrackList = new List(); if (animationClip.m_Legacy) { + foreach (var m_CompressedRotationCurve in animationClip.m_CompressedRotationCurves) + { + var path = m_CompressedRotationCurve.m_Path; + var boneName = path.Substring(path.LastIndexOf('/') + 1); + var track = iAnim.FindTrack(boneName); + + var numKeys = m_CompressedRotationCurve.m_Times.m_NumItems; + var data = m_CompressedRotationCurve.m_Times.UnpackInts(); + var times = new float[numKeys]; + int t = 0; + for (int i = 0; i < numKeys; i++) + { + t += data[i]; + times[i] = t * 0.01f; + } + var quats = m_CompressedRotationCurve.m_Values.UnpackQuats(); + + for (int i = 0; i < numKeys; i++) + { + var quat = quats[i]; + var value = Fbx.QuaternionToEuler(new Quaternion(quat.X, -quat.Y, -quat.Z, quat.W)); + track.Rotations.Add(new ImportedKeyframe(times[i], value)); + } + } foreach (var m_RotationCurve in animationClip.m_RotationCurves) { var path = m_RotationCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); - if (track == null) - { - track = new ImportedAnimationKeyframedTrack(); - track.Name = boneName; - iAnim.TrackList.Add(track); - } foreach (var m_Curve in m_RotationCurve.curve.m_Curve) { var value = Fbx.QuaternionToEuler(new Quaternion(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z, m_Curve.value.W)); @@ -680,12 +698,6 @@ namespace AssetStudio var path = m_PositionCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); - if (track == null) - { - track = new ImportedAnimationKeyframedTrack(); - track.Name = boneName; - iAnim.TrackList.Add(track); - } foreach (var m_Curve in m_PositionCurve.curve.m_Curve) { track.Translations.Add(new ImportedKeyframe(m_Curve.time, new Vector3(-m_Curve.value.X, m_Curve.value.Y, m_Curve.value.Z))); @@ -696,17 +708,34 @@ namespace AssetStudio var path = m_ScaleCurve.path; var boneName = path.Substring(path.LastIndexOf('/') + 1); var track = iAnim.FindTrack(boneName); - if (track == null) - { - track = new ImportedAnimationKeyframedTrack(); - track.Name = boneName; - iAnim.TrackList.Add(track); - } foreach (var m_Curve in m_ScaleCurve.curve.m_Curve) { track.Scalings.Add(new ImportedKeyframe(m_Curve.time, new Vector3(m_Curve.value.X, m_Curve.value.Y, m_Curve.value.Z))); } } + if (animationClip.m_EulerCurves != null) + { + foreach (var m_EulerCurve in animationClip.m_EulerCurves) + { + var path = m_EulerCurve.path; + var boneName = path.Substring(path.LastIndexOf('/') + 1); + var track = iAnim.FindTrack(boneName); + foreach (var m_Curve in m_EulerCurve.curve.m_Curve) + { + track.Rotations.Add(new ImportedKeyframe(m_Curve.time, new Vector3(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z))); + } + } + } + foreach (var m_FloatCurve in animationClip.m_FloatCurves) + { + var path = m_FloatCurve.path; + var boneName = path.Substring(path.LastIndexOf('/') + 1); + var track = iAnim.FindTrack(boneName); + foreach (var m_Curve in m_FloatCurve.curve.m_Curve) + { + track.Curve.Add(new ImportedKeyframe(m_Curve.time, m_Curve.value)); + } + } } else { @@ -775,11 +804,6 @@ namespace AssetStudio } var boneName = GetNameFromHashes(binding.path, binding.attribute); var track = iAnim.FindTrack(boneName); - if (track == null) - { - track = new ImportedAnimationKeyframedTrack { Name = boneName }; - iAnim.TrackList.Add(track); - } switch (binding.attribute) { diff --git a/AssetStudioUtility/Imported.cs b/AssetStudioUtility/Imported.cs index a56cbe3..bb5622a 100644 --- a/AssetStudioUtility/Imported.cs +++ b/AssetStudioUtility/Imported.cs @@ -148,7 +148,14 @@ namespace AssetStudio public ImportedAnimationKeyframedTrack FindTrack(string name) { - return TrackList.Find(track => track.Name == name); + var track = TrackList.Find(x => x.Name == name); + if (track == null) + { + track = new ImportedAnimationKeyframedTrack { Name = name }; + TrackList.Add(track); + } + + return track; } }