Browse Source

Updated nuget packages.
Updated analyzers.
improved extensions validation.

Vicente Penades 5 years ago
parent
commit
ee789d20f5

+ 5 - 25
SharpGLTF.ruleset

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<RuleSet Name="SharpGLTF" ToolsVersion="15.0">
+<RuleSet Name="SharpGLTF" ToolsVersion="16.0">
   <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
     <Rule Id="AD0001" Action="None" />
     <Rule Id="CS1591" Action="None" />
@@ -20,7 +20,9 @@
     <Rule Id="SA1300" Action="None" />
     <Rule Id="SA1306" Action="None" />
     <Rule Id="SA1309" Action="None" />
+    <Rule Id="SA1310" Action="Info" />
     <Rule Id="SA1400" Action="None" />
+    <Rule Id="SA1401" Action="None" />
     <Rule Id="SA1402" Action="None" />
     <Rule Id="SA1405" Action="None" />
     <Rule Id="SA1413" Action="None" />
@@ -29,38 +31,16 @@
     <Rule Id="SA1502" Action="None" />
     <Rule Id="SA1503" Action="None" />
     <Rule Id="SA1512" Action="None" />
-    <Rule Id="SA1516" Action="None" />
     <Rule Id="SA1515" Action="None" />
+    <Rule Id="SA1516" Action="None" />
     <Rule Id="SA1600" Action="None" />
     <Rule Id="SA1601" Action="None" />
     <Rule Id="SA1602" Action="None" />
+    <Rule Id="SA1605" Action="None" />
     <Rule Id="SA1625" Action="None" />
     <Rule Id="SA1629" Action="None" />
     <Rule Id="SA1633" Action="None" />
     <Rule Id="SA1649" Action="None" />
     <Rule Id="SA1652" Action="None" />
-    <Rule Id="SA1605" Action="None" />
-    <Rule Id="SA1310" Action="Info" />
-  </Rules>
-  <Rules AnalyzerId="Microsoft.CodeQuality.CSharp.Analyzers" RuleNamespace="Microsoft.CodeQuality.CSharp.Analyzers">
-    <Rule Id="CA1001" Action="Error" />
-    <Rule Id="CA1032" Action="Info" />
-  </Rules>
-  <Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
-    <Rule Id="CA1051" Action="None" />
-    <Rule Id="CA1715" Action="None" />
-    <Rule Id="CA1063" Action="Error" />
-    <Rule Id="CA1815" Action="Info" />
-    <Rule Id="CA1710" Action="Info" />
-    <Rule Id="CA2225" Action="Info" />
-  </Rules>
-  <Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
-    <Rule Id="CA1816" Action="Error" />
-    <Rule Id="CA2000" Action="Error" />
-    <Rule Id="CA2213" Action="Error" />
-    <Rule Id="CA2216" Action="Error" />
-    <Rule Id="CA2242" Action="Error" />
-    <Rule Id="CA1303" Action="None" />
-    <Rule Id="CA1308" Action="None" />
   </Rules>
 </RuleSet>

+ 1 - 1
build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj

@@ -8,7 +8,7 @@
 
   <ItemGroup>
     <PackageReference Include="LibGit2Sharp" Version="0.26.2" />    
-    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="10.3.1" />
+    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="10.3.2" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
examples/Example1/Example1.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp2.2</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 1
examples/InfiniteSkinnedTentacle/InfiniteSkinnedTentacle.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp2.2</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 1
examples/PointCloudGalaxy/PointCloudGalaxy.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp2.2</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>

+ 5 - 3
src/Analyzers.props

@@ -9,9 +9,11 @@
     <AdditionalFiles Include="$(MsBuildThisFileDirectory)..\stylecop.json" />
   </ItemGroup>
 
-  <ItemGroup>    
-    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0" PrivateAssets="all" />
-    <PackageReference Include="Microsoft.CodeQuality.Analyzers" Version="3.3.0" PrivateAssets="all" />
+  <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.1">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageReference>
     <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
   </ItemGroup>
 	

