Selaa lähdekoodia

More validation improvements... (WIP)

Vicente Penades 5 vuotta sitten
vanhempi
sitoutus
893020e114

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

@@ -357,14 +357,7 @@ namespace SharpGLTF.Schema2
 
             BufferView.VerifyAccess(validate, this.SourceBufferView, this.ByteOffset, this.Format, this.Count);
 
-            try
-            {
-                MemoryAccessor.VerifyAccessorBounds(_GetMemoryAccessor(), _min, _max);
-            }
-            catch (ArgumentException ex)
-            {
-                validate._DataThrow(ex.ParamName, ex.Message);
-            }
+            validate.That(() => MemoryAccessor.VerifyAccessorBounds(_GetMemoryAccessor(), _min, _max));
 
             // at this point we don't know which kind of data we're accessing, so it's up to the components
             // using this accessor to validate the data.
@@ -379,14 +372,7 @@ namespace SharpGLTF.Schema2
 
             validate.AreEqual(nameof(SourceBufferView.ByteStride), SourceBufferView.ByteStride, 0); // "bufferView.byteStride must not be defined for indices accessor.";
 
-            try
-            {
-                MemoryAccessor.VerifyVertexIndices(_GetMemoryAccessor(), vertexCount);
-            }
-            catch (ArgumentException ex)
-            {
-                validate._DataThrow(ex.ParamName, ex.Message);
-            }
+            validate.That(() => MemoryAccessor.VerifyVertexIndices(_GetMemoryAccessor(), vertexCount));
         }
 
         internal static void ValidateVertexAttributes(VALIDATIONCTX validate, IReadOnlyDictionary<string, Accessor> attributes, int skinsMaxJointCount)
@@ -496,14 +482,7 @@ namespace SharpGLTF.Schema2
             var memory0 = weights0?._GetMemoryAccessor("WEIGHTS_0");
             var memory1 = weights1?._GetMemoryAccessor("WEIGHTS_1");
 
-            try
-            {
-                MemoryAccessor.VerifyWeightsSum(memory0, memory1);
-            }
-            catch (ArgumentException ex)
-            {
-                validate._DataThrow(ex.ParamName, ex.Message);
-            }
+            validate.That(() => MemoryAccessor.VerifyWeightsSum(memory0, memory1));
         }
 
         private void _ValidateWeights(VALIDATIONCTX validate)

+ 3 - 6
src/SharpGLTF.Core/Schema2/gltf.Asset.cs

@@ -93,13 +93,10 @@ namespace SharpGLTF.Schema2
         {
             base.OnValidateContent(validate);
 
-            if (!Version.TryParse(_version, out Version ver))
-            {
-                validate._SchemaThrow("Version", $"Unknown glTF major asset version: {_version}.");
-                return;
-            }
+            validate.IsTrue(nameof(Version), Version.TryParse(_version, out Version ver), $"Unknown glTF major asset version: {_version}.");
+
+            validate.IsGreaterOrEqual(nameof(Version), Version, MINVERSION);
 
-            if (Version < MINVERSION) validate._SchemaThrow("Version", $"Minimum supported version is {MINVERSION} but found:{MinVersion}");
             // if (MinVersion > MAXVERSION) result.AddSemanticError( $"Maximum supported version is {MAXVERSION} but found:{MinVersion}");
         }
 

+ 13 - 5
src/SharpGLTF.Core/Schema2/gltf.Buffer.cs

@@ -120,9 +120,7 @@ namespace SharpGLTF.Schema2
             {
                 validate
                     .NotNull(nameof(binaryChunk), binaryChunk)
-                    .IsGreaterOrEqual(nameof(_byteLength), _byteLength, _byteLengthMinimum)
-                    .IsLessOrEqual(nameof(_byteLength), _byteLength, binaryChunk.Length);
-                    // result.CheckSchemaIsMultipleOf("ByteLength", _byteLength, 4);
+                    .IsLessOrEqual("ByteLength", _byteLength, binaryChunk.Length);
             }
             else
             {
@@ -131,11 +129,21 @@ namespace SharpGLTF.Schema2
             }
         }
 
+        protected override void OnValidateReferences(Validation.ValidationContext validate)
+        {
+            validate.IsGreaterOrEqual("ByteLength", _byteLength, _byteLengthMinimum);
+            // result.CheckSchemaIsMultipleOf("ByteLength", _byteLength, 4);
+
+            base.OnValidateReferences(validate);
+        }
+
         protected override void OnValidateContent(Validation.ValidationContext validate)
         {
-            base.OnValidateContent(validate);
+            validate
+                .NotNull("Content", _Content)
+                .IsLessOrEqual("ByteLength", _byteLength, _Content.Length);
 
-            validate.IsGreaterOrEqual("ByteLength", _Content.Length, _byteLength); // $"Actual data length {_Content.Length} is less than the declared buffer byteLength {_byteLength}.");
+            base.OnValidateContent(validate);
         }
 
         #endregion

