Browse Source

Merge pull request #218 from bertt/add_3dtiles_nodata

add 3d tiles metadata noData and default support
Vicente Penades 1 year ago
parent
commit
036ba944b9

+ 1 - 1
src/SharpGLTF.Ext.3DTiles/Schema2/Ext.Features.cs

@@ -336,7 +336,7 @@ namespace SharpGLTF.Schema2
                     {
                     {
                         var expectedTexCoordAttribute = $"TEXCOORD_{texture.TextureCoordinate}";
                         var expectedTexCoordAttribute = $"TEXCOORD_{texture.TextureCoordinate}";
                         var vertex = _meshPrimitive.GetVertexAccessor(expectedTexCoordAttribute);
                         var vertex = _meshPrimitive.GetVertexAccessor(expectedTexCoordAttribute);
-                        var distinctFeatureIds = vertex.AsVector2Array().Count();
+                        var distinctFeatureIds = vertex.AsVector2Array().Count;
 
 
                         Guard.IsTrue(featureId.FeatureCount == distinctFeatureIds, $"Mismatch between FeatureCount ({featureId.FeatureCount}) and Feature Texture ({distinctFeatureIds})");
                         Guard.IsTrue(featureId.FeatureCount == distinctFeatureIds, $"Mismatch between FeatureCount ({featureId.FeatureCount}) and Feature Texture ({distinctFeatureIds})");
 
 

+ 206 - 81
src/SharpGLTF.Ext.3DTiles/Schema2/Ext.StructuralMetadataRoot.cs

@@ -233,7 +233,6 @@ namespace SharpGLTF.Schema2
                     var regex = "^[a-zA-Z_][a-zA-Z0-9_]*$";
                     var regex = "^[a-zA-Z_][a-zA-Z0-9_]*$";
                     Guard.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(Schema.Id, regex), nameof(Schema.Id));
                     Guard.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(Schema.Id, regex), nameof(Schema.Id));
 
 
-
                     foreach (var _class in Schema.Classes)
                     foreach (var _class in Schema.Classes)
                     {
                     {
                         Guard.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(_class.Key, regex), nameof(_class.Key));
                         Guard.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(_class.Key, regex), nameof(_class.Key));
@@ -244,6 +243,17 @@ namespace SharpGLTF.Schema2
                             {
                             {
                                 Guard.MustBeGreaterThanOrEqualTo(property.Value.Count.Value, 2, nameof(property.Value.Count));
                                 Guard.MustBeGreaterThanOrEqualTo(property.Value.Count.Value, 2, nameof(property.Value.Count));
                             }
                             }
+
+                            if (property.Value.Required)
+                            {
+                                Guard.IsTrue(property.Value.NoData == null, nameof(property.Value.NoData), $"The property '{property.Key}' defines a 'noData' value, but is 'required'");
+                            }
+
+                            if(property.Value.Type == ELEMENTTYPE.SCALAR)
+                            {
+                                // check The 'componentType' must be defined for a property with type 'SCALAR'
+                                Guard.IsTrue(property.Value.ComponentType.HasValue, nameof(property.Value.ComponentType), $"The 'componentType' must be defined for a property '{property.Key}' with type 'SCALAR'");
+                            }
                         }
                         }
                     }
                     }
                 }
                 }
@@ -806,7 +816,6 @@ namespace SharpGLTF.Schema2
 
 
                 if (elementType == ELEMENTTYPE.ENUM)
                 if (elementType == ELEMENTTYPE.ENUM)
                 {
                 {
-                    // guard the type of t is an short in case of enum
                     Guard.IsTrue(typeof(T) == typeof(short), nameof(T), $"Enum value type of {LogicalKey} must be short");
                     Guard.IsTrue(typeof(T) == typeof(short), nameof(T), $"Enum value type of {LogicalKey} must be short");
                 }
                 }
                 else if (elementType == ELEMENTTYPE.SCALAR)
                 else if (elementType == ELEMENTTYPE.SCALAR)
@@ -1227,83 +1236,78 @@ namespace SharpGLTF.Schema2
             #endregion
             #endregion
 
 
             #region properties
             #region properties
