Browse Source

more progress on skinning

Vicente Penades 6 years ago
parent
commit
8a5cb2fa3e

+ 29 - 9
examples/InfiniteSkinnedTentacle/Program.cs

@@ -16,6 +16,29 @@ namespace InfiniteSkinnedTentacle
     {
     {
         // Skinning use cases and examples: https://github.com/KhronosGroup/glTF/issues/1403
         // Skinning use cases and examples: https://github.com/KhronosGroup/glTF/issues/1403
 
 
+        // hierarchy created:
+
+        // Mesh1
+        // Skin1─> Armature1
+        // Skin2─> Armature2
+        // Skin3─> Armature3
+        // Scene
+        // ├── Armature1
+        // │   ├── Bone1
+        // │   ├── Bone2
+        // │   └── Bone3        
+        // ├── SkinnedMesh1─> Mesh1, Skin1
+        // ├── Armature2
+        // │   ├── Bone1
+        // │   ├── Bone2
+        // │   └── Bone3
+        // ├── SkinnedMesh2─> Mesh1, Skin2
+        // ├── Armature3
+        // │   ├── Bone1
+        // │   ├── Bone2
+        // │   └── Bone3
+        // └── SkinnedMesh3─> Mesh1, Skin3
+
         private static readonly Random _Randomizer = new Random(17);
         private static readonly Random _Randomizer = new Random(17);
 
 
         static void Main(string[] args)
         static void Main(string[] args)
@@ -60,17 +83,14 @@ namespace InfiniteSkinnedTentacle
                 bindings.Add(bone);                
                 bindings.Add(bone);                
             }
             }
 
 
-            var skin = scene.LogicalParent.CreateSkin();            
-            skin.BindJoints(skeleton, bindings.ToArray());
-
             scene.CreateNode()
             scene.CreateNode()
                 .WithMesh(mesh)
                 .WithMesh(mesh)
-                .WithSkin(skin);
+                .WithSkinBinding(bindings.ToArray());
         }
         }
 
 
         static MESH CreateMesh(int boneCount)
         static MESH CreateMesh(int boneCount)
         {
         {
-            var mesh = VERTEX.CreateCompatibleMesh("skinned mesh");
+            var mesh = new MESH("skinned mesh");
             var prim = mesh.UsePrimitive(new SharpGLTF.Materials.MaterialBuilder("Default"));
             var prim = mesh.UsePrimitive(new SharpGLTF.Materials.MaterialBuilder("Default"));
 
 
             var a0 = default(VERTEX);
             var a0 = default(VERTEX);
@@ -80,10 +100,10 @@ namespace InfiniteSkinnedTentacle
 
 
             for (int i = 0; i < boneCount; ++i)
             for (int i = 0; i < boneCount; ++i)
             {
             {
-                VERTEX b0 = new VERTEX(new Vector3(-5, i * 10, -5), Vector4.One, (i, 1));
-                VERTEX b1 = new VERTEX(new Vector3(+5, i * 10, -5), Vector4.One, (i, 1));
-                VERTEX b2 = new VERTEX(new Vector3(+5, i * 10, +5), Vector4.One, (i, 1));
-                VERTEX b3 = new VERTEX(new Vector3(-5, i * 10, +5), Vector4.One, (i, 1));
+                var b0 = new VERTEX(new Vector3(-5, i * 10, -5), Vector4.One, (i, 1));
+                var b1 = new VERTEX(new Vector3(+5, i * 10, -5), Vector4.One, (i, 1));
+                var b2 = new VERTEX(new Vector3(+5, i * 10, +5), Vector4.One, (i, 1));
+                var b3 = new VERTEX(new Vector3(-5, i * 10, +5), Vector4.One, (i, 1));
 
 
                 if (i > 0)
                 if (i > 0)
                 {
                 {

+ 21 - 8
src/SharpGLTF.Core/Schema2/gltf.Node.cs

@@ -43,6 +43,11 @@ namespace SharpGLTF.Schema2
         /// </summary>
         /// </summary>
         public Node VisualParent => this.LogicalParent._FindVisualParentNode(this);
         public Node VisualParent => this.LogicalParent._FindVisualParentNode(this);
 
 
+        /// <summary>
+        /// Gets the visual root <see cref="Node"/> instance that contains this <see cref="Node"/>.
+        /// </summary>
+        public Node VisualRoot => this.LogicalParent._FindVisualRootNode(this);
+
         /// <summary>
         /// <summary>
         /// Gets the visual root <see cref="Scene"/> instance that contains this <see cref="Node"/>.
         /// Gets the visual root <see cref="Scene"/> instance that contains this <see cref="Node"/>.
         /// </summary>
         /// </summary>
@@ -50,8 +55,7 @@ namespace SharpGLTF.Schema2
         {
         {
             get
             get
             {
             {
-                var rootNode = this;
-                while (rootNode.VisualParent != null) rootNode = rootNode.VisualParent;
+                var rootNode = this.VisualRoot;
                 return LogicalParent.LogicalScenes.FirstOrDefault(item => item._ContainsVisualNode(rootNode, false));
                 return LogicalParent.LogicalScenes.FirstOrDefault(item => item._ContainsVisualNode(rootNode, false));
             }
             }
         }
         }
@@ -66,6 +70,11 @@ namespace SharpGLTF.Schema2
         /// </summary>
         /// </summary>
         public Boolean IsSkinJoint => Skin.FindSkinsUsingJoint(this).Any();
         public Boolean IsSkinJoint => Skin.FindSkinsUsingJoint(this).Any();
 
 
+        /// <summary>
+        /// Gets a value indicating whether this node is used as a Skeleton node in a <see cref="Skin"/>.
+        /// </summary>
+        public Boolean IsSkinSkeleton => Skin.FindSkinsUsingSkeleton(this).Any();
+
         #endregion
         #endregion
 
 
         #region properties - transform
         #region properties - transform
@@ -334,6 +343,16 @@ namespace SharpGLTF.Schema2
             return _nodes.FirstOrDefault(item => item._HasVisualChild(childIdx));
             return _nodes.FirstOrDefault(item => item._HasVisualChild(childIdx));
         }
         }
 
 
+        internal Node _FindVisualRootNode(Node childNode)
+        {
+            while (true)
+            {
+                var parent = childNode.VisualParent;
+                if (parent == null) return childNode;
+                childNode = parent;
+            }
+        }
+
         internal Node _CreateLogicalNode()
         internal Node _CreateLogicalNode()
         {
         {
             var n = new Node();
             var n = new Node();
@@ -347,11 +366,5 @@ namespace SharpGLTF.Schema2
             children.Add(n.LogicalIndex);
             children.Add(n.LogicalIndex);
             return n;
             return n;
         }
         }
-
-        internal Boolean _CheckNodeIsJoint(Node n)
-        {
-            var idx = n.LogicalIndex;
-            return _skins.Any(s => s._ContainsJoint(idx));
-        }
     }
     }
 }
 }