+ 30 - 25
src/SharpGLTF.Core/Schema2/gltf.Camera.cs

@@ -63,7 +63,7 @@ namespace SharpGLTF.Schema2
         /// <param name="zfar">Distance to the far plane in the Z axis.</param>
         public void SetOrthographicMode(float xmag, float ymag, float znear, float zfar)
         {
-            CameraOrthographic.CheckParameters(xmag, ymag, znear, zfar);
+            CameraOrthographic.VerifyParameters(xmag, ymag, znear, zfar);
 
             this._perspective = null;
             this._orthographic = new CameraOrthographic(xmag, ymag, znear, zfar);
@@ -80,7 +80,7 @@ namespace SharpGLTF.Schema2
         /// <param name="zfar">Distance to the far plane in the Z axis.</param>
         public void SetPerspectiveMode(float? aspectRatio, float yfov, float znear, float zfar)
         {
-            CameraPerspective.CheckParameters(aspectRatio, yfov, znear, zfar);
+            CameraPerspective.VerifyParameters(aspectRatio, yfov, znear, zfar);
 
             this._orthographic = null;
             this._perspective = new CameraPerspective(aspectRatio, yfov, znear, zfar);
@@ -94,15 +94,19 @@ namespace SharpGLTF.Schema2
 
         protected override void OnValidateReferences(Validation.ValidationContext validate)
         {
-            base.OnValidateReferences(validate);
-
-            if (_orthographic == null && _perspective == null) validate._LinkThrow("perspective", "Missing orthographic or perspective");
+            if (_type == CameraType.perspective)
+            {
+                validate.IsDefined("perspective", _perspective);
+                validate.IsUndefined("orthographic", _orthographic);
+            }
 
-            if (_orthographic != null && _perspective != null)
+            if (_type == CameraType.orthographic)
             {
-                if (validate.TryFix) _orthographic = null;
-                else validate._LinkThrow("perspective", "orthographic and perspective are mutually exclusive");
+                validate.IsUndefined("perspective", _perspective);
+                validate.IsDefined("orthographic", _orthographic);
             }
+
+            base.OnValidateReferences(validate);
         }
 
         #endregion
@@ -171,15 +175,16 @@ namespace SharpGLTF.Schema2
 
         #region API
 
-        public static void CheckParameters(float xmag, float ymag, float znear, float zfar)
+        public static void VerifyParameters(float xmag, float ymag, float znear, float zfar)
         {
-            Guard.MustBeGreaterThan(xmag, 0, nameof(xmag));
-            Guard.MustBeGreaterThan(ymag, 0, nameof(ymag));
-
-            Guard.MustBeGreaterThanOrEqualTo(znear, 0, nameof(znear));
-            Guard.MustBeGreaterThanOrEqualTo(zfar, 0, nameof(zfar));
+            Guard.MustBeGreaterThanOrEqualTo(znear, (float)_znearMinimum, nameof(znear));
+            Guard.MustBeGreaterThanOrEqualTo(zfar, (float)_zfarMinimum, nameof(zfar));
             Guard.MustBeGreaterThan(zfar, znear, nameof(zfar));
             Guard.MustBeLessThan(zfar, float.PositiveInfinity, nameof(zfar));
+
+            // these are considered warnings
+            // Guard.MustBeGreaterThan(xmag, 0, nameof(xmag));
+            // Guard.MustBeGreaterThan(ymag, 0, nameof(ymag));
         }
 
         #endregion
@@ -188,9 +193,9 @@ namespace SharpGLTF.Schema2
 
         protected override void OnValidateContent(Validation.ValidationContext validate)
         {
-            base.OnValidateContent(validate);
+            validate.That(() => VerifyParameters(this.XMag, this.YMag, this.ZNear, this.ZFar));
 
-            validate.IsGreater(nameof(ZFar), ZFar, ZNear); // "ZFar must be greater than ZNear");
+            base.OnValidateContent(validate);
         }
 
         #endregion
@@ -205,7 +210,7 @@ namespace SharpGLTF.Schema2
 
         internal CameraPerspective(float? aspectRatio, float yfov, float znear, float zfar)
         {
-            CheckParameters(aspectRatio, yfov, znear, zfar);
+            VerifyParameters(aspectRatio, yfov, znear, zfar);
 
             this._aspectRatio = aspectRatio ?? null;
             this._yfov = yfov;
@@ -253,14 +258,14 @@ namespace SharpGLTF.Schema2
 
         #region API
 
-        public static void CheckParameters(float? aspectRatio, float yfov, float znear, float zfar = float.PositiveInfinity)
+        public static void VerifyParameters(float? aspectRatio, float yfov, float znear, float zfar = float.PositiveInfinity)
         {
-            if (aspectRatio.HasValue) Guard.MustBeGreaterThanOrEqualTo(aspectRatio.Value, 0, nameof(aspectRatio));
-            Guard.MustBeGreaterThan(yfov, 0, nameof(yfov));
-            Guard.MustBeLessThan(yfov, (float)Math.PI, nameof(yfov));
+            Guard.MustBeGreaterThanOrEqualTo(aspectRatio.AsValue(1), (float)_aspectRatioMinimum, nameof(aspectRatio));
+            Guard.MustBeGreaterThan(yfov, (float)_yfovMinimum, nameof(yfov));
+            // Guard.MustBeLessThan(yfov, (float)Math.PI, nameof(yfov));
 
-            Guard.MustBeGreaterThanOrEqualTo(znear, 0, nameof(znear));
-            Guard.MustBeGreaterThanOrEqualTo(zfar, 0, nameof(zfar));
+            Guard.MustBeGreaterThanOrEqualTo(znear, (float)_znearMinimum, nameof(znear));
+            Guard.MustBeGreaterThanOrEqualTo(zfar, (float)_zfarMinimum, nameof(zfar));
             Guard.MustBeGreaterThan(zfar, znear, nameof(zfar));
         }
 
@@ -270,9 +275,9 @@ namespace SharpGLTF.Schema2
 
         protected override void OnValidateContent(Validation.ValidationContext validate)
         {
-            base.OnValidateContent(validate);
+            validate.That(() => VerifyParameters(this.AspectRatio, this.VerticalFOV, this.ZNear, this.ZFar));
 
-            validate.IsGreater(nameof(ZFar), ZFar, ZNear); // "ZFar must be greater than ZNear");
+            base.OnValidateContent(validate);
         }
 
         #endregion

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

@@ -122,18 +122,18 @@ namespace SharpGLTF.Schema2
 
         #region validation
 
-        protected override void OnValidateReferences(Validation.ValidationContext result)
+        protected override void OnValidateReferences(Validation.ValidationContext validate)
         {
-            base.OnValidateReferences(result);
+            base.OnValidateReferences(validate);
 
             foreach (var lc in this.GetLogicalChildren())
             {
-                lc.ValidateReferences(result);
+                lc.ValidateReferences(validate);
             }
 
-            foreach (var ext in this.Extensions) ext.ValidateReferences(result);
+            foreach (var ext in this.Extensions) ext.ValidateReferences(validate);
 
-            if (this._extras is JsonSerializable js) js.ValidateReferences(result);
+            if (this._extras is JsonSerializable js) js.ValidateReferences(validate);
         }
 
         protected override void OnValidateContent(Validation.ValidationContext validate)

+ 7 - 10
src/SharpGLTF.Core/Schema2/gltf.Mesh.cs

@@ -102,18 +102,15 @@ namespace SharpGLTF.Schema2
 
         protected override void OnValidateContent(Validation.ValidationContext validate)
         {
-            base.OnValidateContent(validate);
-
-            var morphTargetsCount = this.Primitives
-                .Select(item => item.MorphTargetsCount)
-                .Distinct();
-
-            validate.AreEqual("Morph targets", morphTargetsCount.Count(), 1);
-
-            if (_weights.Count != 0 && this.Primitives.Count > 0)
+            if (_weights.Count > 0)
             {
-                validate.AreEqual("Morph targets", _weights.Count, this.Primitives[0].MorphTargetsCount);
+                foreach (var p in this.Primitives)
+                {
+                    validate.GetContext(p).AreEqual("MorphTargetsCount", p.MorphTargetsCount, _weights.Count);
+                }
             }
+
+            base.OnValidateContent(validate);
         }
 
         #endregion

+ 4 - 8
src/SharpGLTF.Core/Schema2/gltf.MeshPrimitive.cs

@@ -279,18 +279,14 @@ namespace SharpGLTF.Schema2
 
             // all vertices must have the same vertex count
 
-            var vertexCounts = VertexAccessors
-                .Select(item => item.Value.Count)
-                .Distinct();
+            int vertexCount = -1;
 
-            if (vertexCounts.Skip(1).Any())
+            foreach (var va in VertexAccessors)
             {
-                validate._LinkThrow("Attributes", "All accessors of the same primitive must have the same count.");
-                return;
+                if (vertexCount < 0) { vertexCount = va.Value.Count; continue; }
+                validate.AreEqual(va.Key, va.Value.Count, vertexCount);
             }
 
-            var vertexCount = vertexCounts.First();
-
             // check indices
 
             if (IndexAccessor != null)

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

@@ -240,17 +240,13 @@ namespace SharpGLTF.Schema2
 
         protected override void OnValidateReferences(Validation.ValidationContext validate)
         {
-            base.OnValidateReferences(validate);
-
             validate
                 .IsNullOrIndex("Skeleton", _skeleton, this.LogicalParent.LogicalNodes)
-                .IsNullOrIndex("InverseBindMatrices", _inverseBindMatrices, this.LogicalParent.LogicalAccessors);
+                .IsNullOrIndex("InverseBindMatrices", _inverseBindMatrices, this.LogicalParent.LogicalAccessors)
+                .IsGreaterOrEqual("Joints", _joints.Count, _jointsMinItems); // althought mathematically correct, there's no point in having a skin with just one node.
 
-            if (_joints.Count < _jointsMinItems)
-            {
-                // result.AddError(this, $"Expected at least {_jointsMinItems} Joints");
-                return;
-            }
+            var ibxAccessor = GetInverseBindMatricesAccessor();
+            if (ibxAccessor != null) validate.AreEqual("InverseBindMatrices", ibxAccessor.Count, _joints.Count);
 
             Node commonRoot = null;
 
@@ -267,12 +263,12 @@ namespace SharpGLTF.Schema2
 
                 validate.GetContext(jroot).AreSameReference("Root", commonRoot, jroot);
             }
+
+            base.OnValidateReferences(validate);
         }
 
         protected override void OnValidateContent(Validation.ValidationContext validate)
         {
-            base.OnValidateContent(validate);
-
             var ibxAccessor = GetInverseBindMatricesAccessor();
 
             if (ibxAccessor != null)
@@ -296,6 +292,8 @@ namespace SharpGLTF.Schema2
                     // result.AddError(this, $"Skeleton node is not a common ancestor of Joint[{i}]");
                 }
             }
+
+            base.OnValidateContent(validate);
         }
 
         #endregion