+ 2 - 2
src/Shared/_Extensions.cs

@@ -338,9 +338,9 @@ namespace SharpGLTF
             }
         }
 
-        internal static IEnumerable<T> ConcatItems<T>(this IEnumerable<T> collection, params T[] instances)
+        internal static IEnumerable<T> ConcatElements<T>(this IEnumerable<T> collection, params T[] elements)
         {
-            return collection.Concat(instances.Where(item => item != null));
+            return collection.Concat(elements.Where(item => item != null));
         }
 
         public static void SanitizeNormals(this IList<Vector3> normals)

+ 25 - 7
src/SharpGLTF.Core/IO/ReadContext.cs

@@ -64,15 +64,16 @@ namespace SharpGLTF.IO
             return new ReadContext(_loadFile, _uriSolver);
         }
 
-        public static ReadContext CreateFromDictionary(IReadOnlyDictionary<string, BYTES> dictionary)
+        public static ReadContext CreateFromDictionary(IReadOnlyDictionary<string, BYTES> dictionary, bool checkExtensions = true)
         {
-            return new ReadContext(rawUri => dictionary[rawUri]);
+            return new ReadContext(rawUri => dictionary[rawUri], null, checkExtensions);
         }
 
-        private ReadContext(FileReaderCallback reader, UriResolver uriResolver = null)
+        private ReadContext(FileReaderCallback reader, UriResolver uriResolver = null, bool checkExtensions = true)
         {
             _FileReader = reader;
             _UriResolver = uriResolver;
+            _CheckSupportedExtensions = checkExtensions;
         }
 
         internal ReadContext(ReadContext other)
@@ -94,6 +95,11 @@ namespace SharpGLTF.IO
         /// </summary>
         private Byte[] _BinaryChunk;
 
+        /// <summary>
+        /// Gets a value indicating whether to check used/required extensions.
+        /// </summary>
+        internal Boolean _CheckSupportedExtensions { get; private set; } = true;
+
         #endregion
 
         #region API - File System
@@ -277,9 +283,21 @@ namespace SharpGLTF.IO
 
             // schema validation
 
-            root.ValidateReferences(vcontext.GetContext());
-            var ex = vcontext.Errors.FirstOrDefault();
-            if (ex != null) return (null, vcontext);
+            if (this._CheckSupportedExtensions)
+            {
+                root._ValidateExtensions(vcontext.GetContext());
+                var ex = vcontext.Errors.FirstOrDefault();
+                if (ex != null) return (null, vcontext);
+            }
+
+            // we must do a basic validation before resolving external dependencies
+
+            if (true)
+            {
+                root.ValidateReferences(vcontext.GetContext());
+                var ex = vcontext.Errors.FirstOrDefault();
+                if (ex != null) return (null, vcontext);
+            }
 
             // resolve external dependencies
 
@@ -290,7 +308,7 @@ namespace SharpGLTF.IO
             if (this.Validation != VALIDATIONMODE.Skip)
             {
                 root.ValidateContent(vcontext.GetContext());
-                ex = vcontext.Errors.FirstOrDefault();
+                var ex = vcontext.Errors.FirstOrDefault();
                 if (ex != null) return (null, vcontext);
             }
 

+ 8 - 1
src/SharpGLTF.Core/README.md

@@ -18,9 +18,16 @@ represent the bulk of the low level API to access glTF2 documents.
 
 It also contains the main entry point Object that represents a glTF2 model: `ModelRoot`
 
-
 [Additional info](Schema2/README.md)
 
+##### .Runtime
+
+Contains classes and types that can help evaluating a model.
+
+Model evaluation can be useful for these tasks:
+- Dumping a raw list of triangles of the whole scene, in their final positions.
+- Rendering the model on a graphics engine.
+
 ##### .Memory
 
 glTF2 stores structured arrays as encoded byte buffers that are not easy to read directly.

+ 7 - 7
src/SharpGLTF.Core/Runtime/README.md

