Browse Source

WIP on morph targets roundtrip

Vicente Penades 6 years ago
parent
commit
5d01d4b442

+ 24 - 3
src/SharpGLTF.Core/Schema2/gltf.Animations.cs

@@ -132,7 +132,7 @@ namespace SharpGLTF.Schema2
                 .SetSampler(sampler);
         }
 
-        public void CreateMorphChannel(Node node, AnimationInterpolationMode mode, IReadOnlyDictionary<Single, SparseWeight8> keyframes, int morphCount, bool linear = true)
+        public void CreateMorphChannel(Node node, IReadOnlyDictionary<Single, SparseWeight8> keyframes, int morphCount, bool linear = true)
         {
             var sampler = this._CreateSampler(linear ? AnimationInterpolationMode.LINEAR : AnimationInterpolationMode.STEP);
 
@@ -142,6 +142,16 @@ namespace SharpGLTF.Schema2
                 .SetSampler(sampler);
         }
 
+        public void CreateMorphChannel(Node node, IReadOnlyDictionary<Single, (SparseWeight8, SparseWeight8, SparseWeight8)> keyframes, int morphCount)
+        {
+            var sampler = this._CreateSampler(AnimationInterpolationMode.CUBICSPLINE);
+
+            sampler.SetKeys(keyframes, morphCount);
+
+            this._UseChannel(node, PropertyPath.weights)
+                .SetSampler(sampler);
+        }
+
         private AnimationChannel FindChannel(Node node, PropertyPath path)
         {
             return _channels.FirstOrDefault(item => item.TargetNode == node && item.TargetNodePath == path);
@@ -480,13 +490,24 @@ namespace SharpGLTF.Schema2
         {
             var kv = _Split(keyframes);
 
-            kv.Item2[0] = new Quaternion(0, 0, 0, 0);
-            kv.Item2[kv.Item2.Length - 1] = new Quaternion(0, 0, 0, 0);
+            kv.Item2[0] = default(Quaternion);
+            kv.Item2[kv.Item2.Length - 1] = default(Quaternion);
 
             _input = this._CreateInputAccessor(kv.Item1).LogicalIndex;
             _output = this._CreateOutputAccessor(kv.Item2).LogicalIndex;
         }
 
+        internal void SetKeys(IReadOnlyDictionary<Single, (SparseWeight8, SparseWeight8, SparseWeight8)> keyframes, int expandedCount)
+        {
+            var kv = _Split(keyframes);
+
+            kv.Item2[0] = default(SparseWeight8);
+            kv.Item2[kv.Item2.Length - 1] = default(SparseWeight8);
+
+            _input = this._CreateInputAccessor(kv.Item1).LogicalIndex;
+            _output = this._CreateOutputAccessor(kv.Item2, expandedCount).LogicalIndex;
+        }
+
         IEnumerable<(Single, Vector3)> IAnimationSampler<Vector3>.GetLinearKeys(bool isolateMemory)
         {
             Guard.IsFalse(this.InterpolationMode == AnimationInterpolationMode.CUBICSPLINE, nameof(InterpolationMode));

+ 13 - 0
src/SharpGLTF.Core/Schema2/gltf.Mesh.cs

@@ -36,6 +36,19 @@ namespace SharpGLTF.Schema2
 
         #region API
 
+        public void SetMorphWeights(Transforms.SparseWeight8 weights)
+        {
+            int count = _primitives.Max(item => item.MorphTargetsCount);
+
+            while (_weights.Count > count) _weights.RemoveAt(_weights.Count - 1);
+            while (_weights.Count < count) _weights.Add(0);
+
+            foreach (var kw in weights.GetIndexedWeights())
+            {
+                _weights[kw.Item1] = kw.Item2;
+            }
+        }
+
         /// <inheritdoc />
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {

+ 1 - 1
src/SharpGLTF.Toolkit/Geometry/PackedMeshBuilder.cs

@@ -86,7 +86,7 @@ namespace SharpGLTF.Geometry
                 p.CopyToMesh(dstMesh, materialEvaluator);
             }
 
-            // TODO: set default morph target weights.
+            dstMesh.SetMorphWeights(default);
 
             return dstMesh;
         }

+ 1 - 4
src/SharpGLTF.Toolkit/Scenes/NodeBuilder.cs

@@ -34,7 +34,6 @@ namespace SharpGLTF.Scenes
         private Animations.AnimatableProperty<Vector3> _Scale;
         private Animations.AnimatableProperty<Quaternion> _Rotation;
         private Animations.AnimatableProperty<Vector3> _Translation;
-        private Animations.AnimatableProperty<Transforms.SparseWeight8> _Morphings;
 
         #endregion
 
@@ -55,7 +54,7 @@ namespace SharpGLTF.Scenes
         /// <summary>
         /// Gets a value indicating whether this <see cref="NodeBuilder"/> has animations.
         /// </summary>
-        public bool HasAnimations => (_Scale?.IsAnimated ?? false) || (_Rotation?.IsAnimated ?? false) || (_Translation?.IsAnimated ?? false) || (_Morphings?.IsAnimated ?? false);
+        public bool HasAnimations => (_Scale?.IsAnimated ?? false) || (_Rotation?.IsAnimated ?? false) || (_Translation?.IsAnimated ?? false);
 
         public Animations.AnimatableProperty<Vector3> Scale => _Scale;
 
@@ -63,8 +62,6 @@ namespace SharpGLTF.Scenes
 
         public Animations.AnimatableProperty<Vector3> Translation => _Translation;
 
-        public Animations.AnimatableProperty<Transforms.SparseWeight8> Morphings => _Morphings;
-
         /// <summary>
         /// Gets or sets the local transform <see cref="Matrix4x4"/> of this <see cref="NodeBuilder"/>.
         /// </summary>

+ 43 - 12
src/SharpGLTF.Toolkit/Scenes/SceneBuilder.Schema2.cs

@@ -116,7 +116,7 @@ namespace SharpGLTF.Scenes
                 // Copies all the animations to the target node.
                 if (srcNode.Scale != null) foreach (var t in srcNode.Scale.Tracks) dstNode.WithScaleAnimation(t.Key, t.Value);
                 if (srcNode.Rotation != null) foreach (var t in srcNode.Rotation.Tracks) dstNode.WithRotationAnimation(t.Key, t.Value);
-                if (srcNode.Translation != null) foreach (var t in srcNode.Translation.Tracks) dstNode.WithTranslationAnimation(t.Key, t.Value);
+                if (srcNode.Translation != null) foreach (var t in srcNode.Translation.Tracks) dstNode.WithTranslationAnimation(t.Key, t.Value);                
             }
             else
             {
@@ -126,6 +126,16 @@ namespace SharpGLTF.Scenes
             foreach (var c in srcNode.VisualChildren) CreateArmature(dstNode, c);
         }
 