+ 19 - 10
src/SharpGLTF.Core/Schema2/khr.lights.cs

@@ -180,24 +180,35 @@ namespace SharpGLTF.Schema2
 
         #endregion
 
+        #region API
+
+        protected override IEnumerable<ExtraProperties> GetLogicalChildren()
+        {
+            var children = base.GetLogicalChildren();
+
+            if (_spot != null) children = children.Concat(new[] { _spot });
+
+            return children;
+        }
+
+        #endregion
+
         #region Validation
 
         protected override void OnValidateReferences(Validation.ValidationContext validate)
         {
-            base.OnValidateReferences(validate);
-
-            if (string.IsNullOrEmpty(_type)) { validate._SchemaThrow("Type", "light Type must be defined"); return; }
+            validate.IsAnyOf("Type", _type, "directional", "point", "spot");
 
             if (LightType == PunctualLightType.Spot) validate.IsDefined("Spot", _spot);
 
-            _spot?.ValidateReferences(validate);
+            base.OnValidateReferences(validate);
         }
 
         protected override void OnValidateContent(Validation.ValidationContext validate)
         {
-            base.OnValidateContent(validate);
+            validate.IsDefaultOrWithin(nameof(Intensity), _intensity, _intensityMinimum, float.MaxValue);
 
-            _spot?.ValidateContent(validate);
+            base.OnValidateContent(validate);
         }
 
         #endregion
