ソースを参照

minor refactoring and code cleanup

Vicente Penades 6 年 前
コミット
a6b7476da0

+ 1 - 1
src/SharpGLTF.Core/Schema2/gltf.BufferView.cs

@@ -231,7 +231,7 @@ namespace SharpGLTF.Schema2
         {
             Guard.NotNullOrEmpty(data, nameof(data));
 
-            // todo: search data on existing data for reusability.
+            // todo: search data on existing buffers for reusability and compression.
 
             // padding
             while ((_Data.Count & 3) != 0) _Data.Add(0);

+ 0 - 5
src/SharpGLTF.Core/Schema2/gltf.Serialization.Write.cs

@@ -224,11 +224,6 @@ namespace SharpGLTF.Schema2
 
     partial class ModelRoot
     {
-        // TODO: usually when we save the gltf file, we need to amend/fix several features,
-        // which goes against good practices of not modifying any file when it is being saved.
-        // a possible solution would be to do a shallow copy of RootObject and update Buffers, BufferViews, etc
-        // an issue that complicates things is that it requires to copy the extensions of root, buffers, etc
-
         /// <summary>
         /// Writes this <see cref="MODEL"/> to a file in GLB format.
         /// </summary>

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

@@ -281,7 +281,7 @@ namespace SharpGLTF.Geometry
                 if (!c._IsReal() | !c.IsInRange(Vector4.Zero, Vector4.One)) sb.Append($" ❌𝐂{i}:{c}");
             }
 
-            for (int i = 0; i < Material.MaxTextures; ++i)
+            for (int i = 0; i < Material.MaxTextCoords; ++i)
             {
                 var uv = Material.GetTexCoord(i);
                 if (!uv._IsReal()) sb.Append($" ❌𝐔𝐕{i}:{uv}");

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

@@ -231,8 +231,8 @@ namespace SharpGLTF.Geometry
             if (Colors0 != null && cctt.MaxColors > 0) cctt.SetColor(0, Colors0[index]);
             if (Colors1 != null && cctt.MaxColors > 1) cctt.SetColor(1, Colors1[index]);
 
-            if (Textures0 != null && cctt.MaxTextures > 0) cctt.SetTexCoord(0, Textures0[index]);
-            if (Textures1 != null && cctt.MaxTextures > 1) cctt.SetTexCoord(1, Textures1[index]);
+            if (Textures0 != null && cctt.MaxTextCoords > 0) cctt.SetTexCoord(0, Textures0[index]);
+            if (Textures1 != null && cctt.MaxTextCoords > 1) cctt.SetTexCoord(1, Textures1[index]);
 
             return cctt;
         }

+ 262 - 0
src/SharpGLTF.Toolkit/Geometry/VertexTypes/FragmentPreprocessors.cs

@@ -0,0 +1,262 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Geometry.VertexTypes
+{
+    /// <summary>
+    /// Defines a set of vertex fragment preprocessors to be used with <see cref="VertexPreprocessor{TvG, TvM, TvS}"/>
+    /// </summary>
+    static class FragmentPreprocessors
+    {
+        /// <summary>
+        /// validates a vertex geometry, throwing exceptions if found invalid
+        /// </summary>
+        /// <typeparam name="TvG">
+        /// The vertex fragment type with Position, Normal and Tangent.
+        /// Valid types are:
+        /// <see cref="VertexPosition"/>,
+        /// <see cref="VertexPositionNormal"/>,
+        /// <see cref="VertexPositionNormalTangent"/>.
+        /// </typeparam>
+        /// <param name="vertex">the source <typeparamref name="TvG"/> vertex.</param>
+        /// <returns>A sanitized <typeparamref name="TvG"/> vertex, or null if sanitization failed.</returns>
+        /// <exception cref="ArgumentException">When the vertex is invalid.</exception>
+        public static TvG? ValidateVertexGeometry<TvG>(TvG vertex)
+            where TvG : struct, IVertexGeometry
+        {
+            var p = vertex.GetPosition();
+            Guard.IsTrue(p._IsReal(), "Position", "Values are not finite.");
+
+            if (vertex.TryGetNormal(out Vector3 n))
+            {
+                Guard.IsTrue(n._IsReal(), "Normal", "Values are not finite.");
+                Guard.MustBeBetweenOrEqualTo(n.Length(), 0.99f, 1.01f, "Normal.Length");
+            }
+
+            if (vertex.TryGetTangent(out Vector4 t))
+            {
+                Guard.IsTrue(t._IsReal(), "Tangent", "Values are not finite.");
+                Guard.IsTrue(t.W == 1 || t.W == -1, "Tangent.W", "Invalid value");
+                Guard.MustBeBetweenOrEqualTo(new Vector3(t.X, t.Y, t.Z).Length(), 0.99f, 1.01f, "Tangent.XYZ.Length");
+            }
+
+            return vertex;
+        }
+
+        /// <summary>
+        /// Sanitizes a vertex material with a best effort approach
+        /// </summary>
+        /// <typeparam name="TvM">
+        /// The vertex fragment type with Colors and Texture Coordinates.
+        /// Valid types are:
+        /// <see cref="VertexEmpty"/>,
+        /// <see cref="VertexColor1"/>,
+        /// <see cref="VertexTexture1"/>,
+        /// <see cref="VertexColor1Texture1"/>.
+        /// </typeparam>
+        /// <param name="vertex">the source <typeparamref name="TvM"/> vertex.</param>
+        /// <returns>A sanitized <typeparamref name="TvM"/> vertex, or null if sanitization failed.</returns>
+        public static TvM? ValidateVertexMaterial<TvM>(TvM vertex)
+            where TvM : struct, IVertexMaterial
+        {
+            for (int i = 0; i < vertex.MaxColors; ++i)
+            {
+                var c = vertex.GetColor(i);
+                Guard.IsTrue(c._IsReal(), $"Color{i}", "Values are not finite.");
+                Guard.MustBeBetweenOrEqualTo(c.X, 0, 1, $"Color{i}.R");
+                Guard.MustBeBetweenOrEqualTo(c.Y, 0, 1, $"Color{i}.G");
+                Guard.MustBeBetweenOrEqualTo(c.Z, 0, 1, $"Color{i}.B");
+                Guard.MustBeBetweenOrEqualTo(c.W, 0, 1, $"Color{i}.A");
+            }
+
+            for (int i = 0; i < vertex.MaxTextCoords; ++i)
+            {
+                var t = vertex.GetTexCoord(i);
+                Guard.IsTrue(t._IsReal(), $"TexCoord{i}", "Values are not finite.");
+            }
+
+            return vertex;
+        }
+
+        /// <summary>
+        /// Sanitizes a vertex skinning with a best effort approach
+        /// </summary>
+        /// <typeparam name="TvS">
+        /// The vertex fragment type with Skin Joint Weights.
+        /// Valid types are:
+        /// <see cref="VertexEmpty"/>,
+        /// <see cref="VertexJoints8x4"/>,
+        /// <see cref="VertexJoints8x8"/>,
+        /// <see cref="VertexJoints16x4"/>,
+        /// <see cref="VertexJoints16x8"/>.
+        /// </typeparam>
+        /// <param name="vertex">the source <typeparamref name="TvS"/> vertex.</param>
+        /// <returns>A sanitized <typeparamref name="TvS"/> vertex, or null if sanitization failed.</returns>
+        public static TvS? ValidateVertexSkinning<TvS>(TvS vertex)
+            where TvS : struct, IVertexSkinning
+        {
+            // validation must ensure that:
+            // - every joint is unique
+            // - every joint and weight is 0 or positive value
+            // - 0 weight joints point to joint 0
+            // - sum of weights is 1
+
+            if (vertex.MaxBindings == 0) return vertex;
+
+            // Apparently the consensus is that weights are required to be normalized.
+            // More here: https://github.com/KhronosGroup/glTF/issues/1213
+
+            float weightsSum = 0;
+
+            for (int i = 0; i < vertex.MaxBindings; ++i)
+            {
+                var pair = vertex.GetJointBinding(i);
+
+                Guard.MustBeGreaterThanOrEqualTo(pair.Joint, 0, $"Joint{i}");
+                Guard.IsTrue(pair.Weight._IsReal(), $"Weight{i}", "Values are not finite.");
+                if (pair.Weight == 0) Guard.IsTrue(pair.Joint == 0, "joints with weight zero must be set to zero");
+
+                weightsSum += pair.Weight;
+            }
+
+            // TODO: check that joints are unique
+
+            Guard.MustBeBetweenOrEqualTo(weightsSum, 0.99f, 1.01f, "Weights SUM");
+
+            return vertex;
+        }
+
+        /// <summary>
+        /// Sanitizes a vertex geometry with a best effort approach
+        /// </summary>
+        /// <typeparam name="TvG">
+        /// The vertex fragment type with Position, Normal and Tangent.
+        /// Valid types are:
+        /// <see cref="VertexPosition"/>,
+        /// <see cref="VertexPositionNormal"/>,
+        /// <see cref="VertexPositionNormalTangent"/>.
+        /// </typeparam>
+        /// <param name="vertex">the source <typeparamref name="TvG"/> vertex.</param>
+        /// <returns>A sanitized <typeparamref name="TvG"/> vertex, or null if sanitization failed.</returns>
+        public static TvG? SanitizeVertexGeometry<TvG>(TvG vertex)
+            where TvG : struct, IVertexGeometry
+        {
+            var p = vertex.GetPosition();
+
+            if (!p._IsReal()) return null;
+
+            if (vertex.TryGetNormal(out Vector3 n))
+            {
+                if (!n._IsReal()) return null;
+                if (n == Vector3.Zero) n = p;
+                if (n == Vector3.Zero) return null;
+
+                var l = n.Length();
+                if (l < 0.99f || l > 0.01f) vertex.SetNormal(Vector3.Normalize(n));
+            }
+
+            if (vertex.TryGetTangent(out Vector4 tw))
+            {
+                if (!tw._IsReal()) return null;
+
+                var t = new Vector3(tw.X, tw.Y, tw.Z);
+                if (t == Vector3.Zero) return null;
+
+                if (tw.W > 0) tw.W = 1;
+                if (tw.W < 0) tw.W = -1;
+
+                var l = t.Length();
+                if (l < 0.99f || l > 0.01f) t = Vector3.Normalize(t);
+
+                vertex.SetTangent(new Vector4(t, tw.W));
+            }
+
+            return vertex;
+        }
+
+        /// <summary>
+        /// Sanitizes a vertex material with a best effort approach
+        /// </summary>
+        /// <typeparam name="TvM">
+        /// The vertex fragment type with Colors and Texture Coordinates.
+        /// Valid types are:
+        /// <see cref="VertexEmpty"/>,
+        /// <see cref="VertexColor1"/>,
+        /// <see cref="VertexTexture1"/>,
+        /// <see cref="VertexColor1Texture1"/>.
+        /// </typeparam>
+        /// <param name="vertex">the source <typeparamref name="TvM"/> vertex.</param>
+        /// <returns>A sanitized <typeparamref name="TvM"/> vertex, or null if sanitization failed.</returns>
+        public static TvM? SanitizeVertexMaterial<TvM>(TvM vertex)
+            where TvM : struct, IVertexMaterial
+        {
+            for (int i = 0; i < vertex.MaxColors; ++i)
+            {
+                var c = vertex.GetColor(i);
+                if (!c._IsReal()) c = Vector4.Zero;
+                c = Vector4.Min(Vector4.One, c);
+                c = Vector4.Max(Vector4.Zero, c);
+                vertex.SetColor(i, c);
+            }
+
+            for (int i = 0; i < vertex.MaxTextCoords; ++i)
+            {
+                var t = vertex.GetTexCoord(i);
+                if (!t._IsReal()) vertex.SetTexCoord(i, Vector2.Zero);
+            }
+
+            return vertex;
+        }
+
+        /// <summary>
+        /// Sanitizes a vertex skinning with a best effort approach
+        /// </summary>
+        /// <typeparam name="TvS">
+        /// The vertex fragment type with Skin Joint Weights.
+        /// Valid types are:
+        /// <see cref="VertexEmpty"/>,
+        /// <see cref="VertexJoints8x4"/>,
+        /// <see cref="VertexJoints8x8"/>,
+        /// <see cref="VertexJoints16x4"/>,
+        /// <see cref="VertexJoints16x8"/>.
+        /// </typeparam>
+        /// <param name="vertex">the source <typeparamref name="TvS"/> vertex.</param>
+        /// <returns>A sanitized <typeparamref name="TvS"/> vertex, or null if sanitization failed.</returns>
+        public static TvS? SanitizeVertexSkinning<TvS>(TvS vertex)
+            where TvS : struct, IVertexSkinning
+        {
+            if (vertex.MaxBindings == 0) return vertex;
+
+            Span<JointBinding> pairs = stackalloc JointBinding[vertex.MaxBindings];
+
+            // Apparently the consensus is that weights are required to be normalized.
+            // More here: https://github.com/KhronosGroup/glTF/issues/1213
+
+            float weightsSum = 0;
+
+            for (int i = 0; i < pairs.Length; ++i)
+            {
+                var pair = vertex.GetJointBinding(i);
+
+                pairs[i] = pair.Weight == 0 ? default : pair;
+
+                weightsSum += pair.Weight;
+            }
+
+            // TODO: check that joints are unique, and if not, do a merge.
+
+            if (weightsSum == 0) weightsSum = 1;
+
+            JointBinding.InPlaceReverseBubbleSort(pairs);
+
+            for (int i = 0; i < pairs.Length; ++i)
+            {
+                vertex.SetJointBinding(i, pairs[i].Joint, pairs[i].Weight / weightsSum);
+            }
+
+            return vertex;
+        }
+    }
+}

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

@@ -15,7 +15,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public int MaxColors => 0;
 
-        public int MaxTextures => 0;
+        public int MaxTextCoords => 0;
 
         void IVertexMaterial.SetColor(int setIndex, Vector4 color) { }
 

+ 12 - 13
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexMaterial.cs

@@ -9,8 +9,7 @@ namespace SharpGLTF.Geometry.VertexTypes
     {
         int MaxColors { get; }
 
-        // TODO: rename to MaxTexCoords
-        int MaxTextures { get; }
+        int MaxTextCoords { get; }
 
         void Validate();
 
@@ -53,7 +52,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public int MaxColors => 1;
 
-        public int MaxTextures => 0;
+        public int MaxTextCoords => 0;
 
         #endregion
 
@@ -94,7 +93,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public VertexTexture1(IVertexMaterial src)
         {
-            this.TexCoord = src.MaxTextures > 0 ? src.GetTexCoord(0) : Vector2.Zero;
+            this.TexCoord = src.MaxTextCoords > 0 ? src.GetTexCoord(0) : Vector2.Zero;
         }
 
         public static implicit operator VertexTexture1(Vector2 uv)
@@ -111,7 +110,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public int MaxColors => 0;
 
-        public int MaxTextures => 1;
+        public int MaxTextCoords => 1;
 
         #endregion
 
@@ -154,7 +153,7 @@ namespace SharpGLTF.Geometry.VertexTypes
         public VertexColor1Texture1(IVertexMaterial src)
         {
             this.Color = src.MaxColors > 0 ? src.GetColor(0) : Vector4.One;
-            this.TexCoord = src.MaxTextures > 0 ? src.GetTexCoord(0) : Vector2.Zero;
+            this.TexCoord = src.MaxTextCoords > 0 ? src.GetTexCoord(0) : Vector2.Zero;
         }
 
         #endregion
@@ -169,7 +168,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public int MaxColors => 1;
 
-        public int MaxTextures => 1;
+        public int MaxTextCoords => 1;
 
         #endregion
 
@@ -214,8 +213,8 @@ namespace SharpGLTF.Geometry.VertexTypes
         public VertexColor1Texture2(IVertexMaterial src)
         {
             this.Color = src.MaxColors > 0 ? src.GetColor(0) : Vector4.One;
-            this.TexCoord0 = src.MaxTextures > 0 ? src.GetTexCoord(0) : Vector2.Zero;
-            this.TexCoord1 = src.MaxTextures > 1 ? src.GetTexCoord(1) : Vector2.Zero;
+            this.TexCoord0 = src.MaxTextCoords > 0 ? src.GetTexCoord(0) : Vector2.Zero;
+            this.TexCoord1 = src.MaxTextCoords > 1 ? src.GetTexCoord(1) : Vector2.Zero;
         }
 
         #endregion
@@ -233,7 +232,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public int MaxColors => 1;
 
-        public int MaxTextures => 2;
+        public int MaxTextCoords => 2;
 
         #endregion
 
@@ -288,8 +287,8 @@ namespace SharpGLTF.Geometry.VertexTypes
         {
             this.Color0 = src.MaxColors > 0 ? src.GetColor(0) : Vector4.One;
             this.Color1 = src.MaxColors > 1 ? src.GetColor(1) : Vector4.One;
-            this.TexCoord0 = src.MaxTextures > 0 ? src.GetTexCoord(0) : Vector2.Zero;
-            this.TexCoord1 = src.MaxTextures > 1 ? src.GetTexCoord(1) : Vector2.Zero;
+            this.TexCoord0 = src.MaxTextCoords > 0 ? src.GetTexCoord(0) : Vector2.Zero;
+            this.TexCoord1 = src.MaxTextCoords > 1 ? src.GetTexCoord(1) : Vector2.Zero;
         }
 
         #endregion
@@ -310,7 +309,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         public int MaxColors => 2;
 
-        public int MaxTextures => 2;
+        public int MaxTextCoords => 2;
 
         #endregion
 

+ 0 - 249
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexPreprocessors.cs

@@ -122,253 +122,4 @@ namespace SharpGLTF.Geometry.VertexTypes
 
         #endregion
     }
-
-    /// <summary>
-    /// Defines a set of vertex fragment preprocessors to be used with <see cref="VertexPreprocessor{TvG, TvM, TvS}"/>
-    /// </summary>
-    static class FragmentPreprocessors
-    {
-        /// <summary>
-        /// validates a vertex geometry, throwing exceptions if found invalid
-        /// </summary>
-        /// <typeparam name="TvG">
-        /// The vertex fragment type with Position, Normal and Tangent.
-        /// Valid types are:
-        /// <see cref="VertexPosition"/>,
-        /// <see cref="VertexPositionNormal"/>,
-        /// <see cref="VertexPositionNormalTangent"/>.
-        /// </typeparam>
-        /// <param name="vertex">the source <typeparamref name="TvG"/> vertex.</param>
-        /// <returns>A sanitized <typeparamref name="TvG"/> vertex, or null if sanitization failed.</returns>
-        /// <exception cref="ArgumentException">When the vertex is invalid.</exception>
-        public static TvG? ValidateVertexGeometry<TvG>(TvG vertex)
-            where TvG : struct, IVertexGeometry
-        {
-            var p = vertex.GetPosition();
-            Guard.IsTrue(p._IsReal(), "Position", "Values are not finite.");
-
-            if (vertex.TryGetNormal(out Vector3 n))
-            {
-                Guard.IsTrue(n._IsReal(), "Normal", "Values are not finite.");
-                Guard.MustBeBetweenOrEqualTo(n.Length(), 0.99f, 1.01f, "Normal.Length");
-            }
-
-            if (vertex.TryGetTangent(out Vector4 t))
-            {
-                Guard.IsTrue(t._IsReal(), "Tangent", "Values are not finite.");
-                Guard.IsTrue(t.W == 1 || t.W == -1, "Tangent.W", "Invalid value");
-                Guard.MustBeBetweenOrEqualTo(new Vector3(t.X, t.Y, t.Z).Length(), 0.99f, 1.01f, "Tangent.XYZ.Length");
-            }
-
-            return vertex;
-        }
-
-        /// <summary>
-        /// Sanitizes a vertex material with a best effort approach
-        /// </summary>
-        /// <typeparam name="TvM">
-        /// The vertex fragment type with Colors and Texture Coordinates.
-        /// Valid types are:
-        /// <see cref="VertexEmpty"/>,
-        /// <see cref="VertexColor1"/>,
-        /// <see cref="VertexTexture1"/>,
-        /// <see cref="VertexColor1Texture1"/>.
-        /// </typeparam>
-        /// <param name="vertex">the source <typeparamref name="TvM"/> vertex.</param>
-        /// <returns>A sanitized <typeparamref name="TvM"/> vertex, or null if sanitization failed.</returns>
-        public static TvM? ValidateVertexMaterial<TvM>(TvM vertex)
-            where TvM : struct, IVertexMaterial
-        {
-            for (int i = 0; i < vertex.MaxColors; ++i)
-            {
-                var c = vertex.GetColor(i);
-                Guard.IsTrue(c._IsReal(), $"Color{i}", "Values are not finite.");
-                Guard.MustBeBetweenOrEqualTo(c.X, 0, 1, $"Color{i}.R");
-                Guard.MustBeBetweenOrEqualTo(c.Y, 0, 1, $"Color{i}.G");
-                Guard.MustBeBetweenOrEqualTo(c.Z, 0, 1, $"Color{i}.B");
-                Guard.MustBeBetweenOrEqualTo(c.W, 0, 1, $"Color{i}.A");
-            }
-
-            for (int i = 0; i < vertex.MaxTextures; ++i)
-            {
-                var t = vertex.GetTexCoord(i);
-                Guard.IsTrue(t._IsReal(), $"TexCoord{i}", "Values are not finite.");
-            }
-
-            return vertex;
-        }
-
-        /// <summary>
-        /// Sanitizes a vertex skinning with a best effort approach
-        /// </summary>
-        /// <typeparam name="TvS">
-        /// The vertex fragment type with Skin Joint Weights.
-        /// Valid types are:
-        /// <see cref="VertexEmpty"/>,
-        /// <see cref="VertexJoints8x4"/>,
-        /// <see cref="VertexJoints8x8"/>,
-        /// <see cref="VertexJoints16x4"/>,
-        /// <see cref="VertexJoints16x8"/>.
-        /// </typeparam>
-        /// <param name="vertex">the source <typeparamref name="TvS"/> vertex.</param>
-        /// <returns>A sanitized <typeparamref name="TvS"/> vertex, or null if sanitization failed.</returns>
-        public static TvS? ValidateVertexSkinning<TvS>(TvS vertex)
-            where TvS : struct, IVertexSkinning
-        {
-            if (vertex.MaxBindings == 0) return vertex;
-
-            // Apparently the consensus is that weights are required to be normalized.
-            // More here: https://github.com/KhronosGroup/glTF/issues/1213
-
-            float weightsSum = 0;
-
-            for (int i = 0; i < vertex.MaxBindings; ++i)
-            {
-                var pair = vertex.GetJointBinding(i);
-
-                Guard.MustBeGreaterThanOrEqualTo(pair.Joint, 0, $"Joint{i}");
-                Guard.IsTrue(pair.Weight._IsReal(), $"Weight{i}", "Values are not finite.");
-                if (pair.Weight == 0) Guard.IsTrue(pair.Joint == 0, "joints with weight zero must be set to zero");
-
-                weightsSum += pair.Weight;
-            }
-
-            // TODO: check that joints are unique
-
-            Guard.MustBeBetweenOrEqualTo(weightsSum, 0.99f, 1.01f, "Weights SUM");
-
-            return vertex;
-        }
-
-        /// <summary>
-        /// Sanitizes a vertex geometry with a best effort approach
-        /// </summary>
-        /// <typeparam name="TvG">
-        /// The vertex fragment type with Position, Normal and Tangent.
-        /// Valid types are:
-        /// <see cref="VertexPosition"/>,
-        /// <see cref="VertexPositionNormal"/>,
-        /// <see cref="VertexPositionNormalTangent"/>.
-        /// </typeparam>
-        /// <param name="vertex">the source <typeparamref name="TvG"/> vertex.</param>
-        /// <returns>A sanitized <typeparamref name="TvG"/> vertex, or null if sanitization failed.</returns>
-        public static TvG? SanitizeVertexGeometry<TvG>(TvG vertex)
-            where TvG : struct, IVertexGeometry
-        {
-            var p = vertex.GetPosition();
-
-            if (!p._IsReal()) return null;
-
-            if (vertex.TryGetNormal(out Vector3 n))
-            {
-                if (!n._IsReal()) return null;
-                if (n == Vector3.Zero) n = p;
-                if (n == Vector3.Zero) return null;
-
-                var l = n.Length();
-                if (l < 0.99f || l > 0.01f) vertex.SetNormal(Vector3.Normalize(n));
-            }
-
-            if (vertex.TryGetTangent(out Vector4 tw))
-            {
-                if (!tw._IsReal()) return null;
-
-                var t = new Vector3(tw.X, tw.Y, tw.Z);
-                if (t == Vector3.Zero) return null;
-
-                if (tw.W > 0) tw.W = 1;
-                if (tw.W < 0) tw.W = -1;
-
-                var l = t.Length();
-                if (l < 0.99f || l > 0.01f) t = Vector3.Normalize(t);
-
-                vertex.SetTangent(new Vector4(t, tw.W));
-            }
-
-            return vertex;
-        }
-
-        /// <summary>
-        /// Sanitizes a vertex material with a best effort approach
-        /// </summary>
-        /// <typeparam name="TvM">
-        /// The vertex fragment type with Colors and Texture Coordinates.
-        /// Valid types are:
-        /// <see cref="VertexEmpty"/>,
-        /// <see cref="VertexColor1"/>,
-        /// <see cref="VertexTexture1"/>,
-        /// <see cref="VertexColor1Texture1"/>.
-        /// </typeparam>
-        /// <param name="vertex">the source <typeparamref name="TvM"/> vertex.</param>
-        /// <returns>A sanitized <typeparamref name="TvM"/> vertex, or null if sanitization failed.</returns>
-        public static TvM? SanitizeVertexMaterial<TvM>(TvM vertex)
-            where TvM : struct, IVertexMaterial
-        {
-            for (int i = 0; i < vertex.MaxColors; ++i)
-            {
-                var c = vertex.GetColor(i);
-                if (!c._IsReal()) c = Vector4.Zero;
-                c = Vector4.Min(Vector4.One, c);
-                c = Vector4.Max(Vector4.Zero, c);
-                vertex.SetColor(i, c);
-            }
-
-            for (int i = 0; i < vertex.MaxTextures; ++i)
-            {
-                var t = vertex.GetTexCoord(i);
-                if (!t._IsReal()) vertex.SetTexCoord(i, Vector2.Zero);
-            }
-
-            return vertex;
-        }
-
-        /// <summary>
-        /// Sanitizes a vertex skinning with a best effort approach
-        /// </summary>
-        /// <typeparam name="TvS">
-        /// The vertex fragment type with Skin Joint Weights.
-        /// Valid types are:
-        /// <see cref="VertexEmpty"/>,
-        /// <see cref="VertexJoints8x4"/>,
-        /// <see cref="VertexJoints8x8"/>,
-        /// <see cref="VertexJoints16x4"/>,
-        /// <see cref="VertexJoints16x8"/>.
-        /// </typeparam>
-        /// <param name="vertex">the source <typeparamref name="TvS"/> vertex.</param>
-        /// <returns>A sanitized <typeparamref name="TvS"/> vertex, or null if sanitization failed.</returns>
-        public static TvS? SanitizeVertexSkinning<TvS>(TvS vertex)
-            where TvS : struct, IVertexSkinning
-        {
-            if (vertex.MaxBindings == 0) return vertex;
-
-            Span<JointBinding> pairs = stackalloc JointBinding[vertex.MaxBindings];
-
-            // Apparently the consensus is that weights are required to be normalized.
-            // More here: https://github.com/KhronosGroup/glTF/issues/1213
-
-            float weightsSum = 0;
-
-            for (int i = 0; i < pairs.Length; ++i)
-            {
-                var pair = vertex.GetJointBinding(i);
-
-                pairs[i] = pair.Weight == 0 ? default : pair;
-
-                weightsSum += pair.Weight;
-            }
-
-            // TODO: check that joints are unique, and if not, do a merge.
-
-            if (weightsSum == 0) weightsSum = 1;
-
-            JointBinding.InPlaceReverseBubbleSort(pairs);
-
-            for (int i = 0; i < pairs.Length; ++i)
-            {
-                vertex.SetJointBinding(i, pairs[i].Joint, pairs[i].Weight / weightsSum);
-            }
-
-            return vertex;
-        }
-    }
 }

+ 0 - 5
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexSkinning.cs

@@ -119,11 +119,6 @@ namespace SharpGLTF.Geometry.VertexTypes
     {
         int MaxBindings { get; }
 
-        // TODO: validation must ensure that:
-        // - there's some positive weight
-        // - every joint is unique
-        // - joints are sorted by weight
-        // - 0 weight joints point to joint 0
         void Validate();
 
         JointBinding GetJointBinding(int index);

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

@@ -282,7 +282,7 @@ namespace SharpGLTF.Geometry.VertexTypes
 
             i = 0;
 
-            while (i < Math.Min(src.MaxTextures, dst.MaxTextures))
+            while (i < Math.Min(src.MaxTextCoords, dst.MaxTextCoords))
             {
                 dst.SetTexCoord(i, src.GetTexCoord(i));
                 ++i;