+        public void SetMorphAnimation(Node dstNode, Animations.AnimatableProperty<Transforms.SparseWeight8> animation)
+        {
+            if (animation == null) return;
+
+            var dstMesh = dstNode.Mesh;
+            dstMesh.SetMorphWeights(default);
+
+            foreach (var t in animation.Tracks) dstNode.WithMorphingAnimation(t.Key, t.Value);
+        }
+
         #endregion
 
         #region types
@@ -174,7 +184,7 @@ namespace SharpGLTF.Scenes
             foreach (var srcArmature in srcScene.VisualChildren)
             {
                 var dstArmature = new NodeBuilder();
-                CopyToNodeBuilder(srcArmature, dstArmature, dstNodes);
+                CopyToNodeBuilder(dstArmature, srcArmature, dstNodes);
             }
 
             // TODO: we must also process the armatures of every skin, in case the joints are outside the scene.
@@ -212,7 +222,9 @@ namespace SharpGLTF.Scenes
                 if (srcInstance.Skin == null)
                 {
                     var dstNode = dstNodes[srcInstance];
-                    dstScene.AddMesh(dstMesh, dstNode);
+                    var dstInst = dstScene.AddMesh(dstMesh, dstNode);
+
+                    CopyMorphingAnimation(dstInst, srcInstance);
                 }
                 else
                 {
@@ -224,7 +236,9 @@ namespace SharpGLTF.Scenes
                         joints[i] = (dstNodes[j.Item1], j.Item2);
                     }
 
-                    dstScene.AddSkinnedMesh(dstMesh, joints);
+                    var dstInst = dstScene.AddSkinnedMesh(dstMesh, joints);
+
+                    CopyMorphingAnimation(dstInst, srcInstance);
                 }
             }
         }
@@ -250,7 +264,7 @@ namespace SharpGLTF.Scenes
             }
         }
 
-        private static void CopyToNodeBuilder(Node srcNode, NodeBuilder dstNode, IDictionary<Node, NodeBuilder> nodeMapping)
+        private static void CopyToNodeBuilder(NodeBuilder dstNode, Node srcNode, IDictionary<Node, NodeBuilder> nodeMapping)
         {
             Guard.NotNull(srcNode, nameof(srcNode));
             Guard.NotNull(dstNode, nameof(dstNode));
@@ -258,6 +272,21 @@ namespace SharpGLTF.Scenes
             dstNode.Name = srcNode.Name;
             dstNode.LocalTransform = srcNode.LocalTransform;
 
+            CopyTransformAnimation(dstNode, srcNode);
+
+            if (nodeMapping == null) return;
+
+            nodeMapping[srcNode] = dstNode;
+
+            foreach (var srcChild in srcNode.VisualChildren)
+            {
+                var dstChild = dstNode.CreateNode();
+                CopyToNodeBuilder(dstChild, srcChild, nodeMapping);
+            }
+        }
+
+        private static void CopyTransformAnimation(NodeBuilder dstNode, Node srcNode)
+        {
             foreach (var anim in srcNode.LogicalParent.LogicalAnimations)
             {
                 var name = anim.Name;
@@ -272,15 +301,17 @@ namespace SharpGLTF.Scenes
                 var traAnim = anim.FindTranslationSampler(srcNode)?.CreateCurveSampler();
                 if (traAnim != null) dstNode.UseTranslation(name).SetCurve(traAnim);
             }
+        }
 
