Pārlūkot izejas kodu

toolkit will use an universal curve for animations

Vicente Penades 6 gadi atpakaļ
vecāks
revīzija
f620a52201

+ 1 - 13
src/SharpGLTF.Toolkit/Animations/Animatable.cs

@@ -29,19 +29,7 @@ namespace SharpGLTF.Animations
             return _Tracks.TryGetValue(track, out ICurveSampler<T> sampler) ? sampler.GetPoint(value) : this.Default;
         }
 
-        public ICurveSampler<T> UseLinearCurve(string track)
-        {
-            if (!_Tracks.TryGetValue(track, out ICurveSampler<T> curve))
-            {
-                _Tracks[track] = curve = CurveFactory.CreateLinearCurve<T>();
-            }
-
-            if (curve is ILinearCurve<T> editableCurve) return editableCurve;
-
-            throw new ArgumentException(nameof(T), "Generic argument not supported");
-        }
-
-        public ICurveSampler<T> UseSplineCurve(string track)
+        public ICurveSampler<T> UseCurve(string track)
         {
             if (!_Tracks.TryGetValue(track, out ICurveSampler<T> curve))
             {

+ 97 - 224
src/SharpGLTF.Toolkit/Animations/Curves.cs

@@ -9,7 +9,6 @@ namespace SharpGLTF.Animations
     // TODO: define just ONE kind of curve: spline with flags, where a flag might indicate if the current segment is linear or spline.
     // when converting to gltf, check if all segments are linear, and use the appropiate encoding.
 
-
     public interface ICurveSampler<T>
         where T : struct
     {
@@ -22,6 +21,21 @@ namespace SharpGLTF.Animations
         T GetTangent(float offset);
     }
 
+    interface ISplineCurve<T> : ICurveSampler<T>
+        where T : struct
+    {
+        void RemoveKey(float key);
+
+        T GetControlPoint(float key);
+
+        void SetControlPoint(float key, T value);
+
+        void SetTangentIn(float key, T value, float scale);
+        void SetTangentOut(float key, T value, float scale);
+
+        Dictionary<float, (T, T, T)> ToDictionary();
+    }
+
     [System.Diagnostics.DebuggerDisplay("[{_Offset}] = {Sample}")]
     public struct CurvePoint<T>
         where T : struct
@@ -61,12 +75,6 @@ namespace SharpGLTF.Animations
 
             if (c.HasValue && c.Value._Offset == this._Offset) return this;
 
-            if (_Curve is ILinearCurve<T> linear)
-            {
-                linear.SetControlPoint(_Offset, _Curve.GetPoint(_Offset));
-                return this;
-            }
-
             if (_Curve is ISplineCurve<T> spline)
             {
                 var p = _Curve.GetPoint(_Offset);
@@ -104,12 +112,6 @@ namespace SharpGLTF.Animations
         {
             Split();
 
-            if (_Curve is ILinearCurve<T> linear)
-            {
-                linear.SetControlPoint(_Offset, value);
-                return this;
-            }
-
             if (_Curve is ISplineCurve<T> spline)
             {
                 spline.SetControlPoint(_Offset, value);
@@ -148,50 +150,13 @@ namespace SharpGLTF.Animations
         #endregion
     }
 
-    interface ILinearCurve<T> : ICurveSampler<T>
-        where T : struct
-    {
-        void RemoveKey(float key);
-
-        T GetControlPoint(float key);
-
-        void SetControlPoint(float key, T value);
-
-        Dictionary<float, T> ToDictionary();
-    }
-
-    interface ISplineCurve<T> : ICurveSampler<T>
-        where T : struct
-    {
-        IReadOnlyCollection<float> Keys { get; }
-
-        void RemoveKey(float key);
-
-        T GetControlPoint(float key);
-
-        void SetControlPoint(float key, T value);
-
-        void SetTangentIn(float key, T value, float scale);
-        void SetTangentOut(float key, T value, float scale);
-
-        Dictionary<float, (T, T, T)> ToDictionary();
-    }
+    
 
     public static class CurveFactory
     {
         // TODO: we could support conversions between linear and cubic (with hermite regression)
 
-        public static ILinearCurve<T> CreateLinearCurve<T>()
-            where T : struct
-        {
-            if (typeof(T) == typeof(Single)) return new ScalarLinearCurve() as ILinearCurve<T>;
-            if (typeof(T) == typeof(Vector3)) return new Vector3LinearCurve() as ILinearCurve<T>;
-            if (typeof(T) == typeof(Quaternion)) return new QuaternionLinearCurve() as ILinearCurve<T>;
-
-            throw new ArgumentException(nameof(T), "Generic argument not supported");
-        }
-
-        public static ISplineCurve<T> CreateSplineCurve<T>()
+        public static ICurveSampler<T> CreateSplineCurve<T>()
             where T : struct
         {
             if (typeof(T) == typeof(Single)) return new ScalarSplineCurve() as ISplineCurve<T>;
@@ -258,17 +223,15 @@ namespace SharpGLTF.Animations
     /// <summary>
     /// Represents a collection of consecutive nodes that can be sampled into a continuous curve.
     /// </summary>
-    /// <typeparam name="Tin">The type of a node in the sequence.</typeparam>
     /// <typeparam name="Tout">The type of value evaluated at any point in the curve.</typeparam>
-    abstract class Curve<Tin, Tout>
-        where Tin : struct
+    abstract class Curve<Tout>
         where Tout : struct
     {
         #region lifecycle
 
         public Curve() { }
 
-        protected Curve(Curve<Tin, Tout> other)
+        protected Curve(Curve<Tout> other)
         {
             foreach (var kvp in other._Keys)
             {
@@ -280,12 +243,11 @@ namespace SharpGLTF.Animations
 
         #region data
 
-        protected SortedDictionary<float, Tin> _Keys = new SortedDictionary<float, Tin>();
+        protected SortedDictionary<float, _SplinePoint<Tout>> _Keys = new SortedDictionary<float, _SplinePoint<Tout>>();
 
         #endregion
 
         #region properties
-
         public IReadOnlyCollection<float> Keys => _Keys.Keys;
 
         #endregion
@@ -294,13 +256,13 @@ namespace SharpGLTF.Animations
 
         public void RemoveKey(float key) { _Keys.Remove(key); }
 
-        protected Tin? GetKey(float key) { return _Keys.TryGetValue(key, out Tin value) ? value : (Tin?)null; }
+        protected _SplinePoint<Tout>? GetKey(float key) { return _Keys.TryGetValue(key, out _SplinePoint<Tout> value) ? value : (_SplinePoint<Tout>?)null; }
 
-        protected void SetKey(float key, Tin value) { _Keys[key] = value; }
+        protected void SetKey(float key, _SplinePoint<Tout> value) { _Keys[key] = value; }
 
-        protected (Tin, Tin, float) FindSample(float offset)
+        protected (_SplinePoint<Tout>, _SplinePoint<Tout>, float) FindSample(float offset)
         {
-            if (_Keys.Count == 0) return (default(Tin), default(Tin), 0);
+            if (_Keys.Count == 0) return (default(_SplinePoint<Tout>), default(_SplinePoint<Tout>), 0);
 
             var offsets = _FindPairContainingOffset(_Keys.Keys, offset);
 
@@ -368,56 +330,13 @@ namespace SharpGLTF.Animations
     struct _SplinePoint<T>
         where T : struct
     {
-        public T InTangent;
+        public T IncomingTangent;
         public T Point;
-        public T OutTangent;
+        public T OutgoingTangent;
+        public int OutgoingMode;
     }
 
-    class ScalarLinearCurve : Curve<Single, Single>, ILinearCurve<Single>
-    {
-        #region lifecycle
-
-        public ScalarLinearCurve() { }
-
-        protected ScalarLinearCurve(ScalarLinearCurve other) : base(other) { }
-
-        #endregion
-
-        #region API
-
-        public Single GetPoint(float offset)
-        {
-            var sample = FindSample(offset);
-
-            return (sample.Item1 * (1 - sample.Item3)) + (sample.Item2 * sample.Item3);
-        }
-
-        public Single GetTangent(float offset)
-        {
-            var sample = FindSample(offset);
-            return sample.Item2 - sample.Item1;
-        }
-
-        public float GetControlPoint(float key)
-        {
-            var sample = FindSample(key);
-            return sample.Item3 <= 0.5f ? sample.Item1 : sample.Item2;
-        }
-
-        public void SetControlPoint(float offset, Single value)
-        {
-            SetKey(offset, value);
-        }
-
-        public Dictionary<float, float> ToDictionary()
-        {
-            return _Keys.ToDictionary(k => k.Key, v => v.Value);
-        }
-
-        #endregion
-    }
-
-    class ScalarSplineCurve : Curve<_SplinePoint<Single>, Single>, ISplineCurve<Single>
+    class ScalarSplineCurve : Curve<Single>, ISplineCurve<Single>
     {
         #region lifecycle
 
@@ -432,10 +351,18 @@ namespace SharpGLTF.Animations
         public float GetPoint(float offset)
         {
             var sample = FindSample(offset);
+
+            if (sample.Item1.OutgoingMode == 0) return sample.Item1.Point;
+
+            if (sample.Item1.OutgoingMode == 1)
+            {
+                return (sample.Item1.Point * (1 - sample.Item3)) + (sample.Item2.Point * sample.Item3);
+            }
+
             var pointStart = sample.Item1.Point;
-            var tangentOut = sample.Item1.OutTangent;
+            var tangentOut = sample.Item1.OutgoingTangent;
             var pointEnd = sample.Item2.Point;
-            var tangentIn = sample.Item2.InTangent;
+            var tangentIn = sample.Item2.IncomingTangent;
 
             var basis = CurveFactory.CalculateHermiteBasis(sample.Item3);
 
@@ -445,10 +372,15 @@ namespace SharpGLTF.Animations
         public float GetTangent(float offset)
         {
             var sample = FindSample(offset);
+
+            if (sample.Item1.OutgoingMode == 0) return 0;
+
+            if (sample.Item1.OutgoingMode == 1) return sample.Item2.Point - sample.Item1.Point;
+
             var pointStart = sample.Item1.Point;
-            var tangentOut = sample.Item1.OutTangent;
+            var tangentOut = sample.Item1.OutgoingTangent;
             var pointEnd = sample.Item2.Point;
-            var tangentIn = sample.Item2.InTangent;
+            var tangentIn = sample.Item2.IncomingTangent;
 
             var basis = CurveFactory.CalculateHermiteTangent(sample.Item3);
 
@@ -471,46 +403,46 @@ namespace SharpGLTF.Animations
         public void SetCardinalPointIn(float key, Single value)
         {
             var val = GetKey(key) ?? default;
-            val.InTangent = (val.Point - value) * 4;
+            val.IncomingTangent = (val.Point - value) * 4;
             SetKey(key, val);
         }
 
         public void SetCardinalPointOut(float key, Single value)
         {
             var val = GetKey(key) ?? default;
-            val.OutTangent = (value - val.Point) * 4;
+            val.OutgoingTangent = (value - val.Point) * 4;
             SetKey(key, val);
         }
 
         public void SetTangentIn(float key, Single value, float scale)
         {
             var val = GetKey(key) ?? default;
-            val.InTangent = value * scale;
+            val.IncomingTangent = value * scale;
             SetKey(key, val);
         }
 
         public void SetTangentOut(float key, Single value, float scale)
         {
             var val = GetKey(key) ?? default;
-            val.OutTangent = value * scale;
+            val.OutgoingTangent = value * scale;
             SetKey(key, val);
         }
 
         public Dictionary<float, (float, float, float)> ToDictionary()
         {
-            return _Keys.ToDictionary(k => k.Key, v => (v.Value.InTangent, v.Value.Point, v.Value.OutTangent));
+            return _Keys.ToDictionary(k => k.Key, v => (v.Value.IncomingTangent, v.Value.Point, v.Value.OutgoingTangent));
         }
 
         #endregion
     }
 
-    class Vector3LinearCurve : Curve<Vector3, Vector3>, ILinearCurve<Vector3>
+    class Vector3SplineCurve : Curve<Vector3>, ISplineCurve<Vector3>
     {
         #region lifecycle
 
-        public Vector3LinearCurve() { }
+        public Vector3SplineCurve() { }
 
-        protected Vector3LinearCurve(Vector3LinearCurve other) : base(other) { }
+        protected Vector3SplineCurve(Vector3SplineCurve other) : base(other) { }
 
         #endregion
 
@@ -520,54 +452,17 @@ namespace SharpGLTF.Animations
         {
             var sample = FindSample(offset);
 
-            return Vector3.Lerp(sample.Item1, sample.Item2, sample.Item3);
-        }
+            if (sample.Item1.OutgoingMode == 0) return sample.Item1.Point;
 
-        public Vector3 GetTangent(float offset)
-        {
-            var sample = FindSample(offset);
-
-            return sample.Item2 - sample.Item1;
-        }
-
-        public Vector3 GetControlPoint(float key)
-        {
-            var sample = FindSample(key);
-            return sample.Item3 <= 0.5f ? sample.Item1 : sample.Item2;
-        }
-
-        public void SetControlPoint(float offset, Vector3 value)
-        {
-            SetKey(offset, value);
-        }
-
-        public Dictionary<float, Vector3> ToDictionary()
-        {
-            return _Keys.ToDictionary(k => k.Key, v => v.Value);
-        }
-
-        #endregion
-    }
-
-    class Vector3SplineCurve : Curve<_SplinePoint<Vector3>, Vector3>, ISplineCurve<Vector3>
-    {
-        #region lifecycle
-
-        public Vector3SplineCurve() { }
-
-        protected Vector3SplineCurve(Vector3SplineCurve other) : base(other) { }
-
-        #endregion
-
-        #region API
+            if (sample.Item1.OutgoingMode == 1)
+            {
+                return Vector3.Lerp(sample.Item1.Point, sample.Item2.Point, sample.Item3);
+            }
 
-        public Vector3 GetPoint(float offset)
-        {
-            var sample = FindSample(offset);
             var pointStart = sample.Item1.Point;
-            var tangentOut = sample.Item1.OutTangent;
+            var tangentOut = sample.Item1.OutgoingTangent;
             var pointEnd = sample.Item2.Point;
-            var tangentIn = sample.Item2.InTangent;
+            var tangentIn = sample.Item2.IncomingTangent;
 
             var basis = CurveFactory.CalculateHermiteBasis(sample.Item3);
 
@@ -577,10 +472,15 @@ namespace SharpGLTF.Animations
         public Vector3 GetTangent(float offset)
         {
             var sample = FindSample(offset);
+
+            if (sample.Item1.OutgoingMode == 0) return Vector3.Zero;
+
+            if (sample.Item1.OutgoingMode == 1) return sample.Item2.Point - sample.Item1.Point;
+
             var pointStart = sample.Item1.Point;
-            var tangentOut = sample.Item1.OutTangent;
+            var tangentOut = sample.Item1.OutgoingTangent;
             var pointEnd = sample.Item2.Point;
-            var tangentIn = sample.Item2.InTangent;
+            var tangentIn = sample.Item2.IncomingTangent;
 
             var basis = CurveFactory.CalculateHermiteTangent(sample.Item3);
 
@@ -603,46 +503,46 @@ namespace SharpGLTF.Animations
         public void SetCardinalPointIn(float key, Vector3 value)
         {
             var val = GetKey(key) ?? default;
-            val.InTangent = (val.Point - value) * 4;
+            val.IncomingTangent = (val.Point - value) * 4;
             SetKey(key, val);
         }
 
         public void SetCardinalPointOut(float key, Vector3 value)
         {
             var val = GetKey(key) ?? default;
-            val.OutTangent = (value - val.Point) * 4;
+            val.OutgoingTangent = (value - val.Point) * 4;
             SetKey(key, val);
         }
 
         public void SetTangentIn(float key, Vector3 value, float scale)
         {
             var val = GetKey(key) ?? default;
-            val.InTangent = value * scale;
+            val.IncomingTangent = value * scale;
             SetKey(key, val);
         }
 
         public void SetTangentOut(float key, Vector3 value, float scale)
         {
             var val = GetKey(key) ?? default;
-            val.OutTangent = value * scale;
+            val.OutgoingTangent = value * scale;
             SetKey(key, val);
         }
 
         public Dictionary<float, (Vector3, Vector3, Vector3)> ToDictionary()
         {
-            return _Keys.ToDictionary(k => k.Key, v => (v.Value.InTangent, v.Value.Point, v.Value.OutTangent));
+            return _Keys.ToDictionary(k => k.Key, v => (v.Value.IncomingTangent, v.Value.Point, v.Value.OutgoingTangent));
         }
 
         #endregion
     }
 
-    class QuaternionLinearCurve : Curve<Quaternion, Quaternion>, ILinearCurve<Quaternion>
+    class QuaternionSplineCurve : Curve<Quaternion> , ISplineCurve<Quaternion>
     {
         #region lifecycle
 
-        public QuaternionLinearCurve() { }
+        public QuaternionSplineCurve() { }
 
-        protected QuaternionLinearCurve(QuaternionLinearCurve other) : base(other) { }
+        protected QuaternionSplineCurve(QuaternionSplineCurve other) : base(other) { }
 
         #endregion
 
@@ -652,52 +552,17 @@ namespace SharpGLTF.Animations
         {
             var sample = FindSample(offset);
 
-            return Quaternion.Slerp(sample.Item1, sample.Item2, sample.Item3);
-        }
-
-        public Quaternion GetTangent(float offset)
-        {
-            throw new NotImplementedException();
-        }
+            if (sample.Item1.OutgoingMode == 0) return sample.Item1.Point;
 
-        public Quaternion GetControlPoint(float key)
-        {
-            var sample = FindSample(key);
-            return sample.Item3 <= 0.5f ? sample.Item1 : sample.Item2;
-        }
-
-        public void SetControlPoint(float offset, Quaternion value)
-        {
-            SetKey(offset, value);
-        }
-
-        public Dictionary<float, Quaternion> ToDictionary()
-        {
-            return _Keys.ToDictionary(k => k.Key, v => v.Value);
-        }
-
-        #endregion
-    }
-
-    class QuaternionSplineCurve : Curve<_SplinePoint<Quaternion>, Quaternion> , ISplineCurve<Quaternion>
-    {
-        #region lifecycle
-
-        public QuaternionSplineCurve() { }
-
-        protected QuaternionSplineCurve(QuaternionSplineCurve other) : base(other) { }
-
-        #endregion
-
-        #region API
+            if (sample.Item1.OutgoingMode == 1)
+            {
+                return Quaternion.Slerp(sample.Item1.Point, sample.Item2.Point, sample.Item3);
+            }
 
-        public Quaternion GetPoint(float offset)
-        {
-            var sample = FindSample(offset);
             var pointStart = sample.Item1.Point;
-            var tangentOut = sample.Item1.OutTangent;
+            var tangentOut = sample.Item1.OutgoingTangent;
             var pointEnd = sample.Item2.Point;
-            var tangentIn = sample.Item2.InTangent;
+            var tangentIn = sample.Item2.IncomingTangent;
 
             var basis = CurveFactory.CalculateHermiteBasis(sample.Item3);
 
@@ -709,10 +574,18 @@ namespace SharpGLTF.Animations
         public Quaternion GetTangent(float offset)
         {
             var sample = FindSample(offset);
+
+            if (sample.Item1.OutgoingMode == 0) return Quaternion.Identity;
+
+            if (sample.Item1.OutgoingMode == 1)
+            {
+                throw new NotImplementedException();
+            }
+
             var pointStart = sample.Item1.Point;
-            var tangentOut = sample.Item1.OutTangent;
+            var tangentOut = sample.Item1.OutgoingTangent;
             var pointEnd = sample.Item2.Point;
-            var tangentIn = sample.Item2.InTangent;
+            var tangentIn = sample.Item2.IncomingTangent;
 
             var basis = CurveFactory.CalculateHermiteTangent(sample.Item3);
 
@@ -742,7 +615,7 @@ namespace SharpGLTF.Animations
             value = Quaternion.Concatenate(val.Point, inv); // *4? => convert to axisradians; angle * 4, back to Q
             value = Quaternion.Normalize(value);
 
-            val.InTangent = value;
+            val.IncomingTangent = value;
             SetKey(key, val);
         }
 
@@ -754,21 +627,21 @@ namespace SharpGLTF.Animations
             value = Quaternion.Concatenate(value, inv); // *4? => convert to axisradians; angle * 4, back to Q
             value = Quaternion.Normalize(value);
 
-            val.OutTangent = value;
+            val.OutgoingTangent = value;
             SetKey(key, val);
         }
 
         public void SetTangentIn(float key, Quaternion value, float scale)
         {
             var val = GetKey(key) ?? default;
-            val.InTangent = _Scale(value, scale);
+            val.IncomingTangent = _Scale(value, scale);
             SetKey(key, val);
         }
 
         public void SetTangentOut(float key, Quaternion value, float scale)
         {
             var val = GetKey(key) ?? default;
-            val.OutTangent = _Scale(value, scale);
+            val.OutgoingTangent = _Scale(value, scale);
             SetKey(key, val);
         }
 
@@ -782,7 +655,7 @@ namespace SharpGLTF.Animations
 
         public Dictionary<float, (Quaternion, Quaternion, Quaternion)> ToDictionary()
         {
-            return _Keys.ToDictionary(k => k.Key, v => (v.Value.InTangent, v.Value.Point, v.Value.OutTangent));
+            return _Keys.ToDictionary(k => k.Key, v => (v.Value.IncomingTangent, v.Value.Point, v.Value.OutgoingTangent));
         }
 
         #endregion

+ 0 - 18
src/SharpGLTF.Toolkit/Schema2/AnimationExtensions.cs

@@ -27,12 +27,6 @@ namespace SharpGLTF.Schema2
                 return node;
             }
 
-            if (curve is Animations.ILinearCurve<Vector3> linear)
-            {
-                animation.CreateScaleChannel(node, linear.ToDictionary());
-                return node;
-            }
-
             throw new ArgumentException("Not supported", nameof(curve));
         }
 
@@ -48,12 +42,6 @@ namespace SharpGLTF.Schema2
                 return node;
             }
 
-            if (curve is Animations.ILinearCurve<Vector3> linear)
-            {
-                animation.CreateTranslationChannel(node, linear.ToDictionary());
-                return node;
-            }
-
             throw new ArgumentException("Not supported", nameof(curve));
         }
 
@@ -69,12 +57,6 @@ namespace SharpGLTF.Schema2
                 return node;
             }
 
-            if (curve is Animations.ILinearCurve<Quaternion> linear)
-            {
-                animation.CreateRotationChannel(node, linear.ToDictionary());
-                return node;
-            }
-
             throw new ArgumentException("Not supported", nameof(curve));
         }
 

+ 1 - 1
tests/SharpGLTF.Tests/Scenes/SceneBuilderTests.cs

@@ -45,7 +45,7 @@ namespace SharpGLTF.Scenes
 
             var pivot = new NodeBuilder();
 
-            var tcurve = pivot.UseTranslation().UseSplineCurve("default");
+            var tcurve = pivot.UseTranslation().UseCurve("default");
 
             var c = new Animations.CurvePoint<Vector3>(tcurve, 0);