+ 26 - 5
src/SharpGLTF.Core/Schema2/gltf.Skin.cs

@@ -64,14 +64,30 @@ namespace SharpGLTF.Schema2
         /// </summary>
         /// </summary>
         /// <param name="jointNode">A <see cref="Node"/> joint.</param>
         /// <param name="jointNode">A <see cref="Node"/> joint.</param>
         /// <returns>A collection of <see cref="Skin"/> instances.</returns>
         /// <returns>A collection of <see cref="Skin"/> instances.</returns>
-        public static IEnumerable<Skin> FindSkinsUsingJoint(Node jointNode)
+        internal static IEnumerable<Skin> FindSkinsUsingJoint(Node jointNode)
         {
         {
             var idx = jointNode.LogicalIndex;
             var idx = jointNode.LogicalIndex;
 
 
-            return jointNode.LogicalParent.LogicalSkins.Where(s => s._ContainsJoint(idx));
+            return jointNode
+                .LogicalParent
+                .LogicalSkins
+                .Where(s => s._joints.Contains(idx));
         }
         }
 
 
-        internal bool _ContainsJoint(int nodeIdx) { return _joints.Contains(nodeIdx); }
+        /// <summary>
+        /// Finds all the skins that are using the given <see cref="Node"/> as a skeleton.
+        /// </summary>
+        /// <param name="skeletonNode">A <see cref="Node"/> skeleton.</param>
+        /// <returns>A collection of <see cref="Skin"/> instances.</returns>
+        internal static IEnumerable<Skin> FindSkinsUsingSkeleton(Node skeletonNode)
+        {
+            var idx = skeletonNode.LogicalIndex;
+
+            return skeletonNode
+                .LogicalParent
+                .LogicalSkins
+                .Where(s => s._skeleton == idx);
+        }
 
 
         public Accessor GetInverseBindMatricesAccessor()
         public Accessor GetInverseBindMatricesAccessor()
         {
         {
@@ -133,11 +149,16 @@ namespace SharpGLTF.Schema2
             return true;
             return true;
         }
         }
 
 
-        public void BindJoints(Node skeleton, params Node[] joints)
+        public void BindJoints(params Node[] joints)
         {
         {
-            Guard.MustShareLogicalParent(this, skeleton, nameof(skeleton));
             foreach (var j in joints) Guard.MustShareLogicalParent(this, j, nameof(joints));
             foreach (var j in joints) Guard.MustShareLogicalParent(this, j, nameof(joints));
 
 
+            var rootJoint = joints.Select(item => item.VisualRoot).Distinct().ToList();
+
+            Guard.IsTrue(rootJoint.Count == 1, nameof(joints), "All joints must have only one root parent");
+
+            var skeleton = rootJoint[0];
+
             this.Skeleton = skeleton;
             this.Skeleton = skeleton;
 
 
             Matrix4x4.Invert(skeleton.WorldMatrix, out Matrix4x4 skeletonBindXform);
             Matrix4x4.Invert(skeleton.WorldMatrix, out Matrix4x4 skeletonBindXform);

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

@@ -57,7 +57,7 @@ namespace SharpGLTF.Geometry
 
 
             for (int i = 0; i < bindings.Length; ++i)
             for (int i = 0; i < bindings.Length; ++i)
             {
             {
-                Skinning.SetBoneBinding(i, bindings[i].Item1, bindings[i].Item2);
+                Skinning.SetJointBinding(i, bindings[i].Item1, bindings[i].Item2);
             }
             }
         }
         }
 
 

+ 8 - 8
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexColumns.cs

@@ -83,10 +83,10 @@ namespace SharpGLTF.Geometry.VertexTypes
                 var j = Joints0[index];
                 var j = Joints0[index];
                 var w = Weights0[index];
                 var w = Weights0[index];
 
 
