diff --git a/FinModelUtility/Fin/Fin Tests/data/KeyframesTests.cs b/FinModelUtility/Fin/Fin Tests/data/KeyframesTests.cs index 75d320493..74c647b0a 100644 --- a/FinModelUtility/Fin/Fin Tests/data/KeyframesTests.cs +++ b/FinModelUtility/Fin/Fin Tests/data/KeyframesTests.cs @@ -8,11 +8,20 @@ public class KeyframesTests { public void TestAddToEnd() { var impl = new Keyframes(); - impl.SetKeyframe(0, "0"); - impl.SetKeyframe(1, "1"); - impl.SetKeyframe(2, "2"); - impl.SetKeyframe(3, "3"); - impl.SetKeyframe(4, "4"); + impl.SetKeyframe(0, "0", out var performedBinarySearch0); + Assert.False(performedBinarySearch0); + + impl.SetKeyframe(1, "1", out var performedBinarySearch1); + Assert.False(performedBinarySearch1); + + impl.SetKeyframe(2, "2", out var performedBinarySearch2); + Assert.False(performedBinarySearch2); + + impl.SetKeyframe(3, "3", out var performedBinarySearch3); + Assert.False(performedBinarySearch3); + + impl.SetKeyframe(4, "4", out var performedBinarySearch4); + Assert.False(performedBinarySearch4); AssertKeyframes_(impl, new Keyframe(0, "0"), @@ -27,9 +36,14 @@ public void TestAddToEnd() { public void TestReplace() { var impl = new Keyframes(); - impl.SetKeyframe(1, "first"); - impl.SetKeyframe(1, "second"); - impl.SetKeyframe(1, "third"); + impl.SetKeyframe(1, "first", out var performedBinarySearch1); + Assert.False(performedBinarySearch1); + + impl.SetKeyframe(1, "second", out var performedBinarySearch2); + Assert.False(performedBinarySearch2); + + impl.SetKeyframe(1, "third", out var performedBinarySearch3); + Assert.False(performedBinarySearch3); AssertKeyframes_(impl, new Keyframe(1, "third")); } @@ -38,11 +52,20 @@ public void TestReplace() { public void TestInsertAtFront() { var impl = new Keyframes(); - impl.SetKeyframe(4, "4"); - impl.SetKeyframe(5, "5"); - impl.SetKeyframe(2, "2"); - impl.SetKeyframe(1, "1"); - impl.SetKeyframe(0, "0"); + impl.SetKeyframe(4, "4", out var performedBinarySearch4); + Assert.False(performedBinarySearch4); + + impl.SetKeyframe(5, "5", out var performedBinarySearch5); + Assert.False(performedBinarySearch5); + + impl.SetKeyframe(2, "2", out var performedBinarySearch2); + Assert.True(performedBinarySearch2); + + impl.SetKeyframe(1, "1", out var performedBinarySearch1); + Assert.True(performedBinarySearch1); + + impl.SetKeyframe(0, "0", out var performedBinarySearch0); + Assert.True(performedBinarySearch0); AssertKeyframes_(impl, new Keyframe(0, "0"), diff --git a/FinModelUtility/Fin/Fin/src/data/Keyframes.cs b/FinModelUtility/Fin/Fin/src/data/Keyframes.cs index 01edc5f73..f214332f7 100644 --- a/FinModelUtility/Fin/Fin/src/data/Keyframes.cs +++ b/FinModelUtility/Fin/Fin/src/data/Keyframes.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace fin.data { @@ -14,18 +15,16 @@ public Keyframe(int frame, T value) { [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Keyframe other) - => this.Frame.CompareTo(other.Frame); + => this.Frame - other.Frame; public override string ToString() => $"{{ Frame: {this.Frame}, Value: {this.Value} }}"; } - public interface IKeyframes { - IReadOnlyList> Definitions { get; } - + public interface IReadOnlyKeyframes { bool IsDefined { get; } - void SetKeyframe(int frame, T value); + IReadOnlyList> Definitions { get; } Keyframe GetKeyframeAtIndex(int index); Keyframe? GetKeyframeAtFrame(int frame); @@ -37,8 +36,13 @@ bool FindIndexOfKeyframe( out bool isLastKeyframe); } + public interface IKeyframes : IReadOnlyKeyframes { + void SetKeyframe(int frame, T value); + void SetAllKeyframes(IEnumerable value); + } + public class Keyframes : IKeyframes { - private readonly List> impl_; + private List> impl_; public Keyframes(int initialCapacity = 0) { this.impl_ = new(initialCapacity); @@ -48,19 +52,27 @@ public Keyframes(int initialCapacity = 0) { public bool IsDefined { get; set; } - public void SetKeyframe(int frame, T value) { + public void SetKeyframe(int frame, T value) + => SetKeyframe(frame, value, out _); + + public void SetKeyframe(int frame, + T value, + out bool performedBinarySearch) { this.IsDefined = true; var keyframeExists = this.FindIndexOfKeyframe(frame, out var keyframeIndex, out var existingKeyframe, - out var isLastKeyframe); + out var isLastKeyframe, + out performedBinarySearch); var newKeyframe = new Keyframe(frame, value); if (keyframeExists && existingKeyframe.Frame == frame) { + this.lastAccessedKeyframeIndex_ = keyframeIndex; this.impl_[keyframeIndex] = newKeyframe; } else if (isLastKeyframe) { + this.lastAccessedKeyframeIndex_ = this.impl_.Count; this.impl_.Add(newKeyframe); } else if (keyframeExists && existingKeyframe.Frame < frame) { this.impl_.Insert(keyframeIndex + 1, newKeyframe); @@ -69,6 +81,14 @@ public void SetKeyframe(int frame, T value) { } } + public void SetAllKeyframes(IEnumerable values) { + this.impl_ = values + .Select((value, frame) => new Keyframe(frame, value)) + .ToList(); + this.IsDefined = this.impl_.Count > 0; + this.lastAccessedKeyframeIndex_ = this.impl_.Count - 1; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Keyframe GetKeyframeAtIndex(int index) => this.impl_[index]; @@ -82,18 +102,40 @@ public void SetKeyframe(int frame, T value) { } - private int lastAccessedIndex_ = -1; + private int lastAccessedKeyframeIndex_ = -1; public bool FindIndexOfKeyframe( int frame, out int keyframeIndex, out Keyframe keyframe, - out bool isLastKeyframe) { - // Try to optimize the case where the next frame is being accessed. + out bool isLastKeyframe) + => this.FindIndexOfKeyframe(frame, + out keyframeIndex, + out keyframe, + out isLastKeyframe, + out _); + + public bool FindIndexOfKeyframe( + int frame, + out int keyframeIndex, + out Keyframe keyframe, + out bool isLastKeyframe, + out bool performedBinarySearch) { + performedBinarySearch = false; + + // Try to optimize the case where no frames have been processed yet. var keyframeCount = this.impl_.Count; - if (this.lastAccessedIndex_ >= 0 && - this.lastAccessedIndex_ < keyframeCount) { - keyframeIndex = this.lastAccessedIndex_; + if (this.lastAccessedKeyframeIndex_ == -1 || keyframeCount == 0) { + this.lastAccessedKeyframeIndex_ = keyframeIndex = 0; + keyframe = default; + isLastKeyframe = false; + return false; + } + + // Try to optimize the case where the next frame is being accessed. + if (this.lastAccessedKeyframeIndex_ >= 0 && + this.lastAccessedKeyframeIndex_ < keyframeCount) { + keyframeIndex = this.lastAccessedKeyframeIndex_; keyframe = this.impl_[keyframeIndex]; if (frame >= keyframe.Frame) { @@ -103,11 +145,11 @@ public bool FindIndexOfKeyframe( return true; } - var nextKeyframe = this.impl_[this.lastAccessedIndex_ + 1]; + var nextKeyframe = this.impl_[this.lastAccessedKeyframeIndex_ + 1]; if (nextKeyframe.Frame > frame) { return true; } else if (nextKeyframe.Frame == frame) { - this.lastAccessedIndex_ = ++keyframeIndex; + this.lastAccessedKeyframeIndex_ = ++keyframeIndex; keyframe = nextKeyframe; isLastKeyframe = keyframeIndex == keyframeCount - 1; return true; @@ -117,8 +159,10 @@ public bool FindIndexOfKeyframe( // Perform a binary search for the current frame. var result = this.impl_.BinarySearch(new Keyframe(frame, default!)); + performedBinarySearch = true; + if (result >= 0) { - this.lastAccessedIndex_ = keyframeIndex = result; + this.lastAccessedKeyframeIndex_ = keyframeIndex = result; keyframe = this.impl_[keyframeIndex]; isLastKeyframe = keyframeIndex == keyframeCount - 1; return true; @@ -126,7 +170,7 @@ public bool FindIndexOfKeyframe( var i = ~result; if (i == keyframeCount) { - this.lastAccessedIndex_ = keyframeIndex = keyframeCount - 1; + this.lastAccessedKeyframeIndex_ = keyframeIndex = keyframeCount - 1; isLastKeyframe = true; if (keyframeCount > 0) { keyframe = this.impl_[keyframeIndex]; @@ -137,7 +181,7 @@ public bool FindIndexOfKeyframe( return false; } - this.lastAccessedIndex_ = keyframeIndex = Math.Max(0, i - 1); + this.lastAccessedKeyframeIndex_ = keyframeIndex = Math.Max(0, i - 1); keyframe = this.impl_[keyframeIndex]; var keyframeExists = keyframe.Frame <= frame; if (!keyframeExists) { diff --git a/FinModelUtility/Fin/Fin/src/model/AnimationTrackInterfaces.cs b/FinModelUtility/Fin/Fin/src/model/AnimationTrackInterfaces.cs index ac8dd48cb..4a681b562 100644 --- a/FinModelUtility/Fin/Fin/src/model/AnimationTrackInterfaces.cs +++ b/FinModelUtility/Fin/Fin/src/model/AnimationTrackInterfaces.cs @@ -37,19 +37,21 @@ public interface IImplTrack : ITrack { IReadOnlyList>> Keyframes { get; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - void Set(int frame, TValue value) - => this.Set(frame, value, null); + void SetKeyframe(int frame, TValue value) + => this.SetKeyframe(frame, value, null); [MethodImpl(MethodImplOptions.AggressiveInlining)] - void Set(int frame, TValue value, float? tangent) - => this.Set(frame, value, tangent, tangent); + void SetKeyframe(int frame, TValue value, float? tangent) + => this.SetKeyframe(frame, value, tangent, tangent); - void Set( + void SetKeyframe( int frame, TValue value, float? incomingTangent, float? outgoingTangent); + void SetAllKeyframes(IEnumerable value); + bool TryGetInterpolationData( float frame, out (float frame, TValue value, float? tangent)? fromData, diff --git a/FinModelUtility/Fin/Fin/src/model/impl/AnimationImpl.cs b/FinModelUtility/Fin/Fin/src/model/impl/AnimationImpl.cs index 9ac0c1dd5..d6d1d1644 100644 --- a/FinModelUtility/Fin/Fin/src/model/impl/AnimationImpl.cs +++ b/FinModelUtility/Fin/Fin/src/model/impl/AnimationImpl.cs @@ -1,23 +1,26 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using fin.data.indexable; using fin.math.interpolation; namespace fin.model.impl { public partial class ModelImpl { - public IAnimationManager AnimationManager { get; } = - new AnimationManagerImpl(); + public IAnimationManager AnimationManager { get; } private class AnimationManagerImpl : IAnimationManager { + private readonly IModel model_; + private readonly IList animations_ = new List(); private readonly IList morphTargets_ = new List(); - public AnimationManagerImpl() { + public AnimationManagerImpl(IModel model) { + this.model_ = model; this.Animations = new ReadOnlyCollection(this.animations_); this.MorphTargets = @@ -28,20 +31,18 @@ public AnimationManagerImpl() { public IReadOnlyList Animations { get; } public IModelAnimation AddAnimation() { - var animation = new ModelAnimationImpl(); + var animation = new ModelAnimationImpl(this.model_.Skeleton.Count()); this.animations_.Add(animation); return animation; } private class ModelAnimationImpl : IModelAnimation { - private readonly IndexableDictionary boneTracks_ = - new(); - + private readonly IndexableDictionary boneTracks_; private readonly Dictionary meshTracks_ = new(); - public ModelAnimationImpl() { - this.BoneTracks = this.boneTracks_; - this.MeshTracks = this.meshTracks_; + public ModelAnimationImpl(int boneCount) { + this.boneTracks_ = + new IndexableDictionary(boneCount); } public string Name { get; set; } @@ -50,14 +51,14 @@ public ModelAnimationImpl() { public float FrameRate { get; set; } - public IReadOnlyIndexableDictionary BoneTracks { - get; - } + public IReadOnlyIndexableDictionary BoneTracks + => this.boneTracks_; public IBoneTracks AddBoneTracks(IBone bone) => this.boneTracks_[bone] = new BoneTracksImpl(this, bone); - public IReadOnlyDictionary MeshTracks { get; } + public IReadOnlyDictionary MeshTracks + => this.meshTracks_; public IMeshTracks AddMeshTracks(IMesh mesh) => this.meshTracks_[mesh] = new MeshTracksImpl(this); diff --git a/FinModelUtility/Fin/Fin/src/model/impl/BoneWeightsSet.cs b/FinModelUtility/Fin/Fin/src/model/impl/BoneWeightsSet.cs index 0f156b259..53b54d30a 100644 --- a/FinModelUtility/Fin/Fin/src/model/impl/BoneWeightsSet.cs +++ b/FinModelUtility/Fin/Fin/src/model/impl/BoneWeightsSet.cs @@ -18,13 +18,7 @@ public bool TryGetExisting( } public static int GetHashCode(VertexSpace vertexSpace, - IReadOnlyList weights) { - var hash = FluentHash.Start().With(vertexSpace); - foreach (var weight in weights) { - hash = hash.With(weight); - } - - return hash; - } + IEnumerable weights) + => FluentHash.Start().With(vertexSpace).With(weights); } } \ No newline at end of file diff --git a/FinModelUtility/Fin/Fin/src/model/impl/ModelImpl.cs b/FinModelUtility/Fin/Fin/src/model/impl/ModelImpl.cs index 65a56cdb2..b2729b0da 100644 --- a/FinModelUtility/Fin/Fin/src/model/impl/ModelImpl.cs +++ b/FinModelUtility/Fin/Fin/src/model/impl/ModelImpl.cs @@ -6,12 +6,14 @@ public partial class ModelImpl : IModel> where TVertex : IReadOnlyVertex { public ModelImpl(Func vertexCreator) { this.Skin = new SkinImpl(vertexCreator); + this.AnimationManager = new AnimationManagerImpl(this); } // TODO: Rewrite this to take in options instead. public ModelImpl(int vertexCount, Func vertexCreator) { this.Skin = new SkinImpl(vertexCount, vertexCreator); + this.AnimationManager = new AnimationManagerImpl(this); } } diff --git a/FinModelUtility/Fin/Fin/src/model/impl/animation/RadiansRotationTrack3dImpl.cs b/FinModelUtility/Fin/Fin/src/model/impl/animation/RadiansRotationTrack3dImpl.cs index df001bcbd..8a9d489dd 100644 --- a/FinModelUtility/Fin/Fin/src/model/impl/animation/RadiansRotationTrack3dImpl.cs +++ b/FinModelUtility/Fin/Fin/src/model/impl/animation/RadiansRotationTrack3dImpl.cs @@ -45,7 +45,7 @@ public void Set( float? optionalIncomingTangent, float? optionalOutgoingTangent) => this.axisTracks_[axis] - .Set(frame, + .SetKeyframe(frame, radians, optionalIncomingTangent, optionalOutgoingTangent); diff --git a/FinModelUtility/Fin/Fin/src/model/impl/animation/ScalarAxesTrack.cs b/FinModelUtility/Fin/Fin/src/model/impl/animation/ScalarAxesTrack.cs index 99d6b9f67..2aaf85537 100644 --- a/FinModelUtility/Fin/Fin/src/model/impl/animation/ScalarAxesTrack.cs +++ b/FinModelUtility/Fin/Fin/src/model/impl/animation/ScalarAxesTrack.cs @@ -54,7 +54,7 @@ public void Set( float? optionalIncomingTangent, float? optionalOutgoingTangent) => this.axisTracks[axis] - .Set(frame, + .SetKeyframe(frame, value, optionalIncomingTangent, optionalOutgoingTangent); diff --git a/FinModelUtility/Fin/Fin/src/model/impl/animation/TrackImpl.cs b/FinModelUtility/Fin/Fin/src/model/impl/animation/TrackImpl.cs index 0ed6bf0fb..d081c9faa 100644 --- a/FinModelUtility/Fin/Fin/src/model/impl/animation/TrackImpl.cs +++ b/FinModelUtility/Fin/Fin/src/model/impl/animation/TrackImpl.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using fin.data; @@ -22,15 +23,20 @@ public IReadOnlyList>> Keyframes public bool IsDefined => this.impl.IsDefined; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set( + public void SetKeyframe( int frame, TValue t, float? optionalIncomingTangent, float? optionalOutgoingTangent) => this.impl.SetKeyframe(frame, - new ValueAndTangents(t, - optionalIncomingTangent, - optionalOutgoingTangent)); + new ValueAndTangents(t, + optionalIncomingTangent, + optionalOutgoingTangent)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetAllKeyframes(IEnumerable values) + => this.impl.SetAllKeyframes( + values.Select(t => new ValueAndTangents(t, null, null))); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Keyframe>? GetKeyframe(int frame) @@ -116,10 +122,10 @@ public void Set( this.Interpolator = other.Interpolator; foreach (var keyframe in other.Keyframes) { - this.Set(keyframe.Frame, - keyframe.Value.Value, - keyframe.Value.IncomingTangent, - keyframe.Value.OutgoingTangent); + this.SetKeyframe(keyframe.Frame, + keyframe.Value.Value, + keyframe.Value.IncomingTangent, + keyframe.Value.OutgoingTangent); } } @@ -141,7 +147,8 @@ public bool TryGetInterpolatedFrame( // TODO: Make this an option? if (isLastKeyframe && !useLoopingInterpolation) { - interpolatedValue = this.Interpolator.Interpolate(fromValue, fromValue, 0); + interpolatedValue = + this.Interpolator.Interpolate(fromValue, fromValue, 0); return true; } diff --git a/FinModelUtility/Fin/Fin/src/model/io/importers/assimp/AssimpModelImporter.cs b/FinModelUtility/Fin/Fin/src/model/io/importers/assimp/AssimpModelImporter.cs index 07af66016..60fc578a2 100644 --- a/FinModelUtility/Fin/Fin/src/model/io/importers/assimp/AssimpModelImporter.cs +++ b/FinModelUtility/Fin/Fin/src/model/io/importers/assimp/AssimpModelImporter.cs @@ -171,7 +171,7 @@ public unsafe IModel ImportModel(AssimpModelFileBundle modelFileBundle) { .PositionKeys) { var frame = (int) Math.Round(assPositionKey.Time / frameRate); var assPosition = assPositionKey.Value; - positionTrack.Set( + positionTrack.SetKeyframe( frame, new Position(assPosition.X, assPosition.Y, assPosition.Z)); } @@ -183,7 +183,7 @@ public unsafe IModel ImportModel(AssimpModelFileBundle modelFileBundle) { .RotationKeys) { var frame = (int) Math.Round(assRotationKey.Time / frameRate); var assQuaternion = assRotationKey.Value; - rotationTrack.Set(frame, + rotationTrack.SetKeyframe(frame, new System.Numerics.Quaternion( assQuaternion.X, assQuaternion.Y, diff --git a/FinModelUtility/Fin/Fin/src/util/hash/FluentHash.cs b/FinModelUtility/Fin/Fin/src/util/hash/FluentHash.cs index f9e838963..126421a08 100644 --- a/FinModelUtility/Fin/Fin/src/util/hash/FluentHash.cs +++ b/FinModelUtility/Fin/Fin/src/util/hash/FluentHash.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; namespace fin.util.hash { @@ -36,6 +37,18 @@ public FluentHash With(ReadOnlySpan other) { return this; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FluentHash With(IEnumerable others) where T : notnull { + var hash = this.Hash; + foreach (var other in others) { + hash = hash * this.primeCoefficient_ + other.GetHashCode(); + } + + this.Hash = hash; + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator int(FluentHash d) => d.Hash; diff --git a/FinModelUtility/Formats/Glo/Glo/src/api/GloModelImporter.cs b/FinModelUtility/Formats/Glo/Glo/src/api/GloModelImporter.cs index c88169a4a..0144fdf79 100644 --- a/FinModelUtility/Formats/Glo/Glo/src/api/GloModelImporter.cs +++ b/FinModelUtility/Formats/Glo/Glo/src/api/GloModelImporter.cs @@ -184,10 +184,10 @@ public IModel ImportModel(GloModelFileBundle gloModelFileBundle) { Asserts.True(time >= 0 && time < finAnimation.FrameCount); - positions.Set(time, - new Position(moveKey.Xyz.X, - moveKey.Xyz.Y, - moveKey.Xyz.Z)); + positions.SetKeyframe(time, + new Position(moveKey.Xyz.X, + moveKey.Xyz.Y, + moveKey.Xyz.Z)); if (isLast) { break; @@ -221,7 +221,7 @@ public IModel ImportModel(GloModelFileBundle gloModelFileBundle) { rotateKey.Y, rotateKey.Z, rotateKey.W); - rotations.Set(time, quaternionKey); + rotations.SetKeyframe(time, quaternionKey); if (isLast) { break; diff --git a/FinModelUtility/Formats/Level5/Level5/src/api/XcModelImporter.cs b/FinModelUtility/Formats/Level5/Level5/src/api/XcModelImporter.cs index 5f716aa58..6345a16e6 100644 --- a/FinModelUtility/Formats/Level5/Level5/src/api/XcModelImporter.cs +++ b/FinModelUtility/Formats/Level5/Level5/src/api/XcModelImporter.cs @@ -244,7 +244,7 @@ public IModel ImportModel(XcModelFileBundle modelFileBundle) { } if (displayState != null) { - displayStates.Set(frame, displayState.Value); + displayStates.SetKeyframe(frame, displayState.Value); } } } diff --git a/FinModelUtility/Formats/Modl/Modl Tests/goldens/modl/battalion_wars_1/TVET/output/TVET.glb b/FinModelUtility/Formats/Modl/Modl Tests/goldens/modl/battalion_wars_1/TVET/output/TVET.glb index fe799d1d3..435fde354 100644 Binary files a/FinModelUtility/Formats/Modl/Modl Tests/goldens/modl/battalion_wars_1/TVET/output/TVET.glb and b/FinModelUtility/Formats/Modl/Modl Tests/goldens/modl/battalion_wars_1/TVET/output/TVET.glb differ diff --git a/FinModelUtility/Formats/Modl/Modl/src/api/ModlModelReader.cs b/FinModelUtility/Formats/Modl/Modl/src/api/ModlModelReader.cs index 3669261d5..4a4f9e809 100644 --- a/FinModelUtility/Formats/Modl/Modl/src/api/ModlModelReader.cs +++ b/FinModelUtility/Formats/Modl/Modl/src/api/ModlModelReader.cs @@ -287,22 +287,26 @@ private static void AddAnimFileToModel_(IModel model, var fbtPositions = finBoneTracks.UseCombinedPositionAxesTrack( (int) animBone.PositionKeyframeCount); - for (var f = 0; f < animBone.PositionKeyframeCount; ++f) { - var (fPX, fPY, fPZ) = animBoneFrames.PositionFrames[f]; - fbtPositions.Set(f, new Position(flipSign * fPX, fPY, fPZ)); - } + fbtPositions.SetAllKeyframes( + animBoneFrames.PositionFrames.Select( + (axes) => { + var (fPX, fPY, fPZ) = axes; + return new Position(flipSign * fPX, fPY, fPZ); + })); var fbtRotations = finBoneTracks.UseQuaternionRotationTrack( (int) animBone.RotationKeyframeCount); - for (var f = 0; f < animBone.RotationKeyframeCount; ++f) { - var (fRX, fRY, fRZ, frW) = animBoneFrames.RotationFrames[f]; - - var animationQuaternion = - new Quaternion(flipSign * fRX, fRY, fRZ, flipSign * frW); - - fbtRotations.Set(f, animationQuaternion); - } + fbtRotations.SetAllKeyframes( + animBoneFrames.RotationFrames.Select( + (axes) => { + var (fRX, fRY, fRZ, frW) = axes; + return new Quaternion( + flipSign * fRX, + fRY, + fRZ, + flipSign * frW); + })); } } } diff --git a/FinModelUtility/Formats/Modl/Modl/src/schema/anim/bw1/Bw1Anim.cs b/FinModelUtility/Formats/Modl/Modl/src/schema/anim/bw1/Bw1Anim.cs index 65674044c..6906baf03 100644 --- a/FinModelUtility/Formats/Modl/Modl/src/schema/anim/bw1/Bw1Anim.cs +++ b/FinModelUtility/Formats/Modl/Modl/src/schema/anim/bw1/Bw1Anim.cs @@ -68,7 +68,6 @@ public void Read(IBinaryReader br) { } } - Span parseBuffer = stackalloc double[4]; for (var i = 0; i < boneCount; ++i) { var bone = this.AnimBones[i]; @@ -81,26 +80,39 @@ public void Read(IBinaryReader br) { (int) bone.RotationKeyframeCount); this.AnimBoneFrames.Add(animBoneFrames); + Span shorts = stackalloc ushort[3]; + for (var p = 0; p < bone.PositionKeyframeCount; ++p) { - Parse3PositionValuesFrom2UShorts_(bone, ber, parseBuffer); - animBoneFrames.PositionFrames.Add(((float) parseBuffer[0], - (float) parseBuffer[1], - (float) parseBuffer[2])); + Parse3PositionValuesFrom2UShorts_(bone, + ber, + out var outX, + out var outY, + out var outZ); + animBoneFrames.PositionFrames.Add(((float) outX, + (float) outY, + (float) outZ)); } for (var p = 0; p < bone.RotationKeyframeCount; ++p) { var flipSigns = - Parse4RotationValuesFrom3UShorts_(ber, parseBuffer); + Parse4RotationValuesFrom3UShorts_( + ber, + shorts, + out var outX, + out var outY, + out var outZ, + out var outW); if (flipSigns) { - for (var f = 0; f < parseBuffer.Length; f++) { - parseBuffer[f] *= -1; - } + outX *= -1; + outY *= -1; + outZ *= -1; + outW *= -1; } - animBoneFrames.RotationFrames.Add(((float) -parseBuffer[0], - (float) -parseBuffer[1], - (float) -parseBuffer[2], - (float) parseBuffer[3])); + animBoneFrames.RotationFrames.Add(((float) -outX, + (float) -outY, + (float) -outZ, + (float) outW)); } } } @@ -108,90 +120,63 @@ public void Read(IBinaryReader br) { public void Parse3PositionValuesFrom2UShorts_( IBwAnimBone animBone, SchemaBinaryReader br, - Span outValues) { + out double outX, + out double outY, + out double outZ) { var first_uint = br.ReadUInt32(); br.Position -= 2; var second_ushort = br.ReadUInt16(); - outValues[0] = + outX = WeirdFloatMath.CreateWeirdDoubleFromUInt32(first_uint >> 0x15) * animBone.XPosDelta + animBone.XPosMin; - outValues[1] = + outY = WeirdFloatMath.CreateWeirdDoubleFromUInt32( (first_uint >> 10) & 0x7ff) * animBone.YPosDelta + animBone.YPosMin; - outValues[2] = + outZ = WeirdFloatMath.CreateWeirdDoubleFromUInt32( (uint) (second_ushort & 0x3ff)) * animBone.ZPosDelta + animBone.ZPosMin; } public bool Parse4RotationValuesFrom3UShorts_(IBinaryReader br, - Span outValues) { - Span shorts = stackalloc ushort[3]; + Span shorts, + out double outX, + out double outY, + out double outZ, + out double outW) { br.ReadUInt16s(shorts); - + var first_ushort = shorts[0]; var second_ushort = shorts[1]; var third_ushort = shorts[2]; - var const_for_out_value_2 = WeirdFloatMath.C_3_05175_EN5; - - var out_x = - ((WeirdFloatMath.InterpretAsDouble( - WeirdFloatMath.Concat44( - 0x43300000, - (uint) (first_ushort & 0x7fff))) - - WeirdFloatMath.C_4503599627370496) - + outX = + (WeirdFloatMath.CreateWeirdDoubleFromUInt32( + (uint) (first_ushort & 0x7fff)) - WeirdFloatMath.C_16384) * WeirdFloatMath.C_6_10351_EN5; - var out_y = - ((WeirdFloatMath.InterpretAsDouble( - WeirdFloatMath.Concat44(0x43300000, - (uint) (second_ushort & 0x7fff))) - - WeirdFloatMath.C_4503599627370496) - + + outY = + (WeirdFloatMath.CreateWeirdDoubleFromUInt32( + (uint) (second_ushort & 0x7fff)) - WeirdFloatMath.C_16384) * WeirdFloatMath.C_6_10351_EN5; - var third_parsed_thing = - WeirdFloatMath.CreateWeirdDoubleFromUInt32(third_ushort); - - outValues[0] = out_x; - outValues[1] = out_y; - var out_z = - (third_parsed_thing - 32768f) * const_for_out_value_2; - outValues[2] = out_z; + outZ = + (WeirdFloatMath.CreateWeirdDoubleFromUInt32(third_ushort) - + 32768f) * + WeirdFloatMath.C_3_05175_EN5; var expected_normalized_w = - ((1 - out_x * out_x) - out_y * out_y) - out_z * out_z; - var out_w = 0d; - if (out_w <= expected_normalized_w) { - if (WeirdFloatMath.C_ZERO < expected_normalized_w) { - var inverse_sqrt_of_expected_normalized_w = - 1.0 / Math.Sqrt(expected_normalized_w); - out_w = - (float) (-(inverse_sqrt_of_expected_normalized_w * - inverse_sqrt_of_expected_normalized_w * - expected_normalized_w - - WeirdFloatMath.C_3) * - inverse_sqrt_of_expected_normalized_w * - WeirdFloatMath.C_HALF); - if (out_w <= 0.0) { - out_w = expected_normalized_w; - } - - out_w = expected_normalized_w * out_w; - } - - var sign = (first_ushort >> 0xf) switch { - 0 => 1, - 1 => -1, - _ => throw new InvalidDataException(), - }; + ((1 - outX * outX) - outY * outY) - outZ * outZ; + outW = 0d; + if (expected_normalized_w > 0) { + outW = Math.Sqrt(expected_normalized_w); - outValues[3] = out_w * sign; - } else { - outValues[3] = out_w; + var sign = first_ushort >> 0xf == 0 ? 1 : -1; + outW *= sign; } return (short) second_ushort < 0; diff --git a/FinModelUtility/Utility of Time CSharp/api/OotModelImporter.cs b/FinModelUtility/Utility of Time CSharp/api/OotModelImporter.cs index ac1d0dd69..0c266fb34 100644 --- a/FinModelUtility/Utility of Time CSharp/api/OotModelImporter.cs +++ b/FinModelUtility/Utility of Time CSharp/api/OotModelImporter.cs @@ -164,7 +164,7 @@ public IModel ImportModel(OotModelFileBundle modelFileBundle) { rootAnimationTracks.UseCombinedPositionAxesTrack(frameCount); for (var f = 0; f < frameCount; ++f) { var pos = ootAnimation.GetPosition(f); - positions.Set(f, new Position(pos.X, pos.Y, pos.Z)); + positions.SetKeyframe(f, new Position(pos.X, pos.Y, pos.Z)); } for (var i = 0; i < ootLimbs.Count; ++i) {