+
             public string Name
             public string Name
             {
             {
                 get => _name;
                 get => _name;
-                set => _name = value;
             }
             }
 
 
             public string Description
             public string Description
             {
             {
                 get => _description;
                 get => _description;
-                set => _description = value;
             }
             }
 
 
             internal ELEMENTTYPE Type
             internal ELEMENTTYPE Type
             {
             {
                 get => _type;
                 get => _type;
-                set => _type = value;
             }
             }
 
 
             public string EnumType
             public string EnumType
             {
             {
                 get => _enumType;
                 get => _enumType;
-                // set => _enumType = value;
             }
             }
 
 
             public DATATYPE? ComponentType
             public DATATYPE? ComponentType
             {
             {
                 get => _componentType;
                 get => _componentType;
-                set => _componentType = value;
             }
             }
 
 
             public bool Required
             public bool Required
             {
             {
                 get => _required ?? _requiredDefault;
                 get => _required ?? _requiredDefault;
-                set => _required = value.AsNullable(_requiredDefault);
+            }
+
+            public JsonNode NoData
+            {
+                get => _noData;
             }
             }
 
 
             public bool Normalized
             public bool Normalized
             {
             {
                 get => _normalized ?? _normalizedDefault;
                 get => _normalized ?? _normalizedDefault;
-                set => _normalized = value.AsNullable(_normalizedDefault);
             }
             }
 
 
             public bool Array
             public bool Array
             {
             {
                 get => _array ?? _arrayDefault;
                 get => _array ?? _arrayDefault;
-                set => _array = value.AsNullable(_arrayDefault);
+                internal set => _array = value.AsNullable(_arrayDefault);
             }
             }
 
 
             public int? Count
             public int? Count
             {
             {
                 get => _count;
                 get => _count;
-                set => _count = value;
+                internal set => _count = value;
             }
             }
 
 
             /** Commented out for now, as it is not supported
             /** Commented out for now, as it is not supported
             public JsonNode Min
             public JsonNode Min
             {
             {
                 get => _min;
                 get => _min;
-                set => _min = value;
             }
             }
 
 
             public JsonNode Max
             public JsonNode Max
             {
             {
                 get => _max;
                 get => _max;
-                set => _max = value;
             }
             }
 
 
             public JsonNode Scale
             public JsonNode Scale
             {
             {
                 get => _scale;
                 get => _scale;
-                set => _scale = value;
             }
             }
 
 
             public JsonNode Offset
             public JsonNode Offset
             {
             {
                 get => _offset;
                 get => _offset;
-                set => _offset = value;
             }
             }
             */
             */
 
 