-                jjjj.SetBoneBinding(0, (int)j.X, w.X);
-                jjjj.SetBoneBinding(1, (int)j.Y, w.Y);
-                jjjj.SetBoneBinding(2, (int)j.Z, w.Z);
-                jjjj.SetBoneBinding(3, (int)j.W, w.W);
+                jjjj.SetJointBinding(0, (int)j.X, w.X);
+                jjjj.SetJointBinding(1, (int)j.Y, w.Y);
+                jjjj.SetJointBinding(2, (int)j.Z, w.Z);
+                jjjj.SetJointBinding(3, (int)j.W, w.W);
             }
             }
 
 
             if (Joints1 != null && Weights1 != null)
             if (Joints1 != null && Weights1 != null)
@@ -94,10 +94,10 @@ namespace SharpGLTF.Geometry.VertexTypes
                 var j = Joints1[index];
                 var j = Joints1[index];
                 var w = Weights1[index];
                 var w = Weights1[index];
 
 
-                jjjj.SetBoneBinding(4, (int)j.X, w.X);
-                jjjj.SetBoneBinding(5, (int)j.Y, w.Y);
-                jjjj.SetBoneBinding(6, (int)j.Z, w.Z);
-                jjjj.SetBoneBinding(7, (int)j.W, w.W);
+                jjjj.SetJointBinding(4, (int)j.X, w.X);
+                jjjj.SetJointBinding(5, (int)j.Y, w.Y);
+                jjjj.SetJointBinding(6, (int)j.Z, w.Z);
+                jjjj.SetJointBinding(7, (int)j.W, w.W);
             }
             }
 
 
             return jjjj;
             return jjjj;

+ 3 - 3
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexEmpty.cs

@@ -25,10 +25,10 @@ namespace SharpGLTF.Geometry.VertexTypes
 
 
         Vector2 IVertexMaterial.GetTexCoord(int index) { throw new NotSupportedException(); }
         Vector2 IVertexMaterial.GetTexCoord(int index) { throw new NotSupportedException(); }
 
 
-        void IVertexSkinning.SetBoneBinding(int index, int joint, float weight) { }
+        void IVertexSkinning.SetJointBinding(int index, int joint, float weight) { }
 
 
-        BoneBinding IVertexSkinning.GetBoneBinding(int index) { throw new NotSupportedException(); }
+        JointBinding IVertexSkinning.GetJointBinding(int index) { throw new NotSupportedException(); }
 
 
-        public IEnumerable<BoneBinding> BoneBindings => Enumerable.Empty<BoneBinding>();
+        public IEnumerable<JointBinding> JointBindings => Enumerable.Empty<JointBinding>();
     }
     }
 }
 }

+ 68 - 68
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexSkinning.cs

@@ -9,20 +9,20 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// Represents a a Node Joint index and its weight in a skinning system.
     /// Represents a a Node Joint index and its weight in a skinning system.
     /// </summary>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("{Joint} = {Weight}")]
     [System.Diagnostics.DebuggerDisplay("{Joint} = {Weight}")]