@@ -220,10 +231,8 @@ namespace SharpGLTF.Schema2
         protected override void OnValidateContent(Validation.ValidationContext validate)
         {
             validate
-                .IsGreaterOrEqual(nameof(InnerConeAngle), InnerConeAngle, (Single)_innerConeAngleMinimum)
-                .IsLessOrEqual(nameof(InnerConeAngle), InnerConeAngle, (Single)_innerConeAngleMaximum)
-                .IsGreaterOrEqual(nameof(OuterConeAngle), OuterConeAngle, (Single)_outerConeAngleMinimum)
-                .IsLessOrEqual(nameof(OuterConeAngle), OuterConeAngle, (Single)_outerConeAngleMaximum)
+                .IsDefaultOrWithin(nameof(InnerConeAngle), InnerConeAngle, (Single)_innerConeAngleMinimum, (Single)_innerConeAngleMaximum)
+                .IsDefaultOrWithin(nameof(OuterConeAngle), OuterConeAngle, (Single)_outerConeAngleMinimum, (Single)_outerConeAngleMaximum)
                 .IsLess(nameof(InnerConeAngle), InnerConeAngle, OuterConeAngle);
 
             base.OnValidateContent(validate);

+ 33 - 2
src/SharpGLTF.Core/Validation/ValidationContext.Guards.cs

@@ -10,6 +10,7 @@ namespace SharpGLTF.Validation
     using PARAMNAME = ValueLocation;
 
     [System.Diagnostics.DebuggerDisplay("{_Current}")]
+    [System.Diagnostics.DebuggerStepThrough]
     partial struct ValidationContext
     {
         private readonly IO.JsonSerializable _Current;
@@ -19,6 +20,12 @@ namespace SharpGLTF.Validation
         [System.Diagnostics.DebuggerStepThrough]
         internal void _SchemaThrow(PARAMNAME pname, string msg) { throw new SchemaException(_Current, $"{pname}: {msg}"); }
 
+        public OUTTYPE IsTrue(PARAMNAME parameterName, bool value, string msg)
+        {
+            if (!value) _SchemaThrow(parameterName, msg);
+            return this;
+        }
+
         public OUTTYPE NotNull(PARAMNAME parameterName, object target)
         {
             if (target == null) _SchemaThrow(parameterName, "must not be null.");
@@ -39,9 +46,10 @@ namespace SharpGLTF.Validation
             return this;
         }
 
-        public OUTTYPE IsTrue(PARAMNAME parameterName, bool value, string msg)
+        public OUTTYPE IsUndefined<T>(PARAMNAME parameterName, T value)
+            where T : class
         {
-            if (!value) _SchemaThrow(parameterName, msg);
+            if (value != null) _SchemaThrow(parameterName, "must NOT be defined.");
             return this;
         }
 
@@ -87,6 +95,15 @@ namespace SharpGLTF.Validation
             return this;
         }
 
+        public OUTTYPE IsDefaultOrWithin<TValue>(PARAMNAME parameterName, TValue? value, TValue minInclusive, TValue maxInclusive)
+                where TValue : unmanaged, IComparable<TValue>
+        {
+            if (!value.HasValue) return this;
+            if (value.Value.CompareTo(minInclusive) < 0) _SchemaThrow(parameterName, $"{value} must be greater or equal to {minInclusive}.");
+            if (value.Value.CompareTo(maxInclusive) > 0) _SchemaThrow(parameterName, $"{value} must be less or equal to {maxInclusive}.");
+            return this;
+        }
+
         public OUTTYPE IsGreaterOrEqual<TValue>(PARAMNAME parameterName, TValue value, TValue min)
                 where TValue : IComparable<TValue>
         {
@@ -327,6 +344,20 @@ namespace SharpGLTF.Validation
             return this;
         }
 
+        public OUTTYPE That(Action action)
+        {
+            try
+            {
+                action.Invoke();
+            }
+            catch (ArgumentException ex)
+            {
+                _DataThrow(ex.ParamName, ex.Message);
+            }
+
+            return this;
+        }
+
         #endregion
     }
 }