@@ -1315,167 +1319,288 @@ namespace SharpGLTF.Schema2
 
 
             public StructuralMetadataClassProperty WithName(string name)
             public StructuralMetadataClassProperty WithName(string name)
             {
             {
-                Name = name;
+                _name = name;
                 return this;
                 return this;
             }
             }
 
 
             public StructuralMetadataClassProperty WithDescription(string description)
             public StructuralMetadataClassProperty WithDescription(string description)
             {
             {
-                Description = description;
+                _description = description;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithStringType()
+            public StructuralMetadataClassProperty WithStringType(string noData = null, string defaultValue = null)
             {
             {
-                Type = ElementType.STRING;
+                _type = ElementType.STRING;
+                if (noData != null) _noData = noData;
+                if(defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
             public StructuralMetadataClassProperty WithBooleanType()
             public StructuralMetadataClassProperty WithBooleanType()
             {
             {
-                Type = ElementType.BOOLEAN;
+                _type = ElementType.BOOLEAN;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithUInt8Type()
+            public StructuralMetadataClassProperty WithUInt8Type(byte? noData = null, byte? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.UINT8;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.UINT8;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithInt8Type()
+            public StructuralMetadataClassProperty WithInt8Type(sbyte? noData = null, sbyte? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.INT8;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.INT8;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithUInt16Type()
+            public StructuralMetadataClassProperty WithUInt16Type(ushort? noData = null, ushort? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.UINT16;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.UINT16;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithInt16Type()
+            public StructuralMetadataClassProperty WithInt16Type(short? noData = null, short? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.INT16;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.INT16;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithUInt32Type()
+            public StructuralMetadataClassProperty WithUInt32Type(uint? noData = null, uint? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.UINT32;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.UINT32;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithInt32Type()
+            public StructuralMetadataClassProperty WithInt32Type(int? noData = null, int? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.INT32;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.INT32;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithUInt64Type()
+            public StructuralMetadataClassProperty WithUInt64Type(ulong? noData = null, ulong? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.UINT64;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.UINT64;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithInt64Type()
+            public StructuralMetadataClassProperty WithInt64Type(long? noData = null, long? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.INT64;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.INT64;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithFloat32Type()
+            public StructuralMetadataClassProperty WithFloat32Type(float? noData = null, float? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.FLOAT32;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.FLOAT32;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithFloat64Type()
+            public StructuralMetadataClassProperty WithFloat64Type(double? noData = null, double? defaultValue = null)
             {
             {
-                Type = ELEMENTTYPE.SCALAR;
-                ComponentType = DATATYPE.FLOAT64;
+                _type = ELEMENTTYPE.SCALAR;
+                _componentType = DATATYPE.FLOAT64;
+                if (noData != null) _noData = noData;
+                if (defaultValue != null) _default = defaultValue;
                 return this;
                 return this;
             }
             }
 
 
 
 
-            public StructuralMetadataClassProperty WithVector3Type()
+            public StructuralMetadataClassProperty WithVector3Type(Vector3? noData = null, Vector3? defaultValue = null)
             {
             {
-                Type = ElementType.VEC3;
-                ComponentType = DataType.FLOAT32;
+                _type = ElementType.VEC3;
+                _componentType = DataType.FLOAT32;
+
+                if (noData != null)
+                {
+                    _noData = new JsonArray(noData.Value.X, noData.Value.Y, noData.Value.Z);
+                }
+                if (defaultValue != null)
+                {
+                    _default = new JsonArray(defaultValue.Value.X, defaultValue.Value.Y, defaultValue.Value.Z);
+                }
+
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithMatrix4x4Type()
+            public StructuralMetadataClassProperty WithMatrix4x4Type(Matrix4x4? noData = null, Matrix4x4? defaultValue = null)
             {
             {
-                Type = ElementType.MAT4;
-                ComponentType = DataType.FLOAT32;
+                _type = ElementType.MAT4;
+                _componentType = DataType.FLOAT32;
+
+                if (noData != null)
+                {
+                    _noData = ToJsonArray(noData.Value);
+                }
+
+                if (defaultValue != null)
+                {
+                    _default = ToJsonArray(defaultValue.Value);
+                }
+
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithCount()
+            public StructuralMetadataClassProperty WithBooleanArrayType(int? count = null)
             {
             {
-                Type = ElementType.MAT4;
-                ComponentType = DataType.FLOAT32;
-                return this;
+                var property = WithArrayType(ELEMENTTYPE.BOOLEAN, null, count);
+                return property;
+            }
+
+            public StructuralMetadataClassProperty WithUInt8ArrayType(int? count = null, byte? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.UINT8, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
+
+            public StructuralMetadataClassProperty WithInt8ArrayType(int? count = null, sbyte? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.INT8, count);
+                if (noData != null) property._noData = noData;
+                return property;
             }
             }
 
 
+            public StructuralMetadataClassProperty WithInt16ArrayType(int? count = null, short? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.INT16, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
 
 
-            //public StructuralMetadataClassProperty WithValueType(ELEMENTTYPE etype, DATATYPE? ctype = null)
-            //{
-            //    Type = etype;
-            //    ComponentType = ctype;
-            //    Array = false;
-            //    return this;
-            //}
+            public StructuralMetadataClassProperty WithUInt16ArrayType(int? count = null, ushort? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.UINT16, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
+            public StructuralMetadataClassProperty WithInt32ArrayType(int? count = null, int? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.INT32, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
+            public StructuralMetadataClassProperty WithUInt32ArrayType(int? count = null, uint? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.UINT32, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
+            public StructuralMetadataClassProperty WithInt64ArrayType(int? count = null, long? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.INT64, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
+            public StructuralMetadataClassProperty WithUInt64ArrayType(int? count = null, ulong? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.UINT64, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
+            public StructuralMetadataClassProperty WithFloat32ArrayType(int? count = null, float? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.FLOAT32, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
+            public StructuralMetadataClassProperty WithFloat64ArrayType(int? count = null, double? noData = null)
+            {
+                var property = WithArrayType(ELEMENTTYPE.SCALAR, DATATYPE.FLOAT64, count);
+                if (noData != null) property._noData = noData;
+                return property;
+            }
 
 
-            public StructuralMetadataClassProperty WithArrayType(ELEMENTTYPE etype, DATATYPE? ctype = null, int? count = null)
+            public StructuralMetadataClassProperty WithVector3ArrayType(int? count = null, Vector3? noData = null)
             {
             {
-                Type = etype;
-                ComponentType = ctype;
-                Array = true;
-                Count = count;
-                return this;
+                var property = WithArrayType(ELEMENTTYPE.VEC3, DATATYPE.FLOAT32, count);
+                return property;
+            }
+            public StructuralMetadataClassProperty WithMatrix4x4ArrayType(int? count = null)
+            {
+                return WithArrayType(ELEMENTTYPE.MAT4, DATATYPE.FLOAT32, count);
             }
             }
 
 
-            public StructuralMetadataClassProperty WithEnumArrayType(StructuralMetadataEnum enumeration, int? count = null)
+            public StructuralMetadataClassProperty WithStringArrayType(int? count = null)
             {
             {
-                Type = ELEMENTTYPE.ENUM;
+                return WithArrayType(ELEMENTTYPE.STRING, null, count);
+            }
+
+            public StructuralMetadataClassProperty WithEnumArrayType(StructuralMetadataEnum enumeration, int? count = null, string noData = null)
+            {
+                _type = ELEMENTTYPE.ENUM;
                 _enumType = enumeration.LogicalKey;
                 _enumType = enumeration.LogicalKey;
-                Array = true;
-                Count = count;
+                _array = true;
+                _count = count;
+                if (noData != null) _noData = noData;
                 return this;
                 return this;
             }
             }
 
 
-            public StructuralMetadataClassProperty WithEnumeration(StructuralMetadataEnum enumeration)
+            public StructuralMetadataClassProperty WithEnumeration(StructuralMetadataEnum enumeration, string noData = null)
             {
             {
-                Type = ELEMENTTYPE.ENUM;
+                _type = ELEMENTTYPE.ENUM;
                 _enumType = enumeration.LogicalKey;
                 _enumType = enumeration.LogicalKey;
+                if (noData != null) _noData = noData;
                 return this;
                 return this;
             }
             }
 
 
             public StructuralMetadataClassProperty WithRequired(bool required)
             public StructuralMetadataClassProperty WithRequired(bool required)
             {
             {
-                Required = required;
+                _required = required;
                 return this;
                 return this;
             }
             }
 
 
             public StructuralMetadataClassProperty WithNormalized(bool normalized)
             public StructuralMetadataClassProperty WithNormalized(bool normalized)
             {
             {
-                Normalized = normalized;
+                _normalized = normalized;
                 return this;
                 return this;
             }
             }
 
 
+            private StructuralMetadataClassProperty WithArrayType(ELEMENTTYPE etype, DATATYPE? ctype = null, int? count = null)
+            {
+                _type = etype;
+                _componentType = ctype;
+                _array = true;
+                _count = count;
+                return this;
+            }
 
 
+            private static JsonArray ToJsonArray(Matrix4x4 m4)
+            {
+                return new JsonArray(
+                    m4.M11, m4.M12, m4.M13, m4.M14,
+                    m4.M21, m4.M22, m4.M23, m4.M24,
+                    m4.M31, m4.M32, m4.M33, m4.M34,
+                    m4.M41, m4.M42, m4.M43, m4.M44);
+            }
             #endregion
             #endregion
         }
         }
 
 

+ 178 - 3
tests/SharpGLTF.Ext.3DTiles.Tests/ExtStructuralMetadataTests.cs

@@ -82,6 +82,181 @@ namespace SharpGLTF.Schema2.Tiles3D
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// In this test a single triangle is defined, it has attributes defined for all types with a noData value, 
+        /// but the values are set to the noData value. In CesiumJS the triangle is rendered but the 
+        /// attritutes are not shown (because noData).
+        /// </summary>
+        [Test(Description = "MetadataAndNullValuesAttributeSample")]
+        public void MetadataNullValuesAttributeSample()
+        {
+            TestContext.CurrentContext.AttachGltfValidatorLinks();
+
+            int featureId = 0;
+            var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
+
+            var mesh = new MeshBuilder<VertexPositionNormal, VertexWithFeatureId, VertexEmpty>("mesh");
+            var prim = mesh.UsePrimitive(material);
+
+            var vt0 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 0, 0), new Vector3(0, 0, 1), featureId);
+            var vt1 = VertexBuilder.GetVertexWithFeatureId(new Vector3(1, 0, 0), new Vector3(0, 0, 1), featureId);
+            var vt2 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 1, 0), new Vector3(0, 0, 1), featureId);
+
+            prim.AddTriangle(vt0, vt1, vt2);
+            var scene = new SceneBuilder();
+            scene.AddRigidMesh(mesh, Matrix4x4.Identity);
+            var model = scene.ToGltf2();
+
+            var rootMetadata = model.UseStructuralMetadata();
+            var schema = rootMetadata.UseEmbeddedSchema("schema_001");
+
+            var schemaClass = schema.UseClassMetadata("triangles");
+
+            var speciesEnum = schema.UseEnumMetadata("speciesEnum", ("Unspecified", 0), ("Oak", 1), ("Pine", 2), ("Maple", 3));
+            speciesEnum.Name = "Species";
+            speciesEnum.Description = "An example enum for tree species.";
+
+            var descriptionProperty = schemaClass
+                    .UseProperty("description")
+                    .WithStringType();
+
+            // for this property, the default value (byte.MaxValue) should be shown in the client when the actual value is 
+            // equal to the noData value (byte.MinValue)
+            var uint8Property = schemaClass
+                .UseProperty("uint8")
+                .WithUInt8Type(byte.MinValue, byte.MaxValue);
+
+            var int8Property = schemaClass
+                .UseProperty("int8")
+                .WithInt8Type(sbyte.MinValue);
+
+            var int16Property = schemaClass
+                .UseProperty("int16")
+                .WithInt16Type(short.MinValue);
+
+            var uint16Property = schemaClass
+                .UseProperty("uint16")
+                .WithUInt16Type(ushort.MinValue);
+
+            var int32Property = schemaClass
+                .UseProperty("int32")
+                .WithInt32Type(int.MinValue);
+
+            var uint32Property = schemaClass
+                .UseProperty("uint32")
+                .WithUInt32Type(uint.MinValue);
+
+            var int64Property = schemaClass
+                .UseProperty("int64")
+                .WithInt64Type(long.MinValue);
+
+            var uint64Property = schemaClass
+                .UseProperty("uint64")
+                .WithUInt64Type(ulong.MinValue);
+
+            // when using float.MinValue there is an error in the validator: ""The value has type FLOAT32 and must be in [-3.4028234663852886e+38,3.4028234663852886e+38], but is -3.4028235e+38"
+            // And the noData value is shown in CesiumJS. Therefore we use -10.0f here.
+            var float32Property = schemaClass
+                .UseProperty("float32")
+                .WithFloat32Type(-10.0f);
+
+            var float64Property = schemaClass
+                .UseProperty("float64")
+                .WithFloat64Type(double.MinValue);
+
+            var stringProperty = schemaClass
+                .UseProperty("string")
+                .WithStringType("noData", "-");
+
+            var speciesProperty = schemaClass
+                .UseProperty("species")
+                .WithDescription("Type of tree.")
+                .WithEnumeration(speciesEnum, "Unspecified")
+                .WithRequired(false);
+
+            var vector3Property = schemaClass
+                .UseProperty("vector3")
+                .WithVector3Type(new Vector3(-10.0f, -10.0f, -10.0f));
+
+            var matrix4x4Property = schemaClass
+                .UseProperty("matrix4x4")
+                .WithMatrix4x4Type(Matrix4x4.Identity * -10);
+
+            var propertyTable = schemaClass.AddPropertyTable(1);
+
+            propertyTable
+                .UseProperty(descriptionProperty)
+                .SetValues("Description of the triangle");
+
+            propertyTable
+                .UseProperty(uint8Property)
+                .SetValues(byte.MinValue);
+
+            propertyTable
+                .UseProperty(int8Property)
+                .SetValues(sbyte.MinValue);
+
+            propertyTable
+                .UseProperty(int16Property)
+                .SetValues(short.MinValue);
+
+            propertyTable
+                .UseProperty(uint16Property)
+                .SetValues(ushort.MinValue);
+
+            propertyTable
+                .UseProperty(int32Property)
+                .SetValues(int.MinValue);
+
+            propertyTable
+                .UseProperty(uint32Property)
+                .SetValues(uint.MinValue);
+
+            propertyTable
+                .UseProperty(int64Property)
+                .SetValues(long.MinValue);
+
+            propertyTable
+                .UseProperty(uint64Property)
+                .SetValues(ulong.MinValue);
+
+            propertyTable
+                .UseProperty(float32Property)
+                .SetValues(-10f);
+
+            propertyTable
+                .UseProperty(float64Property)
+                .SetValues(double.MinValue);
+
+            propertyTable
+                .UseProperty(stringProperty)
+                .SetValues("noData");
+
+            propertyTable
+                .UseProperty(speciesProperty)
+                .SetValues((short)0);
+
+            propertyTable
+                .UseProperty(vector3Property)
+                .SetValues(new Vector3(10.0f,10.0f,10.0f));
+
+            var m4 = Matrix4x4.Identity;
+            propertyTable
+                .UseProperty(matrix4x4Property)
+                .SetValues(m4);
+
+            foreach (var primitive in model.LogicalMeshes[0].Primitives)
+            {
+                var featureIdAttribute = new FeatureIDBuilder(1, 0, propertyTable);
+                primitive.AddMeshFeatureIds(featureIdAttribute);
+            }
+
+            // create files
+            var ctx = new ValidationResult(model, ValidationMode.Strict, true);
+            model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.glb");
+            model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.gltf");
+            model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.plotly");
+        }
 
 
         [Test(Description = "MinimalMetadataAttributeSample")]
         [Test(Description = "MinimalMetadataAttributeSample")]
         public void MinimalMetadataAttributeSample()
         public void MinimalMetadataAttributeSample()
@@ -681,21 +856,21 @@ namespace SharpGLTF.Schema2.Tiles3D
                 .UseProperty("example_variable_length_ARRAY_normalized_UINT8")
                 .UseProperty("example_variable_length_ARRAY_normalized_UINT8")
                 .WithName("Example variable-length ARRAY normalized INT8 property")
                 .WithName("Example variable-length ARRAY normalized INT8 property")
                 .WithDescription("An example property, with type ARRAY, with component type UINT8, normalized, and variable length")
                 .WithDescription("An example property, with type ARRAY, with component type UINT8, normalized, and variable length")
-                .WithArrayType(ElementType.SCALAR, DataType.UINT8)
+                .WithUInt8ArrayType()
                 .WithNormalized(false);
                 .WithNormalized(false);
 
 
             var fixedLengthBooleanProperty = exampleMetadataClass
             var fixedLengthBooleanProperty = exampleMetadataClass
                 .UseProperty("example_fixed_length_ARRAY_BOOLEAN")
                 .UseProperty("example_fixed_length_ARRAY_BOOLEAN")
                 .WithName("Example fixed-length ARRAY BOOLEAN property")
                 .WithName("Example fixed-length ARRAY BOOLEAN property")
                 .WithDescription("An example property, with type ARRAY, with component type BOOLEAN, and fixed length ")
                 .WithDescription("An example property, with type ARRAY, with component type BOOLEAN, and fixed length ")
-                .WithArrayType(ElementType.BOOLEAN, null, 4)
+                .WithBooleanArrayType(4)
                 .WithNormalized(false);
                 .WithNormalized(false);
 
 
             var variableLengthStringArrayProperty = exampleMetadataClass
             var variableLengthStringArrayProperty = exampleMetadataClass
                 .UseProperty("example_variable_length_ARRAY_STRING")
                 .UseProperty("example_variable_length_ARRAY_STRING")
                 .WithName("Example variable-length ARRAY STRING property")
                 .WithName("Example variable-length ARRAY STRING property")
                 .WithDescription("An example property, with type ARRAY, with component type STRING, and variable length")
                 .WithDescription("An example property, with type ARRAY, with component type STRING, and variable length")
-                .WithArrayType(ElementType.STRING);
+                .WithStringArrayType();
 
 
             var fixed_length_ARRAY_ENUM = exampleMetadataClass
             var fixed_length_ARRAY_ENUM = exampleMetadataClass
                 .UseProperty("example_fixed_length_ARRAY_ENUM")
                 .UseProperty("example_fixed_length_ARRAY_ENUM")