-    public struct BoneBinding : IComparable<BoneBinding>
+    public struct JointBinding : IComparable<JointBinding>
     {
     {
         #region constructors
         #region constructors
 
 
-        public BoneBinding(int joint, float weight)
+        public JointBinding(int joint, float weight)
         {
         {
             this.Joint = joint;
             this.Joint = joint;
             this.Weight = weight;
             this.Weight = weight;
             if (Weight == 0) Joint = 0;
             if (Weight == 0) Joint = 0;
         }
         }
 
 
-        public static implicit operator BoneBinding((int, float) jw)
+        public static implicit operator JointBinding((int, float) jw)
         {
         {
-            return new BoneBinding(jw.Item1, jw.Item2);
+            return new JointBinding(jw.Item1, jw.Item2);
         }
         }
 
 
         #endregion
         #endregion
@@ -36,7 +36,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
 
         #region API
         #region API
 
 
-        public int CompareTo(BoneBinding other)
+        public int CompareTo(JointBinding other)
         {
         {
             var a = this.Weight.CompareTo(other.Weight);
             var a = this.Weight.CompareTo(other.Weight);
             if (a != 0) return a;
             if (a != 0) return a;
@@ -44,7 +44,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             return this.Joint.CompareTo(other.Joint);
             return this.Joint.CompareTo(other.Joint);
         }
         }
 
 
-        internal static void InPlaceReverseBubbleSort(Span<BoneBinding> span)
+        internal static void InPlaceReverseBubbleSort(Span<JointBinding> span)
         {
         {
             for (int i = 1; i < span.Length; ++i)
             for (int i = 1; i < span.Length; ++i)
             {
             {
@@ -68,10 +68,10 @@ namespace SharpGLTF.Geometry.VertexTypes
         /// <summary>
         /// <summary>
         /// Calculates the scale to use on the first <paramref name="count"/> weights.
         /// Calculates the scale to use on the first <paramref name="count"/> weights.
         /// </summary>
         /// </summary>
-        /// <param name="span">A collection of <see cref="BoneBinding"/>.</param>
+        /// <param name="span">A collection of <see cref="JointBinding"/>.</param>
         /// <param name="count">The number of items to take from the beginning of <paramref name="span"/>.</param>
         /// <param name="count">The number of items to take from the beginning of <paramref name="span"/>.</param>
         /// <returns>A Scale factor.</returns>
         /// <returns>A Scale factor.</returns>
-        internal static float CalculateScaleFor(Span<BoneBinding> span, int count)
+        internal static float CalculateScaleFor(Span<JointBinding> span, int count)
         {
         {
             System.Diagnostics.Debug.Assert(count < span.Length, nameof(count));
             System.Diagnostics.Debug.Assert(count < span.Length, nameof(count));
 
 
@@ -88,11 +88,11 @@ namespace SharpGLTF.Geometry.VertexTypes
             return ww / w;
             return ww / w;
         }
         }
 
 
-        public static IEnumerable<BoneBinding> GetBindings(IVertexSkinning vs)
+        public static IEnumerable<JointBinding> GetBindings(IVertexSkinning vs)
         {
         {
             for (int i = 0; i < vs.MaxBindings; ++i)
             for (int i = 0; i < vs.MaxBindings; ++i)
             {
             {
-                var jw = vs.GetBoneBinding(i);
+                var jw = vs.GetJointBinding(i);
                 if (jw.Weight != 0) yield return jw;
                 if (jw.Weight != 0) yield return jw;
             }
             }
         }
         }
@@ -111,11 +111,11 @@ namespace SharpGLTF.Geometry.VertexTypes
         // - 0 weight joints point to joint 0
         // - 0 weight joints point to joint 0
         void Validate();
         void Validate();
 
 
-        BoneBinding GetBoneBinding(int index);
+        JointBinding GetJointBinding(int index);
 
 
-        void SetBoneBinding(int index, int joint, float weight);
+        void SetJointBinding(int index, int joint, float weight);
 
 
-        IEnumerable<BoneBinding> BoneBindings { get; }
+        IEnumerable<JointBinding> JointBindings { get; }
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -131,7 +131,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             Weights = Vector4.UnitX;
             Weights = Vector4.UnitX;
         }
         }
 
 
-        public VertexJoints8x4(BoneBinding a, BoneBinding b)
+        public VertexJoints8x4(JointBinding a, JointBinding b)
         {
         {
             Joints = new Vector4(a.Joint, b.Joint, 0, 0);
             Joints = new Vector4(a.Joint, b.Joint, 0, 0);
             Weights = new Vector4(a.Weight, b.Weight, 0, 0);
             Weights = new Vector4(a.Weight, b.Weight, 0, 0);
@@ -139,7 +139,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             InPlaceSort();
             InPlaceSort();
         }
         }
 
 
-        public VertexJoints8x4(BoneBinding a, BoneBinding b, BoneBinding c)
+        public VertexJoints8x4(JointBinding a, JointBinding b, JointBinding c)
         {
         {
             Joints = new Vector4(a.Joint, b.Joint, c.Joint, 0);
             Joints = new Vector4(a.Joint, b.Joint, c.Joint, 0);
             Weights = new Vector4(a.Weight, b.Weight, c.Weight, 0);
             Weights = new Vector4(a.Weight, b.Weight, c.Weight, 0);
@@ -147,7 +147,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             InPlaceSort();
             InPlaceSort();
         }
         }
 
 
-        public VertexJoints8x4(BoneBinding a, BoneBinding b, BoneBinding c, BoneBinding d)
+        public VertexJoints8x4(JointBinding a, JointBinding b, JointBinding c, JointBinding d)
         {
         {
             Joints = new Vector4(a.Joint, b.Joint, c.Joint, d.Joint);
             Joints = new Vector4(a.Joint, b.Joint, c.Joint, d.Joint);
             Weights = new Vector4(a.Weight, b.Weight, c.Weight, d.Weight);
             Weights = new Vector4(a.Weight, b.Weight, c.Weight, d.Weight);
@@ -162,7 +162,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
 
             for (int i = 0; i < bindings.Length; ++i)
             for (int i = 0; i < bindings.Length; ++i)
             {
             {
-                this.SetBoneBinding(i, bindings[i].Item1, bindings[i].Item2);
+                this.SetJointBinding(i, bindings[i].Item1, bindings[i].Item2);
             }
             }
         }
         }
 
 
@@ -190,19 +190,19 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (!Weights._IsReal()) throw new NotFiniteNumberException(nameof(Weights));
             if (!Weights._IsReal()) throw new NotFiniteNumberException(nameof(Weights));
         }
         }
 
 
-        public BoneBinding GetBoneBinding(int index)
+        public JointBinding GetJointBinding(int index)
         {
         {
             switch (index)
             switch (index)
             {
             {
-                case 0: return new BoneBinding((int)this.Joints.X, this.Weights.X);
-                case 1: return new BoneBinding((int)this.Joints.Y, this.Weights.Y);
-                case 2: return new BoneBinding((int)this.Joints.Z, this.Weights.Z);
-                case 3: return new BoneBinding((int)this.Joints.W, this.Weights.W);
+                case 0: return new JointBinding((int)this.Joints.X, this.Weights.X);
+                case 1: return new JointBinding((int)this.Joints.Y, this.Weights.Y);
+                case 2: return new JointBinding((int)this.Joints.Z, this.Weights.Z);
+                case 3: return new JointBinding((int)this.Joints.W, this.Weights.W);
                 default: throw new ArgumentOutOfRangeException(nameof(index));
                 default: throw new ArgumentOutOfRangeException(nameof(index));
             }
             }
         }
         }
 
 
-        public void SetBoneBinding(int index, int joint, float weight)
+        public void SetJointBinding(int index, int joint, float weight)
         {
         {
             switch (index)
             switch (index)
             {
             {
@@ -216,20 +216,20 @@ namespace SharpGLTF.Geometry.VertexTypes
 
 
         public void InPlaceSort()
         public void InPlaceSort()
         {
         {
-            Span<BoneBinding> pairs = stackalloc BoneBinding[4];
+            Span<JointBinding> pairs = stackalloc JointBinding[4];
 
 
-            pairs[0] = new BoneBinding((int)Joints.X, Weights.X);
-            pairs[1] = new BoneBinding((int)Joints.Y, Weights.Y);
-            pairs[2] = new BoneBinding((int)Joints.Z, Weights.Z);
-            pairs[3] = new BoneBinding((int)Joints.W, Weights.W);
+            pairs[0] = new JointBinding((int)Joints.X, Weights.X);
+            pairs[1] = new JointBinding((int)Joints.Y, Weights.Y);
+            pairs[2] = new JointBinding((int)Joints.Z, Weights.Z);
+            pairs[3] = new JointBinding((int)Joints.W, Weights.W);
 
 
-            BoneBinding.InPlaceReverseBubbleSort(pairs);
+            JointBinding.InPlaceReverseBubbleSort(pairs);
 
 
             Joints = new Vector4(pairs[0].Joint, pairs[1].Joint, pairs[2].Joint, pairs[3].Joint);
             Joints = new Vector4(pairs[0].Joint, pairs[1].Joint, pairs[2].Joint, pairs[3].Joint);
             Weights = new Vector4(pairs[0].Weight, pairs[1].Weight, pairs[2].Weight, pairs[3].Weight);
             Weights = new Vector4(pairs[0].Weight, pairs[1].Weight, pairs[2].Weight, pairs[3].Weight);
         }
         }
 
 
-        public IEnumerable<BoneBinding> BoneBindings => BoneBinding.GetBindings(this);
+        public IEnumerable<JointBinding> JointBindings => JointBinding.GetBindings(this);
 
 
         #endregion
         #endregion
     }
     }
@@ -247,7 +247,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             Weights = Vector4.UnitX;
             Weights = Vector4.UnitX;
         }
         }
 
 
-        public VertexJoints16x4(BoneBinding a, BoneBinding b)
+        public VertexJoints16x4(JointBinding a, JointBinding b)
         {
         {
             Joints = new Vector4(a.Joint, b.Joint, 0, 0);
             Joints = new Vector4(a.Joint, b.Joint, 0, 0);
             Weights = new Vector4(a.Weight, b.Weight, 0, 0);
             Weights = new Vector4(a.Weight, b.Weight, 0, 0);
@@ -255,7 +255,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             InPlaceSort();
             InPlaceSort();
         }
         }
 
 
-        public VertexJoints16x4(BoneBinding a, BoneBinding b, BoneBinding c)
+        public VertexJoints16x4(JointBinding a, JointBinding b, JointBinding c)
         {
         {
             Joints = new Vector4(a.Joint, b.Joint, c.Joint, 0);
             Joints = new Vector4(a.Joint, b.Joint, c.Joint, 0);
             Weights = new Vector4(a.Weight, b.Weight, c.Weight, 0);
             Weights = new Vector4(a.Weight, b.Weight, c.Weight, 0);
@@ -263,7 +263,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             InPlaceSort();
             InPlaceSort();
         }
         }
 
 
-        public VertexJoints16x4(BoneBinding a, BoneBinding b, BoneBinding c, BoneBinding d)
+        public VertexJoints16x4(JointBinding a, JointBinding b, JointBinding c, JointBinding d)
         {
         {
             Joints = new Vector4(a.Joint, b.Joint, c.Joint, d.Joint);
             Joints = new Vector4(a.Joint, b.Joint, c.Joint, d.Joint);
             Weights = new Vector4(a.Weight, b.Weight, c.Weight, d.Weight);
             Weights = new Vector4(a.Weight, b.Weight, c.Weight, d.Weight);
@@ -295,19 +295,19 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (!Weights._IsReal()) throw new NotFiniteNumberException(nameof(Weights));
             if (!Weights._IsReal()) throw new NotFiniteNumberException(nameof(Weights));
         }
         }
 
 
-        public BoneBinding GetBoneBinding(int index)
+        public JointBinding GetJointBinding(int index)
         {
         {
             switch (index)
             switch (index)
             {
             {
-                case 0: return new BoneBinding((int)this.Joints.X, this.Weights.X);
-                case 1: return new BoneBinding((int)this.Joints.Y, this.Weights.Y);
-                case 2: return new BoneBinding((int)this.Joints.Z, this.Weights.Z);
-                case 3: return new BoneBinding((int)this.Joints.W, this.Weights.W);
+                case 0: return new JointBinding((int)this.Joints.X, this.Weights.X);
+                case 1: return new JointBinding((int)this.Joints.Y, this.Weights.Y);
+                case 2: return new JointBinding((int)this.Joints.Z, this.Weights.Z);
+                case 3: return new JointBinding((int)this.Joints.W, this.Weights.W);
                 default: throw new ArgumentOutOfRangeException(nameof(index));
                 default: throw new ArgumentOutOfRangeException(nameof(index));
             }
             }
         }
         }
 
 
-        public void SetBoneBinding(int index, int joint, float weight)
+        public void SetJointBinding(int index, int joint, float weight)
         {
         {
             switch (index)
             switch (index)
             {
             {
@@ -321,20 +321,20 @@ namespace SharpGLTF.Geometry.VertexTypes
 
 
         public void InPlaceSort()
         public void InPlaceSort()
         {
         {
-            Span<BoneBinding> pairs = stackalloc BoneBinding[4];
+            Span<JointBinding> pairs = stackalloc JointBinding[4];
 
 
-            pairs[0] = new BoneBinding((int)Joints.X, Weights.X);
-            pairs[1] = new BoneBinding((int)Joints.Y, Weights.Y);
-            pairs[2] = new BoneBinding((int)Joints.Z, Weights.Z);
-            pairs[3] = new BoneBinding((int)Joints.W, Weights.W);
+            pairs[0] = new JointBinding((int)Joints.X, Weights.X);
+            pairs[1] = new JointBinding((int)Joints.Y, Weights.Y);
+            pairs[2] = new JointBinding((int)Joints.Z, Weights.Z);
+            pairs[3] = new JointBinding((int)Joints.W, Weights.W);
 
 
-            BoneBinding.InPlaceReverseBubbleSort(pairs);
+            JointBinding.InPlaceReverseBubbleSort(pairs);
 
 
             Joints = new Vector4(pairs[0].Joint, pairs[1].Joint, pairs[2].Joint, pairs[3].Joint);
             Joints = new Vector4(pairs[0].Joint, pairs[1].Joint, pairs[2].Joint, pairs[3].Joint);
             Weights = new Vector4(pairs[0].Weight, pairs[1].Weight, pairs[2].Weight, pairs[3].Weight);
             Weights = new Vector4(pairs[0].Weight, pairs[1].Weight, pairs[2].Weight, pairs[3].Weight);
         }
         }
 
 
-        public IEnumerable<BoneBinding> BoneBindings => BoneBinding.GetBindings(this);
+        public IEnumerable<JointBinding> JointBindings => JointBinding.GetBindings(this);
 
 
         #endregion
         #endregion
     }
     }
@@ -396,23 +396,23 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (!Weights1._IsReal()) throw new NotFiniteNumberException(nameof(Weights1));
             if (!Weights1._IsReal()) throw new NotFiniteNumberException(nameof(Weights1));
         }
         }
 
 
-        public BoneBinding GetBoneBinding(int index)
+        public JointBinding GetJointBinding(int index)
         {
         {
             switch (index)
             switch (index)
             {
             {
-                case 0: return new BoneBinding((int)this.Joints0.X, this.Weights0.X);
-                case 1: return new BoneBinding((int)this.Joints0.Y, this.Weights0.Y);
-                case 2: return new BoneBinding((int)this.Joints0.Z, this.Weights0.Z);
-                case 3: return new BoneBinding((int)this.Joints0.W, this.Weights0.W);
-                case 4: return new BoneBinding((int)this.Joints1.X, this.Weights1.X);
-                case 5: return new BoneBinding((int)this.Joints1.Y, this.Weights1.Y);
-                case 6: return new BoneBinding((int)this.Joints1.Z, this.Weights1.Z);
-                case 7: return new BoneBinding((int)this.Joints1.W, this.Weights1.W);
+                case 0: return new JointBinding((int)this.Joints0.X, this.Weights0.X);
+                case 1: return new JointBinding((int)this.Joints0.Y, this.Weights0.Y);
+                case 2: return new JointBinding((int)this.Joints0.Z, this.Weights0.Z);
+                case 3: return new JointBinding((int)this.Joints0.W, this.Weights0.W);
+                case 4: return new JointBinding((int)this.Joints1.X, this.Weights1.X);
+                case 5: return new JointBinding((int)this.Joints1.Y, this.Weights1.Y);
+                case 6: return new JointBinding((int)this.Joints1.Z, this.Weights1.Z);
+                case 7: return new JointBinding((int)this.Joints1.W, this.Weights1.W);
                 default: throw new ArgumentOutOfRangeException(nameof(index));
                 default: throw new ArgumentOutOfRangeException(nameof(index));
             }
             }
         }
         }
 
 
-        public void SetBoneBinding(int index, int joint, float weight)
+        public void SetJointBinding(int index, int joint, float weight)
         {
         {
             switch (index)
             switch (index)
             {
             {
@@ -428,7 +428,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             }
             }
         }
         }
 
 
-        public IEnumerable<BoneBinding> BoneBindings => BoneBinding.GetBindings(this);
+        public IEnumerable<JointBinding> JointBindings => JointBinding.GetBindings(this);
 
 
         #endregion
         #endregion
     }
     }