@@ -10,7 +10,7 @@ var model = SharpGLTF.Schema2.ModelRoot.Load("model.gltf");
 ```
 
 Now, lets say you have an __AwesomeEngine__ which defines an __AwesomeMesh__ that
-is the equivalent of a __glTF Mesh__, so for each glTF Mesh we find in Model.LogicalMeshes,
+is the equivalent of a __glTF Mesh__, so for each _logical_ glTF Mesh we find in Model.LogicalMeshes,
 we create the equivalent AwesomeMesh:
 ```c#
 var gpuMeshes = new AwesomeMesh[model.LogicalMeshes.Count];
@@ -53,18 +53,18 @@ Finally, we render the instances like this:
 ```c#
 void RenderInstance(SharpGLTF.Runtime.SceneInstance modelInstance, Matrix4x4 modelMatrix)
 {
-    foreach(var drawable in modelInstance.DrawableReferences)
+    foreach(var drawable in modelInstance.DrawableInstances)
     {
-        var gpuMesh = gpuMeshes[drawable.Item1];
+        var gpuMesh = gpuMeshes[drawable.Template.LogicalMeshIndex];
 
-        if (drawable.Item2 is SharpGLTF.Transforms.StaticTransform statXform)
+        if (drawable.Transform is SharpGLTF.Transforms.RigidTransform statXform)
         {
-            AwesomeEngine.DrawMesh(gpuMesh, modelMatrix, statXform.WorldMatrix);
+            AwesomeEngine.DrawRigidMesh(gpuMesh, modelMatrix, statXform.WorldMatrix);
         }
 
-        if (drawable.Item2 is SharpGLTF.Transforms.SkinTransform skinXform)
+        if (drawable.Transform is SharpGLTF.Transforms.SkinnedLogicalMeshIndexTransform skinXform)
         {
-            AwesomeEngine.DrawMesh(gpuMesh, modelMatrix, skinXform.SkinMatrices);
+            AwesomeEngine.DrawSkinnedMesh(gpuMesh, modelMatrix, skinXform.SkinMatrices);
         }
     }
 }

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

@@ -23,7 +23,7 @@ namespace SharpGLTF.Schema2
 
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_indices, _values);
+            return base.GetLogicalChildren().ConcatElements(_indices, _values);
         }
 
         internal AccessorSparse(BufferView indices, int indicesOffset, IndexEncodingType indicesEncoding, BufferView values, int valuesOffset, int count)

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

@@ -109,7 +109,7 @@ namespace SharpGLTF.Schema2
 
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_sparse);
+            return base.GetLogicalChildren().ConcatElements(_sparse);
         }
 
         public void UpdateBounds()

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

@@ -27,7 +27,7 @@ namespace SharpGLTF.Schema2
 
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_orthographic, _perspective);
+            return base.GetLogicalChildren().ConcatElements(_orthographic, _perspective);
         }
 
         internal ICamera GetCamera()

+ 26 - 22
src/SharpGLTF.Core/Schema2/gltf.ExtensionsFactory.cs

@@ -102,21 +102,12 @@ namespace SharpGLTF.Schema2
     {
         #region properties
 
-        public bool MeshQuantizationAllowed { get; private set; }
+        public bool MeshQuantizationAllowed => this._extensionsRequired.Contains("KHR_mesh_quantization");
 
         #endregion
 
         #region API
 
-        /// <summary>
-        /// Immediatelly called after deserialization, it enables <see cref="MeshQuantizationAllowed"/>
-        /// to prevent validators to throw errors
-        /// </summary>
-        private void _FindMeshQuantizationExtension()
-        {
-            MeshQuantizationAllowed = this._extensionsRequired.Contains("KHR_mesh_quantization");
-        }
-
         internal void UpdateExtensionsSupport()
         {
             var used = RetrieveUsedExtensions();
@@ -125,12 +116,12 @@ namespace SharpGLTF.Schema2
             this._extensionsUsed.Clear();
             this._extensionsUsed.AddRange(used);
 
-            _SetRequiredExtension("KHR_mesh_quantization", MeshQuantizationAllowed);
+            _SetExtensionUsage("KHR_mesh_quantization", this._extensionsUsed.Contains("KHR_mesh_quantization"), true);
         }
 
-        private void _SetRequiredExtension(string extension, bool enabled)
+        private void _SetExtensionUsage(string extension, bool used, bool required)
         {
-            if (!enabled)
+            if (!used)
             {
                 this._extensionsUsed.Remove(extension);
                 this._extensionsRequired.Remove(extension);
@@ -138,20 +129,15 @@ namespace SharpGLTF.Schema2
             }
 
             if (!this._extensionsUsed.Contains(extension)) this._extensionsUsed.Add(extension);
-            if (!this._extensionsRequired.Contains(extension)) this._extensionsRequired.Add(extension);
-        }
-
-        internal IEnumerable<ExtraProperties> GetLogicalChildrenFlattened()
-        {
-            return GetLogicalChildren()
-                .SelectMany(item => ExtraProperties.Flatten(item)
-                .ToList());
+            if (required && !this._extensionsRequired.Contains(extension)) this._extensionsRequired.Add(extension);
         }
 
         internal IEnumerable<string> RetrieveUsedExtensions()
         {
             // retrieve ALL the property based objects of the whole model.
-            var allObjects = new[] { this }.Concat(GetLogicalChildrenFlattened());
+            var allObjects = new[] { this }
+                .Concat(GetLogicalChildrenFlattened())
+                .ToList();
 
             // check all the extensions used by each object
             var used = new HashSet<string>();
@@ -172,6 +158,11 @@ namespace SharpGLTF.Schema2
                 used.Add(unk.Name);
             }
 
+            // search for special cases
+
+            var isQuantized = MeshPrimitive.CheckAttributesQuantizationRequired(this);
+            if (isQuantized) used.Add("KHR_mesh_quantization");
+
             return used;
         }
 
@@ -185,6 +176,19 @@ namespace SharpGLTF.Schema2
             this._extensionsUsed.Add(id);
         }
 
+        internal void _ValidateExtensions(Validation.ValidationContext validate)
+        {
+            foreach (var iex in this.IncompatibleExtensions)
+            {
+                validate._LinkThrow("Extensions", iex);
+            }
+
+            foreach (var ext in RetrieveUsedExtensions())
+            {
+                if (!this._extensionsUsed.Contains(ext)) validate._LinkThrow("Extensions", ext);
+            }
+        }
+
         #endregion
     }
 }

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

@@ -60,7 +60,7 @@ namespace SharpGLTF.Schema2
 
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_normalTexture, _emissiveTexture, _occlusionTexture, _pbrMetallicRoughness);
+            return base.GetLogicalChildren().ConcatElements(_normalTexture, _emissiveTexture, _occlusionTexture, _pbrMetallicRoughness);
         }
 
         /// <summary>

+ 5 - 5
src/SharpGLTF.Core/Schema2/gltf.MaterialsFactory.cs

@@ -169,7 +169,7 @@ namespace SharpGLTF.Schema2
     {
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_baseColorTexture, _metallicRoughnessTexture);
+            return base.GetLogicalChildren().ConcatElements(_baseColorTexture, _metallicRoughnessTexture);
         }
 
         private TextureInfo _GetBaseTexture(bool create)
@@ -242,7 +242,7 @@ namespace SharpGLTF.Schema2
 
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_diffuseTexture, _specularGlossinessTexture);
+            return base.GetLogicalChildren().ConcatElements(_diffuseTexture, _specularGlossinessTexture);
         }
 
         private TextureInfo _GetDiffuseTexture(bool create)
@@ -313,7 +313,7 @@ namespace SharpGLTF.Schema2
 
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_clearcoatTexture, _clearcoatRoughnessTexture, _clearcoatNormalTexture);
+            return base.GetLogicalChildren().ConcatElements(_clearcoatTexture, _clearcoatRoughnessTexture, _clearcoatNormalTexture);
         }
 
         private TextureInfo _GetClearCoatTexture(bool create)
@@ -373,7 +373,7 @@ namespace SharpGLTF.Schema2
 
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_transmissionTexture);
+            return base.GetLogicalChildren().ConcatElements(_transmissionTexture);
         }
 
         public IEnumerable<MaterialChannel> GetChannels(Material material)
@@ -403,7 +403,7 @@ namespace SharpGLTF.Schema2
 
         protected override IEnumerable<ExtraProperties> GetLogicalChildren()
         {
-            return base.GetLogicalChildren().ConcatItems(_sheenColorTexture, _sheenRoughnessTexture);
+            return base.GetLogicalChildren().ConcatElements(_sheenColorTexture, _sheenRoughnessTexture);
         }
 
         public IEnumerable<MaterialChannel> GetChannels(Material material)

+ 41 - 0
src/SharpGLTF.Core/Schema2/gltf.MeshPrimitive.cs

@@ -256,6 +256,47 @@ namespace SharpGLTF.Schema2
 
         #region validation
 
+        internal static bool CheckAttributesQuantizationRequired(ModelRoot root)
+        {
+            return root
+                .LogicalMeshes
+                .SelectMany(item => item.Primitives)
+                .Any(prim => prim.CheckAttributesQuantizationRequired());
+        }
+
+        private bool CheckAttributesQuantizationRequired()
+        {
+            bool _checkAccessors(IReadOnlyDictionary<string, Accessor> accessors)
+            {
+                foreach (var va in accessors)
+                {
+                    if (va.Value.Encoding == EncodingType.FLOAT) continue;
+
+                    if (va.Key == "POSITION") return true;
+                    if (va.Key == "NORMAL") return true;
+                    if (va.Key == "TANGENT") return true;
+
+                    if (va.Value.Encoding == EncodingType.UNSIGNED_BYTE) continue;
+                    if (va.Value.Encoding == EncodingType.UNSIGNED_SHORT) continue;
+
+                    if (va.Key.StartsWith("TEXCOORD_")) return true;
+                }
+
+                return false;
+            }
+
+            if (_checkAccessors(this.VertexAccessors)) return true;
+
+            for (int midx = 0; midx < this.MorphTargetsCount; ++midx)
+            {
+                var mt = this.GetMorphTargetAccessors(midx);
+
+                if (_checkAccessors(mt)) return true;
+            }
+
+            return false;
+        }
+
         protected override void OnValidateReferences(Validation.ValidationContext validate)
         {
             base.OnValidateReferences(validate);

+ 8 - 10
src/SharpGLTF.Core/Schema2/gltf.Root.cs

@@ -49,7 +49,7 @@ namespace SharpGLTF.Schema2
         /// <returns>A new <see cref="ModelRoot"/> instance.</returns>
         /// <remarks>
         /// Deep cloning is performed as a brute force operation; by serializing
-        /// the whole model to GLTF into memory, and then deserializing it back.
+        /// the whole model to GLTF into memory, and then deserializing it back to DOM.
         /// </remarks>
         public ModelRoot DeepClone()
         {
@@ -67,7 +67,7 @@ namespace SharpGLTF.Schema2
 
             // restore the model from the temporary storage
 
-            var rcontext = IO.ReadContext.CreateFromDictionary(dict);
+            var rcontext = IO.ReadContext.CreateFromDictionary(dict, wcontext._UpdateSupportedExtensions);
             rcontext.Validation = Validation.ValidationMode.Skip;
             var cloned = rcontext._ReadFromDictionary("deepclone.gltf");
 
@@ -121,7 +121,7 @@ namespace SharpGLTF.Schema2
         {
             var containers = base.GetLogicalChildren();
 
-            containers = containers.ConcatItems(this.Asset);
+            containers = containers.ConcatElements(this.Asset);
             containers = containers.Concat(this.LogicalAccessors);
             containers = containers.Concat(this.LogicalAnimations);
             containers = containers.Concat(this.LogicalBuffers);
@@ -139,6 +139,11 @@ namespace SharpGLTF.Schema2
             return containers;
         }
 
+        internal IEnumerable<ExtraProperties> GetLogicalChildrenFlattened()
+        {
+            return GetLogicalChildren().SelectMany(item => Flatten(item));
+        }
+
         #endregion
 
         #region Visual Tree
@@ -172,13 +177,6 @@ namespace SharpGLTF.Schema2
 
             Asset.ValidateReferences(validate);
 
-            // check incompatible extensions
-
-            foreach (var iex in this.IncompatibleExtensions)
-            {
-                validate._LinkThrow("Extensions", iex);
-            }
-
             base.OnValidateReferences(validate);
 
             Node._ValidateParentHierarchy(this.LogicalNodes, validate);

+ 1 - 1
src/SharpGLTF.Core/Schema2/gltf.Serialization.Read.cs

@@ -209,7 +209,7 @@ namespace SharpGLTF.Schema2
 
         internal void OnDeserializationCompleted()
         {
-            _FindMeshQuantizationExtension();
+            
         }
 
         internal void _ResolveSatelliteDependencies(IO.ReadContext context)

+ 1 - 2
src/SharpGLTF.Core/SharpGLTF.Core.csproj

@@ -35,8 +35,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Update="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.1" />
-    <PackageReference Update="Microsoft.CodeQuality.Analyzers" Version="3.3.1" />
+    <None Include="..\..\.editorconfig" Link=".editorconfig" />
   </ItemGroup>  
 
 </Project>

+ 12 - 3
src/SharpGLTF.Toolkit/Schema2/AnimationExtensions.cs

@@ -30,7 +30,10 @@ namespace SharpGLTF.Schema2
                 if (degree == 1) animation.CreateScaleChannel(node, curve.ToLinearCurve(), true);
                 if (degree == 3) animation.CreateScaleChannel(node, curve.ToSplineCurve());
             }
-            else throw new ArgumentException("Must implement IConvertibleCurve<Vector3>", nameof(sampler));
+            else
+            {
+                throw new ArgumentException("Must implement IConvertibleCurve<Vector3>", nameof(sampler));
+            }
 
             return node;
         }
@@ -48,7 +51,10 @@ namespace SharpGLTF.Schema2
                 if (degree == 1) animation.CreateTranslationChannel(node, curve.ToLinearCurve(), true);
                 if (degree == 3) animation.CreateTranslationChannel(node, curve.ToSplineCurve());
             }
-            else throw new ArgumentException("Must implement IConvertibleCurve<Vector3>", nameof(sampler));
+            else
+            {
+                throw new ArgumentException("Must implement IConvertibleCurve<Vector3>", nameof(sampler));
+            }
 
             return node;
         }
@@ -85,7 +91,10 @@ namespace SharpGLTF.Schema2
                 if (degree == 1) animation.CreateRotationChannel(node, curve.ToLinearCurve(), true);
                 if (degree == 3) animation.CreateRotationChannel(node, curve.ToSplineCurve());
             }
-            else throw new ArgumentException("Must implement IConvertibleCurve<Quaternion>", nameof(sampler));
+            else
+            {
+                throw new ArgumentException("Must implement IConvertibleCurve<Quaternion>", nameof(sampler));
+            }
 
             return node;
         }

+ 0 - 5
src/SharpGLTF.Toolkit/SharpGLTF.Toolkit.csproj

@@ -20,11 +20,6 @@
 
   <ItemGroup>
     <ProjectReference Include="..\SharpGLTF.Core\SharpGLTF.Core.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Update="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.1" />
-    <PackageReference Update="Microsoft.CodeQuality.Analyzers" Version="3.3.1" />
   </ItemGroup>  
   
 </Project>

+ 44 - 5
tests/SharpGLTF.NUnit/TestFiles.cs

@@ -58,7 +58,7 @@ namespace SharpGLTF
 
         private static readonly string _SchemaDir;
         private static readonly string _ValidationDir;
-        private static readonly string _SampleModelsDir;
+        internal static readonly string _SampleModelsDir;
 
         private static readonly string _PollyModelsDir;
         private static readonly string _UniVRMModelsDir;
@@ -131,12 +131,13 @@ namespace SharpGLTF
         {
             _Check();
 
-            var files = GetModelPathsInDirectory(_SampleModelsDir, "2.0");
+            var entries = KhronosSampleModel.Load();
 
-            return files
-                .OrderBy(item => item)
-                .Where(item => !item.Contains("\\glTF-Draco\\"))
+            var files = entries
+                .SelectMany(item => item.GetPaths(_SampleModelsDir, "2.0"))
                 .ToList();
+
+            return files;            
         }
 
         public static IReadOnlyList<string> GetKhronosValidationPaths()
@@ -244,4 +245,42 @@ namespace SharpGLTF
 
         #endregion
     }
+
+    [System.Diagnostics.DebuggerDisplay("{Name}")]
+    class KhronosSampleModel
+    {
+        public static KhronosSampleModel[] Load()
+        {
+            var path = System.IO.Path.Combine(TestFiles._SampleModelsDir, "2.0", "model-index.json");
+            var text = System.IO.File.ReadAllText(path);
+            return Read(text);
+        }
+
+        public static KhronosSampleModel[] Read(string json)
+        {
+            var opts = new System.Text.Json.JsonSerializerOptions
+            {
+                PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase,
+                PropertyNameCaseInsensitive = true
+            };
+
+            return System.Text.Json.JsonSerializer.Deserialize<KhronosSampleModel[]>(json, opts);
+        }
+
+        public string Name { get; set; }
+        public string Screenshot { get; set; }
+        public Dictionary<string, string> Variants { get; set; } = new Dictionary<string, string>();
+
+        public IEnumerable<string> GetPaths(params string[] basePath)
+        {
+            var rootPath = System.IO.Path.Combine(basePath);
+
+            foreach(var variant in Variants)
+            {
+                if (variant.Key == "glTF-Draco") continue; // draco is not supported by SharpGLTF
+
+                yield return System.IO.Path.Combine(rootPath, Name, variant.Key, variant.Value);
+            }
+        }
+    }
 }

+ 2 - 2
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadGeneratedTests.cs

@@ -36,7 +36,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
             foreach (var filePath in files)
             {
-                // System.Diagnostics.Debug.Assert(!filePath.EndsWith("Compatibility_05.gltf"));
+                // System.Diagnostics.Debug.Assert(!filePath.EndsWith("Buffer_Interleaved_03.gltf"));
 
                 var gltfJson = filePath.EndsWith(".gltf") ? System.IO.File.ReadAllText(filePath) : string.Empty;
 
@@ -71,7 +71,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
                     else
                     {
                         TestContext.WriteLine($"{filePath.ToShortDisplayPath()} 🙂👍");
-                        TestContext.WriteLine($"   Exception: {ex.Message}");
+                        TestContext.WriteLine($"   Expected Exception: {ex.Message}");
                     }                    
                 }
 

+ 1 - 0
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSampleTests.cs

@@ -95,6 +95,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
         [TestCase("\\glTF-IBL\\")]
         [TestCase("\\glTF-Binary\\")]
         [TestCase("\\glTF-Embedded\\")]
+        [TestCase("\\glTF-Quantized\\")]
         [TestCase("\\glTF-pbrSpecularGlossiness\\")]
         public void LoadModelsFromKhronosSamples(string section)
         {

+ 1 - 1
tests/SharpGLTF.Tests/SharpGLTF.Core.Tests.csproj

@@ -26,7 +26,7 @@
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
   </ItemGroup>  
 
 </Project>

+ 1 - 1
tests/SharpGLTF.Toolkit.Tests/SharpGLTF.Toolkit.Tests.csproj

@@ -26,7 +26,7 @@
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
   </ItemGroup>  
 
 </Project>