+ 0 - 1
src/SharpGLTF.Core/Validation/ValidationContext.cs

@@ -10,7 +10,6 @@ namespace SharpGLTF.Validation
     /// <summary>
     /// Utility class used in the process of model validation.
     /// </summary>
-    // [System.Diagnostics.DebuggerStepThrough]
     public readonly partial struct ValidationContext
     {
         #region constructor

+ 2 - 2
src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs

@@ -139,7 +139,7 @@ namespace SharpGLTF.Schema2
         public static Node WithPerspectiveCamera(this Node node, float? aspectRatio, float fovy, float znear, float zfar = float.PositiveInfinity)
         {
             Guard.NotNull(node, nameof(node));
-            CameraPerspective.CheckParameters(aspectRatio, fovy, znear, zfar);
+            CameraPerspective.VerifyParameters(aspectRatio, fovy, znear, zfar);
 
             var camera = node.LogicalParent.CreateCamera();
             camera.SetPerspectiveMode(aspectRatio, fovy, znear, zfar);
@@ -152,7 +152,7 @@ namespace SharpGLTF.Schema2
         public static Node WithOrthographicCamera(this Node node, float xmag, float ymag, float znear, float zfar)
         {
             Guard.NotNull(node, nameof(node));
-            CameraOrthographic.CheckParameters(xmag, ymag, znear, zfar);
+            CameraOrthographic.VerifyParameters(xmag, ymag, znear, zfar);
 
             var camera = node.LogicalParent.CreateCamera();
             camera.SetOrthographicMode(xmag, ymag, znear, zfar);