@@ -490,23 +490,23 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (!Weights1._IsReal()) throw new NotFiniteNumberException(nameof(Weights1));
             if (!Weights1._IsReal()) throw new NotFiniteNumberException(nameof(Weights1));
         }
         }
 
 
-        public BoneBinding GetBoneBinding(int index)
+        public JointBinding GetJointBinding(int index)
         {
         {
             switch (index)
             switch (index)
             {
             {
-                case 0: return new BoneBinding((int)this.Joints0.X, this.Weights0.X);
-                case 1: return new BoneBinding((int)this.Joints0.Y, this.Weights0.Y);
-                case 2: return new BoneBinding((int)this.Joints0.Z, this.Weights0.Z);
-                case 3: return new BoneBinding((int)this.Joints0.W, this.Weights0.W);
-                case 4: return new BoneBinding((int)this.Joints1.X, this.Weights1.X);
-                case 5: return new BoneBinding((int)this.Joints1.Y, this.Weights1.Y);
-                case 6: return new BoneBinding((int)this.Joints1.Z, this.Weights1.Z);
-                case 7: return new BoneBinding((int)this.Joints1.W, this.Weights1.W);
+                case 0: return new JointBinding((int)this.Joints0.X, this.Weights0.X);
+                case 1: return new JointBinding((int)this.Joints0.Y, this.Weights0.Y);
+                case 2: return new JointBinding((int)this.Joints0.Z, this.Weights0.Z);
+                case 3: return new JointBinding((int)this.Joints0.W, this.Weights0.W);
+                case 4: return new JointBinding((int)this.Joints1.X, this.Weights1.X);
+                case 5: return new JointBinding((int)this.Joints1.Y, this.Weights1.Y);
+                case 6: return new JointBinding((int)this.Joints1.Z, this.Weights1.Z);
+                case 7: return new JointBinding((int)this.Joints1.W, this.Weights1.W);
                 default: throw new ArgumentOutOfRangeException(nameof(index));
                 default: throw new ArgumentOutOfRangeException(nameof(index));
             }
             }
         }
         }
 
 
-        public void SetBoneBinding(int index, int joint, float weight)
+        public void SetJointBinding(int index, int joint, float weight)
         {
         {
             switch (index)
             switch (index)
             {
             {
@@ -522,7 +522,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             }
             }
         }
         }
 
 
-        public IEnumerable<BoneBinding> BoneBindings => BoneBinding.GetBindings(this);
+        public IEnumerable<JointBinding> JointBindings => JointBinding.GetBindings(this);
 
 
         #endregion
         #endregion
     }
     }

+ 7 - 7
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexUtils.cs

@@ -272,9 +272,9 @@ namespace SharpGLTF.Geometry.VertexTypes
             {
             {
                 for (int i = 0; i < src.MaxBindings; ++i)
                 for (int i = 0; i < src.MaxBindings; ++i)
                 {
                 {
-                    var jw = src.GetBoneBinding(i);
+                    var jw = src.GetJointBinding(i);
 
 
-                    dst.SetBoneBinding(i, jw.Joint, jw.Weight);
+                    dst.SetJointBinding(i, jw.Joint, jw.Weight);
                 }
                 }
 
 
                 return dst;
                 return dst;
@@ -282,20 +282,20 @@ namespace SharpGLTF.Geometry.VertexTypes
 
 
             // if there's more source joints than destination joints, transfer with scale
             // if there's more source joints than destination joints, transfer with scale
 
 
-            Span<BoneBinding> srcjw = stackalloc BoneBinding[src.MaxBindings];
+            Span<JointBinding> srcjw = stackalloc JointBinding[src.MaxBindings];
 
 
             for (int i = 0; i < src.MaxBindings; ++i)
             for (int i = 0; i < src.MaxBindings; ++i)
             {
             {
-                srcjw[i] = src.GetBoneBinding(i);
+                srcjw[i] = src.GetJointBinding(i);
             }
             }
 
 
-            BoneBinding.InPlaceReverseBubbleSort(srcjw);
+            JointBinding.InPlaceReverseBubbleSort(srcjw);
 
 
-            var w = BoneBinding.CalculateScaleFor(srcjw, dst.MaxBindings);
+            var w = JointBinding.CalculateScaleFor(srcjw, dst.MaxBindings);
 
 
             for (int i = 0; i < dst.MaxBindings; ++i)
             for (int i = 0; i < dst.MaxBindings; ++i)
             {
             {
-                dst.SetBoneBinding(i, srcjw[i].Joint, srcjw[i].Weight * w);
+                dst.SetJointBinding(i, srcjw[i].Joint, srcjw[i].Weight * w);
             }
             }
 
 
             return dst;
             return dst;

+ 9 - 0
src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs

@@ -49,6 +49,15 @@ namespace SharpGLTF.Schema2
             return node;
             return node;
         }
         }
 
 
+        public static Node WithSkinBinding(this Node node, params Node[] joints)
+        {
+            var skin = node.LogicalParent.CreateSkin();
+            skin.BindJoints(joints);
+
+            node.Skin = skin;
+            return node;
+        }
+
         #endregion
         #endregion
 
 
         #region evaluation
         #region evaluation

+ 7 - 7
tests/SharpGLTF.Tests/Geometry/VertexTypes/JointWeightPairTests.cs

@@ -15,15 +15,15 @@ namespace SharpGLTF.Geometry.VertexTypes
         {
         {
             var pairs = new[]
             var pairs = new[]
             {
             {
-                new BoneBinding(2,0),
-                new BoneBinding(1,0.20f),
-                new BoneBinding(3,0.15f),
-                new BoneBinding(2,0.25f),
-                new BoneBinding(4,0),
-                new BoneBinding(7,0.40f)
+                new JointBinding(2,0),
+                new JointBinding(1,0.20f),
+                new JointBinding(3,0.15f),
+                new JointBinding(2,0.25f),
+                new JointBinding(4,0),
+                new JointBinding(7,0.40f)
             };
             };
 
 
-            BoneBinding.InPlaceReverseBubbleSort(pairs);
+            JointBinding.InPlaceReverseBubbleSort(pairs);
 
 
             Assert.AreEqual(7, pairs[0].Joint);
             Assert.AreEqual(7, pairs[0].Joint);
             Assert.AreEqual(2, pairs[1].Joint);
             Assert.AreEqual(2, pairs[1].Joint);

+ 13 - 13
tests/SharpGLTF.Tests/Geometry/VertexTypes/VertexSkinningTests.cs

@@ -14,23 +14,23 @@ namespace SharpGLTF.Geometry.VertexTypes
         public void TestCloneAs()
         public void TestCloneAs()
         {
         {
             var v8 = new VertexJoints8x8();
             var v8 = new VertexJoints8x8();
-            v8.SetBoneBinding(0, 1, 0.2f);
-            v8.SetBoneBinding(1, 2, 0.15f);
-            v8.SetBoneBinding(2, 3, 0.25f);
-            v8.SetBoneBinding(3, 4, 0.10f);
-            v8.SetBoneBinding(4, 5, 0.30f);
+            v8.SetJointBinding(0, 1, 0.2f);
+            v8.SetJointBinding(1, 2, 0.15f);
+            v8.SetJointBinding(2, 3, 0.25f);
+            v8.SetJointBinding(3, 4, 0.10f);
+            v8.SetJointBinding(4, 5, 0.30f);
 
 
             var v4 = v8.ConvertTo<VertexJoints8x4>();
             var v4 = v8.ConvertTo<VertexJoints8x4>();
 
 
-            Assert.AreEqual(5, v4.GetBoneBinding(0).Joint);
-            Assert.AreEqual(3, v4.GetBoneBinding(1).Joint);
-            Assert.AreEqual(1, v4.GetBoneBinding(2).Joint);
-            Assert.AreEqual(2, v4.GetBoneBinding(3).Joint);
+            Assert.AreEqual(5, v4.GetJointBinding(0).Joint);
+            Assert.AreEqual(3, v4.GetJointBinding(1).Joint);
+            Assert.AreEqual(1, v4.GetJointBinding(2).Joint);
+            Assert.AreEqual(2, v4.GetJointBinding(3).Joint);
 
 
-            Assert.AreEqual(0.333333f, v4.GetBoneBinding(0).Weight, 0.01f);
-            Assert.AreEqual(0.277777f, v4.GetBoneBinding(1).Weight, 0.01f);
-            Assert.AreEqual(0.222222f, v4.GetBoneBinding(2).Weight, 0.01f);
-            Assert.AreEqual(0.166666f, v4.GetBoneBinding(3).Weight, 0.01f);
+            Assert.AreEqual(0.333333f, v4.GetJointBinding(0).Weight, 0.01f);
+            Assert.AreEqual(0.277777f, v4.GetJointBinding(1).Weight, 0.01f);
+            Assert.AreEqual(0.222222f, v4.GetJointBinding(2).Weight, 0.01f);
+            Assert.AreEqual(0.166666f, v4.GetJointBinding(3).Weight, 0.01f);
         }
         }
     }
     }
 }
 }

+ 1 - 1
tests/SharpGLTF.Tests/Schema2/Authoring/MeshBuilderCreationTests.cs

@@ -200,7 +200,7 @@ namespace SharpGLTF.Schema2.Authoring
             // setup skin
             // setup skin
             var snode = scene.CreateNode("Skeleton Node");
             var snode = scene.CreateNode("Skeleton Node");
             snode.Skin = model.CreateSkin();            
             snode.Skin = model.CreateSkin();            
-            snode.Skin.BindJoints(skelet, joint1, joint2, joint3);
+            snode.Skin.BindJoints(joint1, joint2, joint3);
 
 
             snode.WithMesh( model.CreateMesh(meshBuilder) );
             snode.WithMesh( model.CreateMesh(meshBuilder) );
 
 

+ 2 - 1
tests/SharpGLTF.Tests/Utils.cs

@@ -83,7 +83,8 @@ namespace SharpGLTF
         {
         {
             context.AttachLink("🌍 Khronos Validator", "http://github.khronos.org/glTF-Validator/");
             context.AttachLink("🌍 Khronos Validator", "http://github.khronos.org/glTF-Validator/");
             context.AttachLink("🌍 BabylonJS Sandbox", "https://sandbox.babylonjs.com/");
             context.AttachLink("🌍 BabylonJS Sandbox", "https://sandbox.babylonjs.com/");
-            context.AttachLink("🌍 Don McCurdy Sandbox", "https://gltf-viewer.donmccurdy.com/");            
+            context.AttachLink("🌍 Don McCurdy Sandbox", "https://gltf-viewer.donmccurdy.com/");
+            context.AttachLink("🌍 VirtualGIS Cesium Sandbox", "https://www.virtualgis.io/gltfviewer/");
         }
         }
 
 
         public static void AttachLink(this TestContext context, string linkPath, string targetPath)
         public static void AttachLink(this TestContext context, string linkPath, string targetPath)