-            if (nodeMapping == null) return;
-
-            nodeMapping[srcNode] = dstNode;
-
-            foreach (var srcChild in srcNode.VisualChildren)
+        private static void CopyMorphingAnimation(InstanceBuilder dstInst, Node srcNode)
+        {
+            foreach (var anim in srcNode.LogicalParent.LogicalAnimations)
             {
-                var dstChild = dstNode.CreateNode();
-                CopyToNodeBuilder(srcChild, dstChild, nodeMapping);
+                var name = anim.Name;
+                if (string.IsNullOrWhiteSpace(name)) name = anim.LogicalIndex.ToString(System.Globalization.CultureInfo.InvariantCulture);
+
+                var mrpAnim = anim.FindSparseMorphSampler(srcNode)?.CreateCurveSampler();
+                if (mrpAnim != null) dstInst.Content.UseMorphing(name).SetCurve(mrpAnim);
             }
         }
 

+ 9 - 6
src/SharpGLTF.Toolkit/Scenes/Transformers.Schema2.cs

@@ -16,10 +16,11 @@ namespace SharpGLTF.Scenes
         {
             if (!(Content is SCHEMA2NODE schema2Target)) return;
 
-            var node = dstScene.CreateNode();
-            node.LocalMatrix = _WorldTransform;
+            var dstNode = dstScene.CreateNode();
 
-            schema2Target.Setup(node, context);
+            dstNode.LocalMatrix = _WorldTransform;
+
+            schema2Target.Setup(dstNode, context);
         }
     }
 
@@ -29,11 +30,11 @@ namespace SharpGLTF.Scenes
         {
             if (!(Content is SCHEMA2NODE schema2Target)) return;
 
-            var node = context.GetNode(_Node);
+            var dstNode = context.GetNode(_Node);
 
-            if (node == null) dstScene.CreateNode();
+            schema2Target.Setup(dstNode, context);
 
-            schema2Target.Setup(node, context);
+            context.SetMorphAnimation(dstNode, this.Morphings);
         }
     }
 
@@ -82,6 +83,8 @@ namespace SharpGLTF.Scenes
             // skinnedMeshNode.Skin.Skeleton = context.GetNode(root);
 
             schema2Target.Setup(skinnedMeshNode, context);
+
+            context.SetMorphAnimation(skinnedMeshNode, this.Morphings);
         }
     }
 }

+ 20 - 0
src/SharpGLTF.Toolkit/Scenes/Transformers.cs

@@ -34,12 +34,16 @@ namespace SharpGLTF.Scenes
 
         private Object _Content;
 
+        private Animations.AnimatableProperty<Transforms.SparseWeight8> _Morphings; // maybe it should be moved to transformers!!
+
         #endregion
 
         #region properties
 
         public Object Content => _Content;
 
+        public Animations.AnimatableProperty<Transforms.SparseWeight8> Morphings => _Morphings;
+
         #endregion
 
         #region API
@@ -48,6 +52,22 @@ namespace SharpGLTF.Scenes
 
         public abstract NodeBuilder GetArmatureAsset();
 
+        public Animations.AnimatableProperty<Transforms.SparseWeight8> UseMorphing()
+        {
+            if (_Morphings == null)
+            {
+                _Morphings = new Animations.AnimatableProperty<Transforms.SparseWeight8>();
+                _Morphings.Value = default;
+            }
+
+            return _Morphings;
+        }
+
+        public Animations.CurveBuilder<Transforms.SparseWeight8> UseMorphing(string animationTrack)
+        {
+            return UseMorphing().UseTrackBuilder(animationTrack);
+        }
+
         #endregion
     }
 

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

@@ -51,6 +51,25 @@ namespace SharpGLTF.Schema2
             return node;
         }
 
+        public static Node WithMorphingAnimation(this Node node, string animationName, Animations.ICurveSampler<Transforms.SparseWeight8> sampler)
+        {
+            Guard.NotNull(node, nameof(node));
+            Guard.NotNull(node.MorphWeights, nameof(node.MorphWeights), "Set node.MorphWeights before setting morphing animation");
+            Guard.MustBeGreaterThanOrEqualTo(node.MorphWeights.Count, 0, nameof(node.MorphWeights));
+
+            if (sampler is Animations.IConvertibleCurve<Transforms.SparseWeight8> curve)
+            {
+                var animation = node.LogicalParent.UseAnimation(animationName);
+
+                var degree = curve.MaxDegree;
+                if (degree == 0) animation.CreateMorphChannel(node, curve.ToStepCurve(), node.MorphWeights.Count, false);
+                if (degree == 1) animation.CreateMorphChannel(node, curve.ToLinearCurve(), node.MorphWeights.Count, true);
+                if (degree == 3) animation.CreateMorphChannel(node, curve.ToSplineCurve(), node.MorphWeights.Count);
+            }
+
+            return node;
+        }
+
         public static Node WithRotationAnimation(this Node node, string animationName, Animations.ICurveSampler<Quaternion> sampler)
         {
             Guard.NotNull(node, nameof(node));