Browse Source

Fixed bug when an animation curve has only one animation key.

Vicente Penades 6 years ago
parent
commit
763617e5d0

+ 55 - 0
src/SharpGLTF.Core/Animations/LinearSamplers.cs

@@ -6,6 +6,61 @@ using System.Text;
 
 namespace SharpGLTF.Animations
 {
+    struct SingleValueSampler<T> : ICurveSampler<T>, IConvertibleCurve<T>
+    {
+        #region lifecycle
+
+        public static ICurveSampler<T> CreateForSingle(IEnumerable<(float Key, T Value)> sequence)
+        {
+            if (sequence.Skip(1).Any()) return null;
+
+            return new SingleValueSampler<T>(sequence.First().Value);
+        }
+
+        public static ICurveSampler<T> CreateForSingle(IEnumerable<(float Key, (T, T, T) Value)> sequence)
+        {
+            if (sequence.Skip(1).Any()) return null;
+
+            return new SingleValueSampler<T>(sequence.First().Value.Item2);
+        }
+
+        private SingleValueSampler(T value)
+        {
+            _Value = value;
+        }
+
+        #endregion
+
+        #region data
+
+        private readonly T _Value;
+
+        #endregion
+
+        #region API
+
+        public int MaxDegree => 0;
+
+        public T GetPoint(float offset) { return _Value; }
+
+        public IReadOnlyDictionary<float, T> ToLinearCurve()
+        {
+            return new Dictionary<float, T> { [0] = _Value };
+        }
+
+        public IReadOnlyDictionary<float, (T, T, T)> ToSplineCurve()
+        {
+            return new Dictionary<float, (T, T, T)> { [0] = (default, _Value, default) };
+        }
+
+        public IReadOnlyDictionary<float, T> ToStepCurve()
+        {
+            return new Dictionary<float, T> { [0] = _Value };
+        }
+
+        #endregion
+    }
+
     /// <summary>
     /// Defines a <see cref="Vector3"/> curve sampler that can be sampled with STEP or LINEAR interpolations.
     /// </summary>

+ 24 - 0
src/SharpGLTF.Core/Animations/SamplerFactory.cs

@@ -333,6 +333,9 @@ namespace SharpGLTF.Animations
         {
             if (collection == null) return null;
 
+            var single = SingleValueSampler<Vector3>.CreateForSingle(collection);
+            if (single != null) return single;
+
             var sampler = new Vector3LinearSampler(collection, isLinear);
 
             return optimize ? sampler.ToFastSampler() : sampler;
@@ -342,6 +345,9 @@ namespace SharpGLTF.Animations
         {
             if (collection == null) return null;
 
+            var single = SingleValueSampler<Quaternion>.CreateForSingle(collection);
+            if (single != null) return single;
+
             var sampler = new QuaternionLinearSampler(collection, isLinear);
 
             return optimize ? sampler.ToFastSampler() : sampler;
@@ -351,6 +357,9 @@ namespace SharpGLTF.Animations
         {
             if (collection == null) return null;
 
+            var single = SingleValueSampler<Transforms.SparseWeight8>.CreateForSingle(collection);
+            if (single != null) return single;
+
             var sampler = new SparseLinearSampler(collection, isLinear);
 
             return optimize ? sampler.ToFastSampler() : sampler;
@@ -360,6 +369,9 @@ namespace SharpGLTF.Animations
         {
             if (collection == null) return null;
 
+            var single = SingleValueSampler<Single[]>.CreateForSingle(collection);
+            if (single != null) return single;
+
             var sampler = new ArrayLinearSampler(collection, isLinear);
 
             return optimize ? sampler.ToFastSampler() : sampler;
@@ -369,6 +381,9 @@ namespace SharpGLTF.Animations
         {
             if (collection == null) return null;
 
+            var single = SingleValueSampler<Vector3>.CreateForSingle(collection);
+            if (single != null) return single;
+
             var sampler = new Vector3CubicSampler(collection);
 
             return optimize ? sampler.ToFastSampler() : sampler;
@@ -378,6 +393,9 @@ namespace SharpGLTF.Animations
         {
             if (collection == null) return null;
 
+            var single = SingleValueSampler<Quaternion>.CreateForSingle(collection);
+            if (single != null) return single;
+
             var sampler = new QuaternionCubicSampler(collection);
 
             return optimize ? sampler.ToFastSampler() : sampler;
@@ -387,6 +405,9 @@ namespace SharpGLTF.Animations
         {
             if (collection == null) return null;
 
+            var single = SingleValueSampler<Transforms.SparseWeight8>.CreateForSingle(collection);
+            if (single != null) return single;
+
             var sampler = new SparseCubicSampler(collection);
 
             return optimize ? sampler.ToFastSampler() : sampler;
@@ -396,6 +417,9 @@ namespace SharpGLTF.Animations
         {
             if (collection == null) return null;
 
+            var single = SingleValueSampler<Single[]>.CreateForSingle(collection);
+            if (single != null) return single;
+
             var sampler = new ArrayCubicSampler(collection);
 
             return optimize ? sampler.ToFastSampler() : sampler;

+ 2 - 2
src/SharpGLTF.Core/Collections/ChildrenCollection.cs

@@ -7,7 +7,6 @@ using System.Text;
 namespace SharpGLTF.Collections
 {
     [System.Diagnostics.DebuggerDisplay("{Count}")]
-    [System.Diagnostics.DebuggerTypeProxy(typeof(Debug._CollectionDebugProxy<>))]
     sealed class ChildrenCollection<T, TParent> : IList<T>, IReadOnlyList<T>
         where T : class, IChildOf<TParent>
         where TParent : class
@@ -27,7 +26,7 @@ namespace SharpGLTF.Collections
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         private readonly TParent _Parent;
 
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
         private List<T> _Collection;
 
         #endregion
@@ -64,6 +63,7 @@ namespace SharpGLTF.Collections
 
         public int Count => _Collection == null ? 0 : _Collection.Count;
 
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public bool IsReadOnly => false;
 
         #endregion

+ 4 - 0
src/SharpGLTF.Core/Schema2/gltf.Animations.cs

@@ -257,8 +257,10 @@ namespace SharpGLTF.Schema2
 
         #region data
 
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         internal int? _NodeId => this._node;
 
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         internal PropertyPath _NodePath => this._path;
 
         #endregion
@@ -275,6 +277,7 @@ namespace SharpGLTF.Schema2
         #endregion
     }
 
+    [System.Diagnostics.DebuggerDisplay("AnimChannel LogicalNode[{TargetNode.LogicalIndex}].{TargetNodePath}")]
     sealed partial class AnimationChannel : IChildOf<Animation>
     {
         #region lifecycle
@@ -302,6 +305,7 @@ namespace SharpGLTF.Schema2
         /// <summary>
         /// Gets the <see cref="Animation"/> instance that owns this object.
         /// </summary>
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public Animation LogicalParent { get; private set; }
 
         void IChildOf<Animation>._SetLogicalParent(Animation parent) { LogicalParent = parent; }

BIN
tests/SharpGLTF.Tests/Assets/SpecialCases/mouse.glb


+ 23 - 0
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSpecialModelsTest.cs

@@ -106,6 +106,29 @@ namespace SharpGLTF.Schema2.LoadAndSave
             Assert.NotNull(model);
         }
 
+        [Test]
+        public void LoadMouseModel()
+        {
+            // this model has several nodes with curve animations containing a single animation key,
+            // which is causing some problems to the interpolator.
+
+            TestContext.CurrentContext.AttachShowDirLink();
+
+            var path = "Assets\\SpecialCases\\mouse.glb";
+
+            var model = ModelRoot.Load(path);
+
+            var channel = model.LogicalAnimations[1].FindRotationSampler(model.LogicalNodes[5]);
+
+            var node5_R_00 = channel.CreateCurveSampler(true).GetPoint(0);
+            var node5_R_01 = channel.CreateCurveSampler(true).GetPoint(1);
+
+            Assert.AreEqual(node5_R_00, node5_R_01);
+
+            model.AttachToCurrentTest("mouse_00.obj", model.LogicalAnimations[1], 0f);
+            model.AttachToCurrentTest("mouse_01.obj", model.LogicalAnimations[1], 1f);
+        }
+
         // these models show normal mapping but lack tangents, which are expected to be
         // generated at runtime; These tests generate the tangents and check them against the baseline.
         [TestCase("NormalTangentTest.glb")]

+ 10 - 0
tests/SharpGLTF.Tests/SharpGLTF.Tests.csproj

@@ -10,6 +10,16 @@
     <LangVersion>7.1</LangVersion>
   </PropertyGroup>
 
+  <ItemGroup>
+    <None Remove="Assets\SpecialCases\mouse.glb" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="Assets\SpecialCases\mouse.glb">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
   <ItemGroup>    
     <PackageReference Include="nunit" Version="3.12.0" />
     <PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />