Ver código fonte

CreateMeshes now automatically checks for optimal vertex joint index encoding, so no need for so many VertexSkinning types anymore.

Vicente Penades 6 anos atrás
pai
commit
82da3913eb

+ 2 - 2
examples/InfiniteSkinnedTentacle/Program.cs

@@ -9,8 +9,8 @@ using SharpGLTF.Geometry.VertexTypes;
 
 namespace InfiniteSkinnedTentacle
 {
-    using VERTEX = VertexBuilder<VertexPosition, VertexColor1, VertexJoints8x4>;
-    using MESH = MeshBuilder<VertexPosition, VertexColor1, VertexJoints8x4>;
+    using VERTEX = VertexBuilder<VertexPosition, VertexColor1, VertexJoints4>;
+    using MESH = MeshBuilder<VertexPosition, VertexColor1, VertexJoints4>;
 
     class Program
     {

+ 18 - 0
src/SharpGLTF.Core/Transforms/SparseWeight8.cs

@@ -296,6 +296,8 @@ namespace SharpGLTF.Transforms
 
         public float WeightSum => Weight0 + Weight1 + Weight2 + Weight3 + Weight4 + Weight5 + Weight6 + Weight7;
 
+        public int MaxIndex => _GetMaxIndex();
+
         #endregion
 
         #region API
@@ -642,6 +644,22 @@ namespace SharpGLTF.Transforms
             return new SparseWeight8(weights);
         }
 
+        internal int _GetMaxIndex()
+        {
+            int idx = 0;
+
+            if (Weight0 != 0) idx = Math.Max(idx, Index0);
+            if (Weight1 != 0) idx = Math.Max(idx, Index1);
+            if (Weight2 != 0) idx = Math.Max(idx, Index2);
+            if (Weight3 != 0) idx = Math.Max(idx, Index3);
+            if (Weight4 != 0) idx = Math.Max(idx, Index4);
+            if (Weight5 != 0) idx = Math.Max(idx, Index5);
+            if (Weight6 != 0) idx = Math.Max(idx, Index6);
+            if (Weight7 != 0) idx = Math.Max(idx, Index7);
+
+            return idx;
+        }
+
         #endregion
     }
 }

+ 4 - 4
src/SharpGLTF.Toolkit/Geometry/MeshBuilder.cs

@@ -36,8 +36,8 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexEmpty"/>,
     /// <see cref="VertexJoints8x4"/>,
     /// <see cref="VertexJoints8x8"/>,
-    /// <see cref="VertexJoints16x4"/>,
-    /// <see cref="VertexJoints16x8"/>.
+    /// <see cref="VertexJoints4"/>,
+    /// <see cref="VertexJoints8"/>.
     /// </typeparam>
     public class MeshBuilder<TMaterial, TvG, TvM, TvS> : IMeshBuilder<TMaterial>
         where TvG : struct, IVertexGeometry
@@ -177,8 +177,8 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexEmpty"/>,
     /// <see cref="VertexJoints8x4"/>,
     /// <see cref="VertexJoints8x8"/>,
-    /// <see cref="VertexJoints16x4"/>,
-    /// <see cref="VertexJoints16x8"/>.
+    /// <see cref="VertexJoints4"/>,
+    /// <see cref="VertexJoints8"/>.
     /// </typeparam>
     public class MeshBuilder<TvG, TvM, TvS> : MeshBuilder<Materials.MaterialBuilder, TvG, TvM, TvS>
         where TvG : struct, IVertexGeometry

+ 12 - 0
src/SharpGLTF.Toolkit/Geometry/MeshBuilderToolkit.cs

@@ -47,6 +47,18 @@ namespace SharpGLTF.Geometry
             return maxIndex < 65535 ? Schema2.EncodingType.UNSIGNED_SHORT : Schema2.EncodingType.UNSIGNED_INT;
         }
 
+        public static Schema2.EncodingType GetOptimalJointEncoding<TMaterial>(this IEnumerable<IMeshBuilder<TMaterial>> meshes)
+        {
+            var indices = meshes
+                .SelectMany(item => item.Primitives)
+                .SelectMany(item => item.Vertices)
+                .Select(item => item.GetSkinning().GetWeights().MaxIndex);
+
+            var maxIndex = indices.Any() ? indices.Max() : 0;
+
+            return maxIndex < 256 ? Schema2.EncodingType.UNSIGNED_BYTE : Schema2.EncodingType.UNSIGNED_SHORT;
+        }
+
         public static IMeshBuilder<TMaterial> CreateMeshBuilderFromVertexAttributes<TMaterial>(params string[] vertexAttributes)
         {
             Type meshType = GetMeshBuilderType(typeof(TMaterial), vertexAttributes);

+ 3 - 2
src/SharpGLTF.Toolkit/Geometry/PackedMeshBuilder.cs

@@ -27,6 +27,7 @@ namespace SharpGLTF.Geometry
                 throw new ArgumentException(ex.Message, nameof(meshBuilders), ex);
             }
 
+            var jointEncoding = meshBuilders.GetOptimalJointEncoding();
             var indexEncoding = meshBuilders.GetOptimalIndexEncoding();
 
             foreach (var srcMesh in meshBuilders)
@@ -42,8 +43,8 @@ namespace SharpGLTF.Geometry
                     bool useStrided = prefferStrided;
                     if (srcPrim.MorphTargets.TargetsCount > 0) useStrided = false;
 
-                    if (useStrided) dstPrim.SetStridedVertices(srcPrim);
-                    else dstPrim.SetStreamedVertices(srcPrim);
+                    if (useStrided) dstPrim.SetStridedVertices(srcPrim, jointEncoding);
+                    else dstPrim.SetStreamedVertices(srcPrim, jointEncoding);
 
                     dstPrim.SetIndices(srcPrim, indexEncoding);
                     dstPrim.SetMorphTargets(srcPrim);

+ 8 - 8
src/SharpGLTF.Toolkit/Geometry/PackedPrimitiveBuilder.cs

@@ -37,11 +37,11 @@ namespace SharpGLTF.Geometry
 
         #region API
 
-        public void SetStridedVertices(IPrimitiveReader<TMaterial> srcPrim)
+        public void SetStridedVertices(IPrimitiveReader<TMaterial> srcPrim , EncodingType encoding)
         {
             Guard.NotNull(srcPrim, nameof(srcPrim));
 
-            var vAccessors = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(srcPrim.Vertices);
+            var vAccessors = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(srcPrim.Vertices, encoding);
 
             Guard.NotNull(vAccessors, nameof(srcPrim));
 
@@ -49,12 +49,12 @@ namespace SharpGLTF.Geometry
             _VertexAccessors = vAccessors;
         }
 
-        public void SetStreamedVertices(IPrimitiveReader<TMaterial> srcPrim)
+        public void SetStreamedVertices(IPrimitiveReader<TMaterial> srcPrim, EncodingType encoding)
         {
             Guard.NotNull(srcPrim, nameof(srcPrim));
 
             var attributeNames = VertexTypes.VertexUtils
-                        .GetVertexAttributes(srcPrim.Vertices[0], srcPrim.Vertices.Count)
+                        .GetVertexAttributes(srcPrim.Vertices[0], srcPrim.Vertices.Count, encoding)
                         .Select(item => item.Name)
                         .ToList();
 
@@ -62,7 +62,7 @@ namespace SharpGLTF.Geometry
 
             foreach (var an in attributeNames)
             {
-                var vAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(srcPrim.Vertices, an);
+                var vAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessor(srcPrim.Vertices, an, encoding);
                 if (vAccessor == null) continue;
 
                 System.Diagnostics.Debug.Assert(vAccessor.Attribute.ByteOffset == 0);
@@ -96,13 +96,13 @@ namespace SharpGLTF.Geometry
 
                 vAccessors.Clear();
 
-                var pAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(mtv, "POSITION");
+                var pAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessor(mtv, "POSITION", EncodingType.UNSIGNED_SHORT);
                 if (pAccessor != null) vAccessors.Add(pAccessor);
 
-                var nAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(mtv, "NORMAL");
+                var nAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessor(mtv, "NORMAL", EncodingType.UNSIGNED_SHORT);
                 if (nAccessor != null) vAccessors.Add(nAccessor);
 
-                var tAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessors(mtv, "MORPHTANGENT");
+                var tAccessor = VertexTypes.VertexUtils.CreateVertexMemoryAccessor(mtv, "MORPHTANGENT", EncodingType.UNSIGNED_SHORT);
                 if (tAccessor != null) vAccessors.Add(tAccessor);
 
                 AddMorphTarget(pAccessor, nAccessor, tAccessor);

+ 2 - 2
src/SharpGLTF.Toolkit/Geometry/PrimitiveBuilder.cs

@@ -106,8 +106,8 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexEmpty"/>,
     /// <see cref="VertexJoints8x4"/>,
     /// <see cref="VertexJoints8x8"/>,
-    /// <see cref="VertexJoints16x4"/>,
-    /// <see cref="VertexJoints16x8"/>.
+    /// <see cref="VertexJoints4"/>,
+    /// <see cref="VertexJoints8"/>.
     /// </typeparam>
     public abstract class PrimitiveBuilder<TMaterial, TvG, TvM, TvS> : IPrimitiveBuilder, IPrimitiveReader<TMaterial>
         where TvG : struct, IVertexGeometry

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

@@ -309,7 +309,7 @@ namespace SharpGLTF.Geometry
         {
             var g = GetVertexGeometry<VertexPositionNormalTangent>(index);
             var m = GetVertexMaterial<VertexColor2Texture2>(index);
-            var s = GetVertexSkinning<VertexJoints16x8>(index);
+            var s = GetVertexSkinning<VertexJoints8>(index);
 
             var v = (IVertexBuilder)Activator.CreateInstance(vertexType);
 

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

@@ -66,8 +66,8 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexEmpty"/>,
     /// <see cref="VertexJoints8x4"/>,
     /// <see cref="VertexJoints8x8"/>,
-    /// <see cref="VertexJoints16x4"/>,
-    /// <see cref="VertexJoints16x8"/>.
+    /// <see cref="VertexJoints4"/>,
+    /// <see cref="VertexJoints8"/>.
     /// </typeparam>
     [System.Diagnostics.DebuggerDisplay("Vertex 𝐏:{Position} {_GetDebugWarnings()}")]
     public partial struct VertexBuilder<TvG, TvM, TvS> : IVertexBuilder

+ 4 - 4
src/SharpGLTF.Toolkit/Geometry/VertexTypes/FragmentPreprocessors.cs

@@ -89,8 +89,8 @@ namespace SharpGLTF.Geometry.VertexTypes
         /// <see cref="VertexEmpty"/>,
         /// <see cref="VertexJoints8x4"/>,
         /// <see cref="VertexJoints8x8"/>,
-        /// <see cref="VertexJoints16x4"/>,
-        /// <see cref="VertexJoints16x8"/>.
+        /// <see cref="VertexJoints4"/>,
+        /// <see cref="VertexJoints8"/>.
         /// </typeparam>
         /// <param name="vertex">the source <typeparamref name="TvS"/> vertex.</param>
         /// <returns>A sanitized <typeparamref name="TvS"/> vertex, or null if sanitization failed.</returns>
@@ -219,8 +219,8 @@ namespace SharpGLTF.Geometry.VertexTypes
         /// <see cref="VertexEmpty"/>,
         /// <see cref="VertexJoints8x4"/>,
         /// <see cref="VertexJoints8x8"/>,
-        /// <see cref="VertexJoints16x4"/>,
-        /// <see cref="VertexJoints16x8"/>.
+        /// <see cref="VertexJoints4"/>,
+        /// <see cref="VertexJoints8"/>.
         /// </typeparam>
         /// <param name="vertex">the source <typeparamref name="TvS"/> vertex.</param>
         /// <returns>A sanitized <typeparamref name="TvS"/> vertex, or null if sanitization failed.</returns>

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

@@ -26,7 +26,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         Vector2 IVertexMaterial.GetTexCoord(int index) { throw new ArgumentOutOfRangeException(nameof(index)); }
 
-        public SparseWeight8 GetWeights() { throw new NotSupportedException(); }
+        public SparseWeight8 GetWeights() { return default; }
 
         public void SetWeights(in SparseWeight8 weights) { throw new NotSupportedException(); }
 
@@ -43,6 +43,6 @@ namespace SharpGLTF.Geometry.VertexTypes
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         Vector4 IVertexSkinning.WeightsLow => Vector4.Zero;
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.Weightshigh => Vector4.Zero;
+        Vector4 IVertexSkinning.WeightsHigh => Vector4.Zero;
     }
 }

+ 2 - 2
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexPreprocessors.cs

@@ -38,8 +38,8 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// <see cref="VertexEmpty"/>,
     /// <see cref="VertexJoints8x4"/>,
     /// <see cref="VertexJoints8x8"/>,
-    /// <see cref="VertexJoints16x4"/>,
-    /// <see cref="VertexJoints16x8"/>.
+    /// <see cref="VertexJoints4"/>,
+    /// <see cref="VertexJoints8"/>.
     /// </typeparam>
     public sealed class VertexPreprocessor<TvG, TvM, TvS>
         where TvG : struct, IVertexGeometry

+ 14 - 220
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexSkinning.cs

@@ -23,122 +23,26 @@ namespace SharpGLTF.Geometry.VertexTypes
         Vector4 JointsHigh { get; }
 
         Vector4 WeightsLow { get; }
-        Vector4 Weightshigh { get; }
-    }
-
-    /// <summary>
-    /// Defines a Vertex attribute with up to 256 bone joints and 4 weights.
-    /// </summary>
-    public struct VertexJoints8x4 : IVertexSkinning
-    {
-        #region constructors
-
-        public VertexJoints8x4(int jointIndex)
-        {
-            Joints = new Vector4(jointIndex, 0, 0, 0);
-            Weights = Vector4.UnitX;
-        }
-
-        public VertexJoints8x4(params (int, float)[] bindings)
-            : this(SparseWeight8.Create(bindings)) { }
-
-        public VertexJoints8x4(in SparseWeight8 weights)
-        {
-            var w4 = SparseWeight8.OrderedByWeight(weights);
-
-            Joints = new Vector4(w4.Index0, w4.Index1, w4.Index2, w4.Index3);
-            Weights = new Vector4(w4.Weight0, w4.Weight1, w4.Weight2, w4.Weight3);
-
-            // renormalize
-            var w = Vector4.Dot(Weights, Vector4.One);
-            if (w != 0 && w != 1) Weights /= w;
-        }
-
-        #endregion
-
-        #region data
-
-        [VertexAttribute("JOINTS_0", Schema2.EncodingType.UNSIGNED_BYTE, false)]
-        public Vector4 Joints;
-
-        [VertexAttribute("WEIGHTS_0", Schema2.EncodingType.UNSIGNED_BYTE, true)]
-        public Vector4 Weights;
-
-        public int MaxBindings => 4;
-
-        #endregion
-
-        #region properties
-
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.JointsLow => this.Joints;
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.JointsHigh => Vector4.Zero;
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.WeightsLow => this.Weights;
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.Weightshigh => Vector4.Zero;
-
-        #endregion
-
-        #region API
-
-        public void Validate() { FragmentPreprocessors.ValidateVertexSkinning(this); }
-
-        public SparseWeight8 GetWeights() { return new SparseWeight8(this.Joints, this.Weights); }
-
-        public void SetWeights(in SparseWeight8 weights) { this = new VertexJoints8x4(weights); }
-
-        public (int, float) GetJointBinding(int index)
-        {
-            switch (index)
-            {
-                case 0: return ((int)this.Joints.X, this.Weights.X);
-                case 1: return ((int)this.Joints.Y, this.Weights.Y);
-                case 2: return ((int)this.Joints.Z, this.Weights.Z);
-                case 3: return ((int)this.Joints.W, this.Weights.W);
-                default: throw new ArgumentOutOfRangeException(nameof(index));
-            }
-        }
-
-        public void SetJointBinding(int index, int joint, float weight)
-        {
-            switch (index)
-            {
-                case 0: { this.Joints.X = joint; this.Weights.X = weight; return; }
-                case 1: { this.Joints.Y = joint; this.Weights.Y = weight; return; }
-                case 2: { this.Joints.Z = joint; this.Weights.Z = weight; return; }
-                case 3: { this.Joints.W = joint; this.Weights.W = weight; return; }
-                default: throw new ArgumentOutOfRangeException(nameof(index));
-            }
-        }
-
-        public void InPlaceSort()
-        {
-            var sparse = new SparseWeight8(this.Joints, this.Weights);
-            this = new VertexJoints8x4(sparse);
-        }
-
-        #endregion
+        Vector4 WeightsHigh { get; }
     }
 
     /// <summary>
     /// Defines a Vertex attribute with up to 65535 bone joints and 4 weights.
     /// </summary>
-    public struct VertexJoints16x4 : IVertexSkinning
+    public struct VertexJoints4 : IVertexSkinning
     {
         #region constructors
 
-        public VertexJoints16x4(int jointIndex)
+        public VertexJoints4(int jointIndex)
         {
             Joints = new Vector4(jointIndex, 0, 0, 0);
             Weights = Vector4.UnitX;
         }
 
-        public VertexJoints16x4(params (int, float)[] bindings)
+        public VertexJoints4(params (int, float)[] bindings)
             : this( SparseWeight8.Create(bindings) ) { }
 
-        public VertexJoints16x4(in SparseWeight8 weights)
+        public VertexJoints4(in SparseWeight8 weights)
         {
             var w4 = SparseWeight8.OrderedByWeight(weights);
 
@@ -173,7 +77,7 @@ namespace SharpGLTF.Geometry.VertexTypes
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         Vector4 IVertexSkinning.WeightsLow => this.Weights;
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.Weightshigh => Vector4.Zero;
+        Vector4 IVertexSkinning.WeightsHigh => Vector4.Zero;
 
         #endregion
 
@@ -183,7 +87,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public SparseWeight8 GetWeights() { return new SparseWeight8(this.Joints, this.Weights); }
 
-        public void SetWeights(in SparseWeight8 weights) { this = new VertexJoints16x4(weights); }
+        public void SetWeights(in SparseWeight8 weights) { this = new VertexJoints4(weights); }
 
         public (int, float) GetJointBinding(int index)
         {
@@ -212,117 +116,7 @@ namespace SharpGLTF.Geometry.VertexTypes
         public void InPlaceSort()
         {
             var sparse = new SparseWeight8(this.Joints, this.Weights);
-            this = new VertexJoints16x4(sparse);
-        }
-
-        #endregion
-    }
-
-    /// <summary>
-    /// Defines a Vertex attribute with up to 256 bone joints and 8 weights.
-    /// </summary>
-    public struct VertexJoints8x8 : IVertexSkinning
-    {
-        #region constructors
-
-        public VertexJoints8x8(int jointIndex)
-        {
-            Joints0 = new Vector4(jointIndex, 0, 0, 0);
-            Joints1 = Vector4.Zero;
-            Weights0 = Vector4.UnitX;
-            Weights1 = Vector4.Zero;
-        }
-
-        public VertexJoints8x8(params (int, float)[] bindings)
-            : this(SparseWeight8.Create(bindings)) { }
-
-        public VertexJoints8x8(in SparseWeight8 weights)
-        {
-            var w8 = SparseWeight8.OrderedByWeight(weights);
-
-            Joints0 = new Vector4(w8.Index0, w8.Index1, w8.Index2, w8.Index3);
-            Joints1 = new Vector4(w8.Index4, w8.Index5, w8.Index6, w8.Index7);
-            Weights0 = new Vector4(w8.Weight0, w8.Weight1, w8.Weight2, w8.Weight3);
-            Weights1 = new Vector4(w8.Weight4, w8.Weight5, w8.Weight6, w8.Weight7);
-
-            // renormalize
-            var w = Vector4.Dot(Weights0, Vector4.One) + Vector4.Dot(Weights1, Vector4.One);
-            if (w != 0 && w != 1) { Weights0 /= w; Weights1 /= w; }
-        }
-
-        #endregion
-
-        #region data
-
-        [VertexAttribute("JOINTS_0", Schema2.EncodingType.UNSIGNED_BYTE, false)]
-        public Vector4 Joints0;
-
-        [VertexAttribute("JOINTS_1", Schema2.EncodingType.UNSIGNED_BYTE, false)]
-        public Vector4 Joints1;
-
-        [VertexAttribute("WEIGHTS_0", Schema2.EncodingType.UNSIGNED_BYTE, true)]
-        public Vector4 Weights0;
-
-        [VertexAttribute("WEIGHTS_1", Schema2.EncodingType.UNSIGNED_BYTE, true)]
-        public Vector4 Weights1;
-
-        public int MaxBindings => 8;
-
-        #endregion
-
-        #region properties
-
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.JointsLow => this.Joints0;
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.JointsHigh => this.Joints1;
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.WeightsLow => this.Weights0;
-        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.Weightshigh => this.Joints1;
-
-        public SparseWeight8 SparseWeights => new SparseWeight8(this.Joints0, this.Joints1, this.Weights0, this.Weights1);
-
-        #endregion
-
-        #region API
-
-        public void Validate() { FragmentPreprocessors.ValidateVertexSkinning(this); }
-
-        public SparseWeight8 GetWeights() { return new SparseWeight8(this.Joints0, this.Joints1, this.Weights0, this.Weights1); }
-
-        public void SetWeights(in SparseWeight8 weights) { this = new VertexJoints8x8(weights); }
-
-        public (int, float) GetJointBinding(int index)
-        {
-            switch (index)
-            {
-                case 0: return ((int)this.Joints0.X, this.Weights0.X);
-                case 1: return ((int)this.Joints0.Y, this.Weights0.Y);
-                case 2: return ((int)this.Joints0.Z, this.Weights0.Z);
-                case 3: return ((int)this.Joints0.W, this.Weights0.W);
-                case 4: return ((int)this.Joints1.X, this.Weights1.X);
-                case 5: return ((int)this.Joints1.Y, this.Weights1.Y);
-                case 6: return ((int)this.Joints1.Z, this.Weights1.Z);
-                case 7: return ((int)this.Joints1.W, this.Weights1.W);
-                default: throw new ArgumentOutOfRangeException(nameof(index));
-            }
-        }
-
-        public void SetJointBinding(int index, int joint, float weight)
-        {
-            switch (index)
-            {
-                case 0: { this.Joints0.X = joint; this.Weights0.X = weight; return; }
-                case 1: { this.Joints0.Y = joint; this.Weights0.Y = weight; return; }
-                case 2: { this.Joints0.Z = joint; this.Weights0.Z = weight; return; }
-                case 3: { this.Joints0.W = joint; this.Weights0.W = weight; return; }
-                case 4: { this.Joints1.X = joint; this.Weights1.X = weight; return; }
-                case 5: { this.Joints1.Y = joint; this.Weights1.Y = weight; return; }
-                case 6: { this.Joints1.Z = joint; this.Weights1.Z = weight; return; }
-                case 7: { this.Joints1.W = joint; this.Weights1.W = weight; return; }
-                default: throw new ArgumentOutOfRangeException(nameof(index));
-            }
+            this = new VertexJoints4(sparse);
         }
 
         #endregion
@@ -331,11 +125,11 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// <summary>
     /// Defines a Vertex attribute with up to 65535 bone joints and 8 weights.
     /// </summary>
-    public struct VertexJoints16x8 : IVertexSkinning
+    public struct VertexJoints8 : IVertexSkinning
     {
         #region constructors
 
-        public VertexJoints16x8(int jointIndex)
+        public VertexJoints8(int jointIndex)
         {
             Joints0 = new Vector4(jointIndex, 0, 0, 0);
             Joints1 = Vector4.Zero;
@@ -343,10 +137,10 @@ namespace SharpGLTF.Geometry.VertexTypes
             Weights1 = Vector4.Zero;
         }
 
-        public VertexJoints16x8(params (int, float)[] bindings)
+        public VertexJoints8(params (int, float)[] bindings)
             : this(SparseWeight8.Create(bindings)) { }
 
-        public VertexJoints16x8(in SparseWeight8 weights)
+        public VertexJoints8(in SparseWeight8 weights)
         {
             var w8 = SparseWeight8.OrderedByWeight(weights);
 
@@ -389,7 +183,7 @@ namespace SharpGLTF.Geometry.VertexTypes
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         Vector4 IVertexSkinning.WeightsLow => this.Weights0;
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        Vector4 IVertexSkinning.Weightshigh => this.Joints1;
+        Vector4 IVertexSkinning.WeightsHigh => this.Joints1;
 
         #endregion
 
@@ -399,7 +193,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public SparseWeight8 GetWeights() { return new SparseWeight8(this.Joints0, this.Joints1, this.Weights0, this.Weights1); }
 
-        public void SetWeights(in SparseWeight8 weights) { this = new VertexJoints16x8(weights); }
+        public void SetWeights(in SparseWeight8 weights) { this = new VertexJoints8(weights); }
 
         public (int, float) GetJointBinding(int index)
         {

+ 17 - 11
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexUtils.cs

@@ -65,8 +65,8 @@ namespace SharpGLTF.Geometry.VertexTypes
             var joints = vertexAttributes.Contains("JOINTS_0") && vertexAttributes.Contains("WEIGHTS_0") ? 4 : 0;
             joints = vertexAttributes.Contains("JOINTS_1") && vertexAttributes.Contains("WEIGHTS_1") ? 8 : joints;
 
-            if (joints == 4) return typeof(VertexJoints16x4);
-            if (joints == 8) return typeof(VertexJoints16x8);
+            if (joints == 4) return typeof(VertexJoints4);
+            if (joints == 8) return typeof(VertexJoints8);
 
             return typeof(VertexEmpty);
         }
@@ -91,8 +91,8 @@ namespace SharpGLTF.Geometry.VertexTypes
             var tvm = GetVertexMaterialType(numCols, numUV);
 
             var tvs = typeof(VertexEmpty);
-            if (numJoints == 4) tvs = typeof(VertexJoints16x4);
-            if (numJoints >= 8) tvs = typeof(VertexJoints16x8);
+            if (numJoints == 4) tvs = typeof(VertexJoints4);
+            if (numJoints >= 8) tvs = typeof(VertexJoints8);
 
             var vtype = typeof(VertexBuilder<,,>);
 
@@ -207,13 +207,13 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #region memory buffers API
 
-        public static MemoryAccessor CreateVertexMemoryAccessors<TVertex>(this IReadOnlyList<TVertex> vertices, string attributeName)
+        public static MemoryAccessor CreateVertexMemoryAccessor<TVertex>(this IReadOnlyList<TVertex> vertices, string attributeName, Schema2.EncodingType jointEncoding)
             where TVertex : IVertexBuilder
         {
             if (vertices == null || vertices.Count == 0) return null;
 
             // determine the vertex attributes from the first vertex.
-            var attributes = GetVertexAttributes(vertices[0], vertices.Count);
+            var attributes = GetVertexAttributes(vertices[0], vertices.Count, jointEncoding);
 
             var isMorphTangent = attributeName == "MORPHTANGENT";
             if (isMorphTangent) attributeName = "TANGENT";
@@ -243,13 +243,13 @@ namespace SharpGLTF.Geometry.VertexTypes
             return accessor;
         }
 
-        public static MemoryAccessor[] CreateVertexMemoryAccessors<TVertex>(this IReadOnlyList<TVertex> vertices)
+        public static MemoryAccessor[] CreateVertexMemoryAccessors<TVertex>(this IReadOnlyList<TVertex> vertices, Schema2.EncodingType jointEncoding)
             where TVertex : IVertexBuilder
         {
             if (vertices == null || vertices.Count == 0) return null;
 
             // determine the vertex attributes from the first vertex.
-            var attributes = GetVertexAttributes(vertices[0], vertices.Count);
+            var attributes = GetVertexAttributes(vertices[0], vertices.Count, jointEncoding);
 
             // create a buffer
             int byteStride = attributes[0].ByteStride;
@@ -298,7 +298,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             return accessor;
         }
 
-        public static MemoryAccessInfo[] GetVertexAttributes(this IVertexBuilder firstVertex, int vertexCount)
+        public static MemoryAccessInfo[] GetVertexAttributes(this IVertexBuilder firstVertex, int vertexCount, Schema2.EncodingType jointEncoding)
         {
             var tvg = firstVertex.GetGeometry().GetType();
             var tvm = firstVertex.GetMaterial().GetType();
@@ -321,7 +321,13 @@ namespace SharpGLTF.Geometry.VertexTypes
             foreach (var finfo in tvs.GetFields())
             {
                 var attribute = _GetMemoryAccessInfo(finfo);
-                if (attribute.HasValue) attributes.Add(attribute.Value);
+                if (attribute.HasValue)
+                {
+                    var a = attribute.Value;
+                    if (a.Name.StartsWith("JOINTS_", StringComparison.OrdinalIgnoreCase)) a.Encoding = jointEncoding;
+
+                    attributes.Add(a);
+                }
             }
 
             var array = attributes.ToArray();
@@ -374,7 +380,7 @@ namespace SharpGLTF.Geometry.VertexTypes
             if (attributeName == "JOINTS_1") return v => v.GetSkinning().JointsHigh;
 
             if (attributeName == "WEIGHTS_0") return v => v.GetSkinning().WeightsLow;
-            if (attributeName == "WEIGHTS_1") return v => v.GetSkinning().Weightshigh;
+            if (attributeName == "WEIGHTS_1") return v => v.GetSkinning().WeightsHigh;
 
             return v => v.GetMaterial().GetCustomAttribute(attributeName);
         }

+ 7 - 1
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -263,7 +263,13 @@ namespace SharpGLTF.Schema2
         public static MeshPrimitive WithVertexAccessors<TVertex>(this MeshPrimitive primitive, IReadOnlyList<TVertex> vertices)
             where TVertex : IVertexBuilder
         {
-            var memAccessors = VertexUtils.CreateVertexMemoryAccessors( vertices );
+            var indices = vertices.Select(item => item.GetSkinning().GetWeights().MaxIndex);
+
+            var maxIndex = indices.Any() ? indices.Max() : 0;
+
+            var encoding = maxIndex < 256 ? Schema2.EncodingType.UNSIGNED_BYTE : EncodingType.UNSIGNED_SHORT;
+
+            var memAccessors = VertexUtils.CreateVertexMemoryAccessors(vertices, encoding);
 
             return primitive.WithVertexAccessors(memAccessors);
         }

+ 3 - 3
tests/SharpGLTF.Tests/Geometry/MeshBuilderTests.cs

@@ -9,8 +9,8 @@ using SharpGLTF.Schema2;
 
 namespace SharpGLTF.Geometry
 {
-    using VERTEX1 = VertexBuilder<VertexPosition, VertexColor1Texture1, VertexJoints8x4>;
-    using VERTEX2 = VertexBuilder<VertexPositionNormal, VertexColor1Texture1, VertexJoints8x4>;
+    using VERTEX1 = VertexBuilder<VertexPosition, VertexColor1Texture1, VertexJoints4>;
+    using VERTEX2 = VertexBuilder<VertexPositionNormal, VertexColor1Texture1, VertexJoints4>;
 
     [Category("Toolkit.Geometry")]
     public class MeshBuilderTests
@@ -90,7 +90,7 @@ namespace SharpGLTF.Geometry
 
             var p = new VertexPositionNormal(Vector3.UnitX, new Vector3(float.NaN));
             var m = new VertexColor1Texture1(Vector4.One * 7, new Vector2(float.NaN));
-            var s = new VertexJoints8x4((0, 2), (1, 7), (2, 6), (3, 5));
+            var s = new VertexJoints4((0, 2), (1, 7), (2, 6), (3, 5));
 
             var v1 = new VERTEX2(p, m, s);
             var v1Idx = prim.AddPoint(new VERTEX2(p, m, s));

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

@@ -14,7 +14,7 @@ namespace SharpGLTF.Geometry.VertexTypes
         public void TestVertexSkinningDowngradeFrom8To4Joints()
         {
             // vertex with 5 bindings
-            var v8 = new VertexJoints8x8
+            var v8 = new VertexJoints8
                 (
                 (1, 0.20f),
                 (2, 0.15f),
@@ -24,7 +24,7 @@ namespace SharpGLTF.Geometry.VertexTypes
                 );
 
             // we downgrade to 4 bindings; remaining bindings should be interpolated to keep weighting 1.
-            var v4 = v8.ConvertToSkinning<VertexJoints8x4>();
+            var v4 = v8.ConvertToSkinning<VertexJoints4>();
 
             var sparse = v4.GetWeights();
 

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

@@ -14,7 +14,7 @@ namespace SharpGLTF.Scenes
 {
     using VPOSNRM = VertexBuilder<VertexPositionNormal, VertexEmpty, VertexEmpty>;
 
-    using SKINNEDVERTEX = VertexBuilder<VertexPosition, VertexEmpty, VertexJoints8x4>;
+    using SKINNEDVERTEX = VertexBuilder<VertexPosition, VertexEmpty, VertexJoints4>;
 
 
     [Category("Toolkit.Scenes")]

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

@@ -14,7 +14,7 @@ namespace SharpGLTF.Schema2.Authoring
     using VEMPTY = Geometry.VertexTypes.VertexEmpty;
     using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;
     using VPOS = Geometry.VertexTypes.VertexPosition;
-    using VSKIN4 = Geometry.VertexTypes.VertexJoints8x4;
+    using VSKIN4 = Geometry.VertexTypes.VertexJoints4;
 
     [TestFixture]
     [Category("Model Authoring")]