From b7cc008326a9cabb55a1f29dd1e6078794798fac Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Tue, 14 Nov 2023 15:31:12 +0100 Subject: [PATCH 01/14] initial code for ext_instance_features --- build/SharpGLTF.CodeGen/Constants.cs | 7 ++ .../Ext.EXT_InstanceFeatures.cs | 33 ++++++ build/SharpGLTF.CodeGen/Program.cs | 4 +- .../Schemas/EXT_instance_features/README.md | 8 ++ .../schema/featureId.schema.json | 43 +++++++ .../schema/featureIdAttribute.schema.json | 8 ++ .../node.EXT_instance_features.schema.json | 27 +++++ .../SharpGLTF.CodeGen.csproj | 12 ++ .../Schema2/CesiumExtensions.cs | 1 + .../Ext.CESIUM_ext_instance_features.g.cs | 110 ++++++++++++++++++ .../Schema2/MeshExtInstanceFeatures.cs | 63 ++++++++++ .../ExtInstanceFeaturesTests.cs | 65 +++++++++++ 12 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs create mode 100644 build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/README.md create mode 100644 build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/featureId.schema.json create mode 100644 build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/featureIdAttribute.schema.json create mode 100644 build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/node.EXT_instance_features.schema.json create mode 100644 src/SharpGLTF.Cesium/Schema2/Generated/Ext.CESIUM_ext_instance_features.g.cs create mode 100644 src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs create mode 100644 tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs diff --git a/build/SharpGLTF.CodeGen/Constants.cs b/build/SharpGLTF.CodeGen/Constants.cs index 335e7510..eecb3950 100644 --- a/build/SharpGLTF.CodeGen/Constants.cs +++ b/build/SharpGLTF.CodeGen/Constants.cs @@ -15,6 +15,8 @@ static class Constants /// public static string LocalRepoDirectory => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(typeof(Program).Assembly.Location), "glTF"); + public static string ProgramDirectory => System.IO.Path.GetDirectoryName(typeof(Program).Assembly.Location); + #endregion #region main schema paths @@ -51,6 +53,11 @@ internal static string VendorExtensionPath(string ext, string json) return System.IO.Path.Combine(VendorSchemaDir, ext, "schema", json); } + internal static string CustomExtensionsPath(string ext, string json) + { + return System.IO.Path.Combine(ProgramDirectory, "Schemas", ext, "schema", json); + } + #endregion #region code generation output paths diff --git a/build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs b/build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs new file mode 100644 index 00000000..b8a20de4 --- /dev/null +++ b/build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs @@ -0,0 +1,33 @@ +using SharpGLTF.CodeGen; +using SharpGLTF.SchemaReflection; +using System.Collections.Generic; + +namespace SharpGLTF +{ + class ExtInstanceFeaturesExtension : SchemaProcessor + { + public override string GetTargetProject() { return Constants.CesiumProjectDirectory; } + + private static string RootSchemaUri => Constants.CustomExtensionsPath("EXT_instance_features", "node.EXT_instance_features.schema.json"); + + public override void PrepareTypes(CSharpEmitter newEmitter, SchemaType.Context ctx) + { + newEmitter.SetRuntimeName("EXT_instance_features glTF Node extension", "MeshExtInstanceFeatures"); + newEmitter.SetRuntimeName("Feature ID in EXT_instance_features", "MeshExtInstanceFeatureID"); + } + + public override IEnumerable<(string TargetFileName, SchemaType.Context Schema)> Process() + { + yield return ("Ext.CESIUM_ext_instance_features.g", ProcessNode()); + } + + private static SchemaType.Context ProcessNode() + { + var ctx = SchemaProcessing.LoadSchemaContext(RootSchemaUri); + ctx.IgnoredByCodeEmitter("glTF Property"); + ctx.IgnoredByCodeEmitter("glTF Child of Root Property"); + ctx.IgnoredByCodeEmitter("Texture Info"); + return ctx; + } + } +} \ No newline at end of file diff --git a/build/SharpGLTF.CodeGen/Program.cs b/build/SharpGLTF.CodeGen/Program.cs index 0722c9bb..0ffa1666 100644 --- a/build/SharpGLTF.CodeGen/Program.cs +++ b/build/SharpGLTF.CodeGen/Program.cs @@ -21,7 +21,7 @@ static void Main(string[] args) processors.Add(new MainSchemaProcessor()); - // ---------------------------------------------- Add extensions + //// ---------------------------------------------- Add extensions // material extensions processors.Add(new UnlitExtension()); @@ -56,6 +56,8 @@ static void Main(string[] args) // other processors.Add(new XmpJsonLdExtension()); + processors.Add(new ExtInstanceFeaturesExtension()); + // ---------------------------------------------- process all files foreach (var processor in processors) diff --git a/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/README.md b/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/README.md new file mode 100644 index 00000000..0f6ec6c9 --- /dev/null +++ b/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/README.md @@ -0,0 +1,8 @@ +# EXT_instance_features + +This directory contains schema's for Cesium extension +EXT_instance_features + +Specs: https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_instance_features + +Schema's are copied from https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_instance_features/schema diff --git a/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/featureId.schema.json b/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/featureId.schema.json new file mode 100644 index 00000000..bc237fcb --- /dev/null +++ b/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/featureId.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "featureId.schema.json", + "title": "Feature ID in EXT_instance_features", + "type": "object", + "description": "Feature IDs stored in a GPU mesh instancing attribute", + "allOf": [ + { + "$ref": "glTFProperty.schema.json" + } + ], + "properties": { + "featureCount": { + "type": "integer", + "minimum": 1, + "description": "The number of unique features in the attribute." + }, + "nullFeatureId": { + "type": "integer", + "minimum": 0, + "description": "A value that indicates that no feature is associated with this instance." + }, + "label": { + "type": "string", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", + "description": "A label assigned to this feature ID set. Labels must be alphanumeric identifiers matching the regular expression `^[a-zA-Z_][a-zA-Z0-9_]*$`." + }, + "attribute": { + "description": "An attribute containing feature IDs. When this is omitted, then the feature IDs are assigned to the GPU instances by their index.", + "$ref": "featureIdAttribute.schema.json" + }, + "propertyTable": { + "type": "integer", + "minimum": 0, + "description": "The index of the property table containing per-feature property values. Only applicable when using the `EXT_structural_metadata` extension." + }, + "extensions": {}, + "extras": {} + }, + "required": [ + "featureCount" + ] +} \ No newline at end of file diff --git a/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/featureIdAttribute.schema.json b/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/featureIdAttribute.schema.json new file mode 100644 index 00000000..6e1bc8d2 --- /dev/null +++ b/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/featureIdAttribute.schema.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "featureIdAttribute.schema.json", + "title": "Feature ID Attribute in EXT_instance_features", + "type": "integer", + "minimum": 0, + "description": "An integer value used to construct a string in the format `_FEATURE_ID_` which is a reference to a key in `EXT_mesh_gpu_instancing.attributes` dictionary (e.g. a value of `0` corresponds to `_FEATURE_ID_0`)." +} \ No newline at end of file diff --git a/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/node.EXT_instance_features.schema.json b/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/node.EXT_instance_features.schema.json new file mode 100644 index 00000000..d49f6d97 --- /dev/null +++ b/build/SharpGLTF.CodeGen/Schemas/EXT_instance_features/schema/node.EXT_instance_features.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "node.EXT_instance_features.schema.json", + "title": "EXT_instance_features glTF Node extension", + "type": "object", + "description": "An object describing per-instance feature IDs.", + "allOf": [ + { + "$ref": "glTFProperty.schema.json" + } + ], + "properties": { + "featureIds": { + "type": "array", + "description": "An array of feature ID sets.", + "minItems": 1, + "items": { + "$ref": "featureId.schema.json" + } + }, + "extensions": {}, + "extras": {} + }, + "required": [ + "featureIds" + ] +} \ No newline at end of file diff --git a/build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj b/build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj index 54a037ab..92936f5e 100644 --- a/build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj +++ b/build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj @@ -11,4 +11,16 @@ + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + diff --git a/src/SharpGLTF.Cesium/Schema2/CesiumExtensions.cs b/src/SharpGLTF.Cesium/Schema2/CesiumExtensions.cs index c013bf5f..83f3bee2 100644 --- a/src/SharpGLTF.Cesium/Schema2/CesiumExtensions.cs +++ b/src/SharpGLTF.Cesium/Schema2/CesiumExtensions.cs @@ -21,6 +21,7 @@ public static void RegisterExtensions() _CesiumRegistered = true; ExtensionsFactory.RegisterExtension("CESIUM_primitive_outline"); + ExtensionsFactory.RegisterExtension("EXT_instance_features"); } } } diff --git a/src/SharpGLTF.Cesium/Schema2/Generated/Ext.CESIUM_ext_instance_features.g.cs b/src/SharpGLTF.Cesium/Schema2/Generated/Ext.CESIUM_ext_instance_features.g.cs new file mode 100644 index 00000000..14f20c7b --- /dev/null +++ b/src/SharpGLTF.Cesium/Schema2/Generated/Ext.CESIUM_ext_instance_features.g.cs @@ -0,0 +1,110 @@ +// + +//------------------------------------------------------------------------------------------------ +// This file has been programatically generated; DON´T EDIT! +//------------------------------------------------------------------------------------------------ + +#pragma warning disable SA1001 +#pragma warning disable SA1027 +#pragma warning disable SA1028 +#pragma warning disable SA1121 +#pragma warning disable SA1205 +#pragma warning disable SA1309 +#pragma warning disable SA1402 +#pragma warning disable SA1505 +#pragma warning disable SA1507 +#pragma warning disable SA1508 +#pragma warning disable SA1652 + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Numerics; +using System.Text.Json; + +namespace SharpGLTF.Schema2 +{ + using Collections; + + /// + /// Feature IDs stored in a GPU mesh instancing attribute + /// + #if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + #endif + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("SharpGLTF.CodeGen", "1.0.0.0")] + partial class MeshExtInstanceFeatureID : ExtraProperties + { + + private Int32? _attribute; + + private const Int32 _featureCountMinimum = 1; + private Int32 _featureCount; + + private String _label; + + private const Int32 _nullFeatureIdMinimum = 0; + private Int32? _nullFeatureId; + + private const Int32 _propertyTableMinimum = 0; + private Int32? _propertyTable; + + + protected override void SerializeProperties(Utf8JsonWriter writer) + { + base.SerializeProperties(writer); + SerializeProperty(writer, "attribute", _attribute); + SerializeProperty(writer, "featureCount", _featureCount); + SerializeProperty(writer, "label", _label); + SerializeProperty(writer, "nullFeatureId", _nullFeatureId); + SerializeProperty(writer, "propertyTable", _propertyTable); + } + + protected override void DeserializeProperty(string jsonPropertyName, ref Utf8JsonReader reader) + { + switch (jsonPropertyName) + { + case "attribute": _attribute = DeserializePropertyValue(ref reader); break; + case "featureCount": _featureCount = DeserializePropertyValue(ref reader); break; + case "label": _label = DeserializePropertyValue(ref reader); break; + case "nullFeatureId": _nullFeatureId = DeserializePropertyValue(ref reader); break; + case "propertyTable": _propertyTable = DeserializePropertyValue(ref reader); break; + default: base.DeserializeProperty(jsonPropertyName,ref reader); break; + } + } + + } + + /// + /// An object describing per-instance feature IDs. + /// + #if NET6_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + #endif + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("SharpGLTF.CodeGen", "1.0.0.0")] + partial class MeshExtInstanceFeatures : ExtraProperties + { + + private const int _featureIdsMinItems = 1; + private List _featureIds; + + + protected override void SerializeProperties(Utf8JsonWriter writer) + { + base.SerializeProperties(writer); + SerializeProperty(writer, "featureIds", _featureIds, _featureIdsMinItems); + } + + protected override void DeserializeProperty(string jsonPropertyName, ref Utf8JsonReader reader) + { + switch (jsonPropertyName) + { + case "featureIds": DeserializePropertyList(ref reader, _featureIds); break; + default: base.DeserializeProperty(jsonPropertyName,ref reader); break; + } + } + + } + +} diff --git a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs new file mode 100644 index 00000000..9e7e76d4 --- /dev/null +++ b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs @@ -0,0 +1,63 @@ +using SharpGLTF.Validation; +using System.Collections.Generic; +using System.Linq; + +namespace SharpGLTF.Schema2 +{ + public partial class MeshExtInstanceFeatures + { + private Node _node; + internal MeshExtInstanceFeatures(Node node) + { + _node = node; + _featureIds = new List(); + } + + public List FeatureIds + { + get + { + return _featureIds; + } + set + { + if (value == null) { _featureIds = null; return; } + + _featureIds = value; + } + } + + protected override void OnValidateContent(ValidationContext validate) + { + var extInstanceFeatures = (MeshExtInstanceFeatures)_node.Extensions.FirstOrDefault(); + + validate.NotNull(nameof(FeatureIds), extInstanceFeatures.FeatureIds); + validate.IsTrue(nameof(FeatureIds), extInstanceFeatures.FeatureIds.Count > 0, "Instance FeatureIds has items"); + + base.OnValidateContent(validate); + } + } + + public partial class MeshExtInstanceFeatureID + { + public MeshExtInstanceFeatureID(int featureCount, int? attribute = null, int? propertyTable = null, string label = null, int? nullFeatureId = null) + { + _featureCount = featureCount; + _attribute = attribute; + _label = label; + _propertyTable = propertyTable; + _nullFeatureId = nullFeatureId; + } + } + + public static class ExtInstanceFeatures + { + public static void SetFeatureIds(this Node node, List list) + { + if (list == null) { node.RemoveExtensions(); return; } + + var ext = node.UseExtension(); + ext.FeatureIds = list; + } + } +} diff --git a/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs b/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs new file mode 100644 index 00000000..cce1d25a --- /dev/null +++ b/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs @@ -0,0 +1,65 @@ +using NUnit.Framework; +using SharpGLTF.Geometry; +using SharpGLTF.Geometry.VertexTypes; +using SharpGLTF.Materials; +using SharpGLTF.Scenes; +using SharpGLTF.Schema2; +using SharpGLTF.Validation; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace SharpGLTF.Cesium +{ + [Category("Toolkit.Scenes")] + public class ExtInstanceFeaturesTests + { + [SetUp] + public void SetUp() + { + CesiumExtensions.RegisterExtensions(); + } + + [Test(Description = "Creates a simple triangle with Cesium EXT_Instance_Features")] + public void AddExtInstanceFeatures() + { + TestContext.CurrentContext.AttachGltfValidatorLinks(); + + // todo: create a test with ext_mesh_gpu_instancing + + var material = MaterialBuilder.CreateDefault(); + + var mesh = new MeshBuilder("mesh"); + + var prim = mesh.UsePrimitive(material); + prim.AddTriangle(new VertexPosition(-10, 0, 0), new VertexPosition(10, 0, 0), new VertexPosition(0, 10, 0)); + + var scene = new SceneBuilder(); + + scene.AddRigidMesh(mesh, Matrix4x4.Identity); + + var model = scene.ToGltf2(); + + // following the sample at https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_instance_features#feature-id-by-gpu-instance + var featureId0 = new MeshExtInstanceFeatureID(2, 0, 0, "Forests",2); + var featureId1 = new MeshExtInstanceFeatureID(9, propertyTable: 1, label: "Trees"); + + var featureIds = new List() { featureId0, featureId1 }; + model.LogicalNodes[0].SetFeatureIds(featureIds); + + var cesiumExtInstanceFeaturesExtension = (MeshExtInstanceFeatures)model.LogicalNodes[0].Extensions.FirstOrDefault(); + Assert.NotNull(cesiumExtInstanceFeaturesExtension.FeatureIds); + + Assert.IsTrue(cesiumExtInstanceFeaturesExtension.FeatureIds.Equals(featureIds)); + Assert.IsTrue(cesiumExtInstanceFeaturesExtension.FeatureIds[0].Equals(featureId0)); + Assert.IsTrue(cesiumExtInstanceFeaturesExtension.FeatureIds[1].Equals(featureId1)); + + var ctx = new ValidationResult(model, ValidationMode.Strict, true); + model.ValidateContent(ctx.GetContext()); + + scene.AttachToCurrentTest("cesium_ext_instance_features.glb"); + scene.AttachToCurrentTest("cesium_ext_instance_features.gltf"); + scene.AttachToCurrentTest("cesium_ext_instance_features.plotly"); + } + } +} From f16732d15c913f6e0c982a6f09d0da3a703812aa Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 13:49:32 +0100 Subject: [PATCH 02/14] undo commenting --- build/SharpGLTF.CodeGen/Program.cs | 72 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/build/SharpGLTF.CodeGen/Program.cs b/build/SharpGLTF.CodeGen/Program.cs index 38a2ca92..655b1f24 100644 --- a/build/SharpGLTF.CodeGen/Program.cs +++ b/build/SharpGLTF.CodeGen/Program.cs @@ -23,42 +23,42 @@ static void Main(string[] args) processors.Add(new MainSchemaProcessor()); - //// ---------------------------------------------- Add extensions - - //// material extensions - //processors.Add(new UnlitExtension()); - //processors.Add(new IorExtension()); - //processors.Add(new SheenExtension()); - //processors.Add(new VolumeExtension()); - //processors.Add(new SpecularExtension()); - //processors.Add(new ClearCoatExtension()); - //processors.Add(new IridescenceExtension()); - //processors.Add(new TransmissionExtension()); - //processors.Add(new EmissiveStrengthExtension()); - //processors.Add(new SpecularGlossinessExtension()); - - //// cesium outlines - //processors.Add(new CesiumPrimitiveOutlineExtension()); - - //// lights - //processors.Add(new LightsPunctualExtension()); - - //// gpu mesh instancing - //processors.Add(new MeshGpuInstancingExtension()); - - //// textures - //processors.Add(new TextureTransformExtension()); - //processors.Add(new TextureDDSExtension()); - //processors.Add(new TextureWebpExtension()); - //processors.Add(new TextureKtx2Extension()); - - //processors.Add(new AgiArticulationsExtension()); - //processors.Add(new AgiStkMetadataExtension()); - - //// other - //processors.Add(new XmpJsonLdExtension()); - - //processors.Add(new ExtMeshFeaturesExtension()); + // ---------------------------------------------- Add extensions + + // material extensions + processors.Add(new UnlitExtension()); + processors.Add(new IorExtension()); + processors.Add(new SheenExtension()); + processors.Add(new VolumeExtension()); + processors.Add(new SpecularExtension()); + processors.Add(new ClearCoatExtension()); + processors.Add(new IridescenceExtension()); + processors.Add(new TransmissionExtension()); + processors.Add(new EmissiveStrengthExtension()); + processors.Add(new SpecularGlossinessExtension()); + + // cesium outlines + processors.Add(new CesiumPrimitiveOutlineExtension()); + + // lights + processors.Add(new LightsPunctualExtension()); + + // gpu mesh instancing + processors.Add(new MeshGpuInstancingExtension()); + + // textures + processors.Add(new TextureTransformExtension()); + processors.Add(new TextureDDSExtension()); + processors.Add(new TextureWebpExtension()); + processors.Add(new TextureKtx2Extension()); + + processors.Add(new AgiArticulationsExtension()); + processors.Add(new AgiStkMetadataExtension()); + + // other + processors.Add(new XmpJsonLdExtension()); + + processors.Add(new ExtMeshFeaturesExtension()); processors.Add(new ExtInstanceFeaturesExtension()); From 8fbe6072966e162e6ca789f00978d17bfff57961 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 15:36:22 +0100 Subject: [PATCH 03/14] add validation and guards --- .../Schema2/MeshExtInstanceFeatures.cs | 72 +++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs index 9e7e76d4..844e7338 100644 --- a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs +++ b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs @@ -6,7 +6,7 @@ namespace SharpGLTF.Schema2 { public partial class MeshExtInstanceFeatures { - private Node _node; + private Node _node; internal MeshExtInstanceFeatures(Node node) { _node = node; @@ -48,16 +48,80 @@ public MeshExtInstanceFeatureID(int featureCount, int? attribute = null, int? pr _propertyTable = propertyTable; _nullFeatureId = nullFeatureId; } + + /// + /// The number of unique features in the attribute + /// + public int FeatureCount { get => _featureCount; } + + /// + /// An attribute containing feature IDs. When this is omitted, then the feature IDs are assigned to the GPU instances by their index. + /// + public int? Attribute { get => _attribute; } + + /// + /// A label assigned to this feature ID set + /// + public string Label { get => _label; } + + /// + /// The index of the property table containing per-feature property values. Only applicable when using the `EXT_structural_metadata` extension. + /// + public int? PropertyTable { get => _propertyTable; } + + /// + /// A value that indicates that no feature is associated with this instance + /// + public int? NullFeatureId { get => _nullFeatureId; } } public static class ExtInstanceFeatures { - public static void SetFeatureIds(this Node node, List list) + /// + /// Set the instance feature ids for this node. + /// + /// + /// + public static void SetFeatureIds(this Node node, List instanceFeatureIds) { - if (list == null) { node.RemoveExtensions(); return; } + if (instanceFeatureIds == null) { node.RemoveExtensions(); return; } + + Guard.NotNullOrEmpty(instanceFeatureIds, nameof(instanceFeatureIds)); + + foreach (var instanceFeatureId in instanceFeatureIds) + { + ValidateInstanceFeatureId(node, instanceFeatureId); + }; var ext = node.UseExtension(); - ext.FeatureIds = list; + ext.FeatureIds = instanceFeatureIds; + } + + private static void ValidateInstanceFeatureId(Node node, MeshExtInstanceFeatureID item) + { + Guard.MustBeGreaterThanOrEqualTo((int)item.FeatureCount, 1, nameof(item.FeatureCount)); + + if (item.NullFeatureId.HasValue) + { + Guard.MustBeGreaterThanOrEqualTo((int)item.NullFeatureId, 0, nameof(item.NullFeatureId)); + } + if (item.Label != null) + { + var regex = "^[a-zA-Z_][a-zA-Z0-9_]*$"; + Guard.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(item.Label, regex), nameof(item.Label)); + } + if (item.Attribute.HasValue) + { + Guard.MustBeGreaterThanOrEqualTo((int)item.Attribute, 0, nameof(item.Attribute)); + // todo: check attribute + // Guard that the custom vertex attribute (_FEATURE_ID_{attribute}) exists when FeatureID has attribute set + // var expectedVertexAttribute = $"_FEATURE_ID_{item.Attribute}"; + // Guard.NotNull(primitive.GetVertexAccessor(expectedVertexAttribute), expectedVertexAttribute); + } + if (item.PropertyTable.HasValue) + { + Guard.MustBeGreaterThanOrEqualTo((int)item.PropertyTable, 0, nameof(item.PropertyTable)); + } } } } From ce996e690ee01200fcd5f525b7b9ece17f7d4944 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 16:19:42 +0100 Subject: [PATCH 04/14] create test with gpu instancing and ext_instance_features --- .../Schema2/MeshExtInstanceFeatures.cs | 6 ++- .../ExtInstanceFeaturesTests.cs | 47 +++++++++--------- .../SharpGLTF.Cesium.Tests.csproj | 6 +++ tests/SharpGLTF.Cesium.Tests/tree.glb | Bin 0 -> 20220 bytes 4 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 tests/SharpGLTF.Cesium.Tests/tree.glb diff --git a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs index 844e7338..762b97c0 100644 --- a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs +++ b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs @@ -29,7 +29,7 @@ public List FeatureIds protected override void OnValidateContent(ValidationContext validate) { - var extInstanceFeatures = (MeshExtInstanceFeatures)_node.Extensions.FirstOrDefault(); + var extInstanceFeatures = (MeshExtInstanceFeatures)_node.Extensions.Where(item => item is MeshExtInstanceFeatures).FirstOrDefault(); validate.NotNull(nameof(FeatureIds), extInstanceFeatures.FeatureIds); validate.IsTrue(nameof(FeatureIds), extInstanceFeatures.FeatureIds.Count > 0, "Instance FeatureIds has items"); @@ -40,6 +40,10 @@ protected override void OnValidateContent(ValidationContext validate) public partial class MeshExtInstanceFeatureID { + public MeshExtInstanceFeatureID() + { + } + public MeshExtInstanceFeatureID(int featureCount, int? attribute = null, int? propertyTable = null, string label = null, int? nullFeatureId = null) { _featureCount = featureCount; diff --git a/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs b/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs index cce1d25a..52a35eba 100644 --- a/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs +++ b/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs @@ -1,13 +1,12 @@ using NUnit.Framework; -using SharpGLTF.Geometry; -using SharpGLTF.Geometry.VertexTypes; -using SharpGLTF.Materials; using SharpGLTF.Scenes; using SharpGLTF.Schema2; +using SharpGLTF.Transforms; using SharpGLTF.Validation; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Text.Json.Nodes; namespace SharpGLTF.Cesium { @@ -21,33 +20,35 @@ public void SetUp() } [Test(Description = "Creates a simple triangle with Cesium EXT_Instance_Features")] - public void AddExtInstanceFeatures() + public void AddExtGpuInstanceFeatures() { - TestContext.CurrentContext.AttachGltfValidatorLinks(); + var settings = SceneBuilderSchema2Settings.WithGpuInstancing; + settings.GpuMeshInstancingMinCount = 0; - // todo: create a test with ext_mesh_gpu_instancing + var modelRoot = ModelRoot.Load(ResourceInfo.From("tree.glb")); + var meshBuilder = modelRoot.LogicalMeshes.First().ToMeshBuilder(); + var sceneBuilder = new SceneBuilder(); + var quaternion = Quaternion.CreateFromYawPitchRoll(0, 0, 0); + var scale = Vector3.One; - var material = MaterialBuilder.CreateDefault(); + sceneBuilder. + AddRigidMesh(meshBuilder, new AffineTransform(scale, quaternion, new Vector3(-10, 0, 10))). + WithExtras(JsonNode.Parse("{\"_FEATURE_ID_0\":0}")); + sceneBuilder. + AddRigidMesh(meshBuilder, new AffineTransform(scale, quaternion, new Vector3(0, 0, 0))). + WithExtras(JsonNode.Parse("{\"_FEATURE_ID_0\":1}")); - var mesh = new MeshBuilder("mesh"); - var prim = mesh.UsePrimitive(material); - prim.AddTriangle(new VertexPosition(-10, 0, 0), new VertexPosition(10, 0, 0), new VertexPosition(0, 10, 0)); - - var scene = new SceneBuilder(); - - scene.AddRigidMesh(mesh, Matrix4x4.Identity); - - var model = scene.ToGltf2(); - - // following the sample at https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_instance_features#feature-id-by-gpu-instance - var featureId0 = new MeshExtInstanceFeatureID(2, 0, 0, "Forests",2); + var featureId0 = new MeshExtInstanceFeatureID(2, 0, 0, "Forests", 2); var featureId1 = new MeshExtInstanceFeatureID(9, propertyTable: 1, label: "Trees"); var featureIds = new List() { featureId0, featureId1 }; + + var model = sceneBuilder.ToGltf2(settings); model.LogicalNodes[0].SetFeatureIds(featureIds); - var cesiumExtInstanceFeaturesExtension = (MeshExtInstanceFeatures)model.LogicalNodes[0].Extensions.FirstOrDefault(); + var cesiumExtInstanceFeaturesExtension = (MeshExtInstanceFeatures)model.LogicalNodes[0].Extensions.Where(item => item is MeshExtInstanceFeatures).FirstOrDefault(); + Assert.NotNull(cesiumExtInstanceFeaturesExtension.FeatureIds); Assert.IsTrue(cesiumExtInstanceFeaturesExtension.FeatureIds.Equals(featureIds)); @@ -57,9 +58,9 @@ public void AddExtInstanceFeatures() var ctx = new ValidationResult(model, ValidationMode.Strict, true); model.ValidateContent(ctx.GetContext()); - scene.AttachToCurrentTest("cesium_ext_instance_features.glb"); - scene.AttachToCurrentTest("cesium_ext_instance_features.gltf"); - scene.AttachToCurrentTest("cesium_ext_instance_features.plotly"); + model.AttachToCurrentTest("cesium_ext_instance_features.glb"); + model.AttachToCurrentTest("cesium_ext_instance_features.gltf"); + model.AttachToCurrentTest("cesium_ext_instance_features.plotly"); } } } diff --git a/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj b/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj index c7295562..5639682d 100644 --- a/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj +++ b/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj @@ -20,4 +20,10 @@ + + + PreserveNewest + + + diff --git a/tests/SharpGLTF.Cesium.Tests/tree.glb b/tests/SharpGLTF.Cesium.Tests/tree.glb new file mode 100644 index 0000000000000000000000000000000000000000..461b252128c6ccfe7b1c142b7860c621f0754c3f GIT binary patch literal 20220 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 z Date: Mon, 27 Nov 2023 16:48:40 +0100 Subject: [PATCH 05/14] github actions to .net 8 --- .github/workflows/dotnet.yml | 2 +- src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs | 3 ++- tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index bd76993b..d1425feb 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -8,7 +8,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - dotnet-version: [ '6.0.x' ] + dotnet-version: [ '8.0.x' ] os: [windows-latest, ubuntu-latest, macos-latest] steps: diff --git a/src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs b/src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs index 693b6164..0912c45f 100644 --- a/src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs +++ b/src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs @@ -1,6 +1,7 @@ using SharpGLTF.Validation; using System.Collections.Generic; using System.Linq; +using System.Xml.Linq; namespace SharpGLTF.Schema2 { @@ -26,7 +27,7 @@ public List FeatureIds protected override void OnValidateContent(ValidationContext validate) { - var extMeshFeatures = (MeshExtMeshFeatures)_meshPrimitive.Extensions.FirstOrDefault(); + var extMeshFeatures = (MeshExtMeshFeatures)_meshPrimitive.Extensions.Where(item => item is MeshExtMeshFeatures).FirstOrDefault(); validate.NotNull(nameof(FeatureIds), extMeshFeatures.FeatureIds); validate.IsTrue(nameof(FeatureIds), extMeshFeatures.FeatureIds.Count > 0, "FeatureIds has items"); diff --git a/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj b/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj index 5639682d..b45f4a6c 100644 --- a/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj +++ b/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj @@ -1,4 +1,4 @@ - + net471;net8.0-windows From 6fd314cc00b3be63b26f9c9252300a95a897f11a Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 17:04:19 +0100 Subject: [PATCH 06/14] add check for instance vertex accessor --- .../Schema2/MeshExtInstanceFeatures.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs index 762b97c0..2a14a9af 100644 --- a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs +++ b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs @@ -101,30 +101,30 @@ public static void SetFeatureIds(this Node node, List ext.FeatureIds = instanceFeatureIds; } - private static void ValidateInstanceFeatureId(Node node, MeshExtInstanceFeatureID item) + private static void ValidateInstanceFeatureId(Node node, MeshExtInstanceFeatureID instanceFeatureId) { - Guard.MustBeGreaterThanOrEqualTo((int)item.FeatureCount, 1, nameof(item.FeatureCount)); + Guard.MustBeGreaterThanOrEqualTo((int)instanceFeatureId.FeatureCount, 1, nameof(instanceFeatureId.FeatureCount)); - if (item.NullFeatureId.HasValue) + if (instanceFeatureId.NullFeatureId.HasValue) { - Guard.MustBeGreaterThanOrEqualTo((int)item.NullFeatureId, 0, nameof(item.NullFeatureId)); + Guard.MustBeGreaterThanOrEqualTo((int)instanceFeatureId.NullFeatureId, 0, nameof(instanceFeatureId.NullFeatureId)); } - if (item.Label != null) + if (instanceFeatureId.Label != null) { var regex = "^[a-zA-Z_][a-zA-Z0-9_]*$"; - Guard.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(item.Label, regex), nameof(item.Label)); + Guard.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(instanceFeatureId.Label, regex), nameof(instanceFeatureId.Label)); } - if (item.Attribute.HasValue) + if (instanceFeatureId.Attribute.HasValue) { - Guard.MustBeGreaterThanOrEqualTo((int)item.Attribute, 0, nameof(item.Attribute)); - // todo: check attribute - // Guard that the custom vertex attribute (_FEATURE_ID_{attribute}) exists when FeatureID has attribute set - // var expectedVertexAttribute = $"_FEATURE_ID_{item.Attribute}"; - // Guard.NotNull(primitive.GetVertexAccessor(expectedVertexAttribute), expectedVertexAttribute); + Guard.MustBeGreaterThanOrEqualTo((int)instanceFeatureId.Attribute, 0, nameof(instanceFeatureId.Attribute)); + var expectedVertexAttribute = $"_FEATURE_ID_{instanceFeatureId.Attribute}"; + var gpuInstancing = node.GetGpuInstancing(); + var featureIdAccessors = gpuInstancing.GetAccessor(expectedVertexAttribute); + Guard.NotNull(featureIdAccessors, expectedVertexAttribute); } - if (item.PropertyTable.HasValue) + if (instanceFeatureId.PropertyTable.HasValue) { - Guard.MustBeGreaterThanOrEqualTo((int)item.PropertyTable, 0, nameof(item.PropertyTable)); + Guard.MustBeGreaterThanOrEqualTo((int)instanceFeatureId.PropertyTable, 0, nameof(instanceFeatureId.PropertyTable)); } } } From 99ad1920297bd698c3a602af84b650603c69e90f Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 17:51:11 +0100 Subject: [PATCH 07/14] add some more checks for gpu_instancing --- .../Schema2/MeshExtInstanceFeatures.cs | 14 +++++++++++--- .../Schema2/MeshExtMeshFeatures.cs | 9 +++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs index 2a14a9af..12c36ee1 100644 --- a/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs +++ b/src/SharpGLTF.Cesium/Schema2/MeshExtInstanceFeatures.cs @@ -1,6 +1,7 @@ using SharpGLTF.Validation; using System.Collections.Generic; using System.Linq; +using System.Xml.Linq; namespace SharpGLTF.Schema2 { @@ -29,10 +30,14 @@ public List FeatureIds protected override void OnValidateContent(ValidationContext validate) { - var extInstanceFeatures = (MeshExtInstanceFeatures)_node.Extensions.Where(item => item is MeshExtInstanceFeatures).FirstOrDefault(); + var extInstanceFeatures = _node.Extensions.Where(item => item is MeshExtInstanceFeatures).FirstOrDefault(); + validate.NotNull(nameof(extInstanceFeatures), extInstanceFeatures); + var ext = (MeshExtInstanceFeatures)extInstanceFeatures; + var extMeshGpInstancing = _node.Extensions.Where(item => item is MeshGpuInstancing).FirstOrDefault(); + validate.NotNull(nameof(extMeshGpInstancing), extMeshGpInstancing); - validate.NotNull(nameof(FeatureIds), extInstanceFeatures.FeatureIds); - validate.IsTrue(nameof(FeatureIds), extInstanceFeatures.FeatureIds.Count > 0, "Instance FeatureIds has items"); + validate.NotNull(nameof(FeatureIds), ext.FeatureIds); + validate.IsTrue(nameof(FeatureIds), ext.FeatureIds.Count > 0, "Instance FeatureIds has items"); base.OnValidateContent(validate); } @@ -92,6 +97,9 @@ public static void SetFeatureIds(this Node node, List Guard.NotNullOrEmpty(instanceFeatureIds, nameof(instanceFeatureIds)); + var extMeshGpInstancing = node.Extensions.Where(item => item is MeshGpuInstancing).FirstOrDefault(); + Guard.NotNull(extMeshGpInstancing, nameof(extMeshGpInstancing)); + foreach (var instanceFeatureId in instanceFeatureIds) { ValidateInstanceFeatureId(node, instanceFeatureId); diff --git a/src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs b/src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs index 0912c45f..a20c7ce3 100644 --- a/src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs +++ b/src/SharpGLTF.Cesium/Schema2/MeshExtMeshFeatures.cs @@ -27,10 +27,11 @@ public List FeatureIds protected override void OnValidateContent(ValidationContext validate) { - var extMeshFeatures = (MeshExtMeshFeatures)_meshPrimitive.Extensions.Where(item => item is MeshExtMeshFeatures).FirstOrDefault(); - - validate.NotNull(nameof(FeatureIds), extMeshFeatures.FeatureIds); - validate.IsTrue(nameof(FeatureIds), extMeshFeatures.FeatureIds.Count > 0, "FeatureIds has items"); + var extMeshFeatures = _meshPrimitive.Extensions.Where(item => item is MeshExtMeshFeatures).FirstOrDefault(); + validate.NotNull(nameof(extMeshFeatures), extMeshFeatures); + var ext = (MeshExtMeshFeatures)extMeshFeatures; + validate.NotNull(nameof(FeatureIds), ext.FeatureIds); + validate.IsTrue(nameof(FeatureIds), ext.FeatureIds.Count > 0, "FeatureIds has items"); base.OnValidateContent(validate); } From 8778d145e8e95af8fc1217e29a51c9b9c6c9cc57 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 17:55:54 +0100 Subject: [PATCH 08/14] update comment --- tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs b/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs index 52a35eba..e90b1b06 100644 --- a/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs +++ b/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs @@ -19,7 +19,7 @@ public void SetUp() CesiumExtensions.RegisterExtensions(); } - [Test(Description = "Creates a simple triangle with Cesium EXT_Instance_Features")] + [Test(Description = "Creates a gpu_instancing glTF from a tree with Cesium EXT_Instance_Features")] public void AddExtGpuInstanceFeatures() { var settings = SceneBuilderSchema2Settings.WithGpuInstancing; From d66c03b148eb85c830fe08161dd360dea2150b51 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 19:24:53 +0100 Subject: [PATCH 09/14] Update SharpGLTF.Cesium.Tests.csproj --- tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj b/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj index b45f4a6c..b7f52163 100644 --- a/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj +++ b/tests/SharpGLTF.Cesium.Tests/SharpGLTF.Cesium.Tests.csproj @@ -1,7 +1,7 @@  - net471;net8.0-windows + net471;net6.0-windows;net8.0-windows false SharpGLTF latest From 603a458eeb6429d929be4ccdba6276d6fae7a13a Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 19:25:14 +0100 Subject: [PATCH 10/14] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d1425feb..bd76993b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -8,7 +8,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - dotnet-version: [ '8.0.x' ] + dotnet-version: [ '6.0.x' ] os: [windows-latest, ubuntu-latest, macos-latest] steps: From db8d1c83aabd012af781b72ac49171fde703f973 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Mon, 27 Nov 2023 19:32:18 +0100 Subject: [PATCH 11/14] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index bd76993b..d1425feb 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -8,7 +8,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - dotnet-version: [ '6.0.x' ] + dotnet-version: [ '8.0.x' ] os: [windows-latest, ubuntu-latest, macos-latest] steps: From 233b0baca6e93e41f403e0608b557d32e5d68e67 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Wed, 29 Nov 2023 22:38:16 +0100 Subject: [PATCH 12/14] Update Ext.EXT_InstanceFeatures.cs --- build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs b/build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs index b8a20de4..f0cb8695 100644 --- a/build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs +++ b/build/SharpGLTF.CodeGen/Ext.EXT_InstanceFeatures.cs @@ -8,7 +8,7 @@ class ExtInstanceFeaturesExtension : SchemaProcessor { public override string GetTargetProject() { return Constants.CesiumProjectDirectory; } - private static string RootSchemaUri => Constants.CustomExtensionsPath("EXT_instance_features", "node.EXT_instance_features.schema.json"); + private static string NodeSchemaUri => Constants.CustomExtensionsPath("EXT_instance_features", "node.EXT_instance_features.schema.json"); public override void PrepareTypes(CSharpEmitter newEmitter, SchemaType.Context ctx) { @@ -23,11 +23,11 @@ public override void PrepareTypes(CSharpEmitter newEmitter, SchemaType.Context c private static SchemaType.Context ProcessNode() { - var ctx = SchemaProcessing.LoadSchemaContext(RootSchemaUri); + var ctx = SchemaProcessing.LoadSchemaContext(NodeSchemaUri); ctx.IgnoredByCodeEmitter("glTF Property"); ctx.IgnoredByCodeEmitter("glTF Child of Root Property"); ctx.IgnoredByCodeEmitter("Texture Info"); return ctx; } } -} \ No newline at end of file +} From 19c5f236dfee07eb6826c659f16c44094fea2fff Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Thu, 30 Nov 2023 09:48:50 +0100 Subject: [PATCH 13/14] .net 8 -> .net 6 --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d1425feb..bd76993b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -8,7 +8,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - dotnet-version: [ '8.0.x' ] + dotnet-version: [ '6.0.x' ] os: [windows-latest, ubuntu-latest, macos-latest] steps: From 448987a971db4e4cfe0070cc9bd610c61f3df317 Mon Sep 17 00:00:00 2001 From: Bert Temme Date: Thu, 30 Nov 2023 09:58:13 +0100 Subject: [PATCH 14/14] fix the nunit tests --- tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs b/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs index e90b1b06..81ce995a 100644 --- a/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs +++ b/tests/SharpGLTF.Cesium.Tests/ExtInstanceFeaturesTests.cs @@ -49,11 +49,10 @@ public void AddExtGpuInstanceFeatures() var cesiumExtInstanceFeaturesExtension = (MeshExtInstanceFeatures)model.LogicalNodes[0].Extensions.Where(item => item is MeshExtInstanceFeatures).FirstOrDefault(); - Assert.NotNull(cesiumExtInstanceFeaturesExtension.FeatureIds); - - Assert.IsTrue(cesiumExtInstanceFeaturesExtension.FeatureIds.Equals(featureIds)); - Assert.IsTrue(cesiumExtInstanceFeaturesExtension.FeatureIds[0].Equals(featureId0)); - Assert.IsTrue(cesiumExtInstanceFeaturesExtension.FeatureIds[1].Equals(featureId1)); + Assert.That(cesiumExtInstanceFeaturesExtension.FeatureIds, Is.Not.Null); + Assert.That(cesiumExtInstanceFeaturesExtension.FeatureIds.Equals(featureIds)); + Assert.That(cesiumExtInstanceFeaturesExtension.FeatureIds[0].Equals(featureId0)); + Assert.That(cesiumExtInstanceFeaturesExtension.FeatureIds[1].Equals(featureId1)); var ctx = new ValidationResult(model, ValidationMode.Strict, true); model.ValidateContent(ctx.GetContext());