From f497e7d8d89d16b321c50bd784eb769f0628426c Mon Sep 17 00:00:00 2001 From: Bezaleel Olakunori Date: Thu, 23 May 2024 08:17:43 +0200 Subject: [PATCH 1/2] Create a helper for reading a list of objects --- src/ThreeDModels/Format/Gltf/IO/Utf8JsonReaderHelpers.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ThreeDModels/Format/Gltf/IO/Utf8JsonReaderHelpers.cs b/src/ThreeDModels/Format/Gltf/IO/Utf8JsonReaderHelpers.cs index 53d6e8a..1112afc 100644 --- a/src/ThreeDModels/Format/Gltf/IO/Utf8JsonReaderHelpers.cs +++ b/src/ThreeDModels/Format/Gltf/IO/Utf8JsonReaderHelpers.cs @@ -91,4 +91,9 @@ public static class Utf8JsonReaderHelpers { return ReadList(ref jsonReader, context, JsonTokenType.Number, (ref Utf8JsonReader reader, GltfReaderContext _) => (float)ReadFloat(ref reader)!); } + + public static List? ReadObjectList(ref Utf8JsonReader jsonReader, GltfReaderContext context, ArrayElementReader elementReader) + { + return ReadList(ref jsonReader, context, JsonTokenType.StartObject, elementReader); + } } From d3b9771b1b300d2be0df105fd73c63c4452ccf01 Mon Sep 17 00:00:00 2001 From: Bezaleel Olakunori Date: Thu, 23 May 2024 08:18:45 +0200 Subject: [PATCH 2/2] Implement the KHR_materials_variants glTF extension --- .../Gltf/Extensions/KHR_materials_variants.cs | 298 ++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 src/ThreeDModels/Format/Gltf/Extensions/KHR_materials_variants.cs diff --git a/src/ThreeDModels/Format/Gltf/Extensions/KHR_materials_variants.cs b/src/ThreeDModels/Format/Gltf/Extensions/KHR_materials_variants.cs new file mode 100644 index 0000000..e2a7ae6 --- /dev/null +++ b/src/ThreeDModels/Format/Gltf/Extensions/KHR_materials_variants.cs @@ -0,0 +1,298 @@ +using System.Text.Json; +using ThreeDModels.Format.Gltf.Elements; +using ThreeDModels.Format.Gltf.IO; +using static ThreeDModels.Format.Gltf.IO.Utf8JsonReaderHelpers; + +namespace ThreeDModels.Format.Gltf.Extensions; + +public class KHR_materials_variants : IGltfProperty +{ + /// + /// A list of objects defining a valid material variant. + /// + public required List Variants { get; set; } + public Dictionary? Extensions { get; set; } + public object? Extras { get; set; } +} + +/// +/// Represents an object defining a valid material variant. +/// +public class KhrMaterialsVariantsVariant : IGltfRootProperty +{ + /// + /// The name of the material variant. + /// + public string? Name { get; set; } + public Dictionary? Extensions { get; set; } + public object? Extras { get; set; } +} + +public class KHR_materials_variants_mesh_primitive : IGltfProperty +{ + /// + /// A list of material to variant mappings. + /// + public required List? Mappings { get; set; } + public Dictionary? Extensions { get; set; } + public object? Extras { get; set; } +} + +/// +/// Represents a mapping of an indexed material to a set of variants. +/// +public class KhrMaterialsVariantsMapping : IGltfProperty +{ + /// + /// A list of variant index values. + /// + public required List Variants { get; set; } + /// + /// The material associated with the set of variants. + /// + public required int Material { get; set; } + /// + /// The user-defined name of this variant material mapping. + /// + public string? Name { get; set; } + public Dictionary? Extensions { get; set; } + public object? Extras { get; set; } +} + +public class KhrMaterialsVariantsExtension : IGltfExtension +{ + public string Name => nameof(KHR_materials_variants); + + public object? Read(ref Utf8JsonReader jsonReader, GltfReaderContext context, Type parentType) + { + if (parentType != typeof(Gltf)) + { + List? variants = null; + Dictionary? extensions = null; + object? extras = null; + if (jsonReader.TokenType == JsonTokenType.PropertyName && jsonReader.Read()) + { + } + if (jsonReader.TokenType == JsonTokenType.Null) + { + return null; + } + else if (jsonReader.TokenType != JsonTokenType.StartObject) + { + throw new InvalidDataException("Failed to find start of property."); + } + while (jsonReader.Read()) + { + if (jsonReader.TokenType == JsonTokenType.EndObject) + { + break; + } + var propertyName = jsonReader.GetString(); + if (propertyName == nameof(variants)) + { + variants = ReadObjectList(ref jsonReader, context, (ref Utf8JsonReader reader, GltfReaderContext ctx) => KhrMaterialsVariantsVariantSerialization.Read(ref reader, ctx)!); + } + else if (propertyName == nameof(extensions)) + { + extensions = ExtensionsSerialization.Read(ref jsonReader, context); + } + else if (propertyName == nameof(extras)) + { + extras = ExtrasSerialization.Read(ref jsonReader, context); + } + else + { + throw new InvalidDataException($"Unknown property: {propertyName}"); + } + } + if (variants == null || variants.Count < 1) + { + throw new InvalidDataException("KHR_materials_variants.variants must have at least one variant."); + } + return new KHR_materials_variants() + { + Variants = variants!, + Extensions = extensions, + Extras = extras, + }; + } + else if (parentType == typeof(MeshPrimitive)) + { + List? mappings = null; + Dictionary? extensions = null; + object? extras = null; + if (jsonReader.TokenType == JsonTokenType.PropertyName && jsonReader.Read()) + { + } + if (jsonReader.TokenType == JsonTokenType.Null) + { + return null; + } + else if (jsonReader.TokenType != JsonTokenType.StartObject) + { + throw new InvalidDataException("Failed to find start of property."); + } + while (jsonReader.Read()) + { + if (jsonReader.TokenType == JsonTokenType.EndObject) + { + break; + } + var propertyName = jsonReader.GetString(); + if (propertyName == nameof(mappings)) + { + mappings = ReadObjectList(ref jsonReader, context, (ref Utf8JsonReader reader, GltfReaderContext ctx) => KhrMaterialsVariantsMappingSerialization.Read(ref reader, ctx)!); + } + else if (propertyName == nameof(extensions)) + { + extensions = ExtensionsSerialization.Read(ref jsonReader, context); + } + else if (propertyName == nameof(extras)) + { + extras = ExtrasSerialization.Read(ref jsonReader, context); + } + else + { + throw new InvalidDataException($"Unknown property: {propertyName}"); + } + } + if (mappings == null || mappings.Count < 1) + { + throw new InvalidDataException("KHR_materials_variants.mappings must have at least 1 mapping."); + } + return new KHR_materials_variants_mesh_primitive() + { + Mappings = mappings!, + Extensions = extensions, + Extras = extras, + }; + } + throw new InvalidDataException("KHR_materials_variants must be used in a either the Gltf root or a MeshPrimitive."); + } +} + +public class KhrMaterialsVariantsVariantSerialization +{ + public static KhrMaterialsVariantsVariant? Read(ref Utf8JsonReader jsonReader, GltfReaderContext context) + { + string? name = null; + Dictionary? extensions = null; + object? extras = null; + if (jsonReader.TokenType == JsonTokenType.PropertyName && jsonReader.Read()) + { + } + if (jsonReader.TokenType == JsonTokenType.Null) + { + return null; + } + else if (jsonReader.TokenType != JsonTokenType.StartObject) + { + throw new InvalidDataException("Failed to find start of property."); + } + while (jsonReader.Read()) + { + if (jsonReader.TokenType == JsonTokenType.EndObject) + { + break; + } + var propertyName = jsonReader.GetString(); + if (propertyName == nameof(name)) + { + name = ReadString(ref jsonReader); + } + else if (propertyName == nameof(extensions)) + { + extensions = ExtensionsSerialization.Read(ref jsonReader, context); + } + else if (propertyName == nameof(extras)) + { + extras = ExtrasSerialization.Read(ref jsonReader, context); + } + else + { + throw new InvalidDataException($"Unknown property: {propertyName}"); + } + } + if (name == null) + { + throw new InvalidDataException("KhrMaterialsVariantsVariant.name is a required property."); + } + return new() + { + Name = name!, + Extensions = extensions, + Extras = extras, + }; + } +} + +public class KhrMaterialsVariantsMappingSerialization +{ + public static KhrMaterialsVariantsMapping? Read(ref Utf8JsonReader jsonReader, GltfReaderContext context) + { + List? variants = null; + int? material = null; + string? name = null; + Dictionary? extensions = null; + object? extras = null; + if (jsonReader.TokenType == JsonTokenType.PropertyName && jsonReader.Read()) + { + } + if (jsonReader.TokenType == JsonTokenType.Null) + { + return null; + } + else if (jsonReader.TokenType != JsonTokenType.StartObject) + { + throw new InvalidDataException("Failed to find start of property."); + } + while (jsonReader.Read()) + { + if (jsonReader.TokenType == JsonTokenType.EndObject) + { + break; + } + var propertyName = jsonReader.GetString(); + if (propertyName == nameof(variants)) + { + variants = ReadIntegerList(ref jsonReader, context); + } + else if (propertyName == nameof(material)) + { + material = ReadInteger(ref jsonReader); + } + else if (propertyName == nameof(name)) + { + name = ReadString(ref jsonReader); + } + else if (propertyName == nameof(extensions)) + { + extensions = ExtensionsSerialization.Read(ref jsonReader, context); + } + else if (propertyName == nameof(extras)) + { + extras = ExtrasSerialization.Read(ref jsonReader, context); + } + else + { + throw new InvalidDataException($"Unknown property: {propertyName}"); + } + } + if (variants == null || variants.Count < 1) + { + throw new InvalidDataException("KHR_materials_variants.mappings[i].variants must have at least one variant."); + } + if (material == null) + { + throw new InvalidDataException("KHR_materials_variants.mappings[i].material is a required property."); + } + return new() + { + Variants = variants!, + Material = (int)material!, + Name = name, + Extensions = extensions, + Extras = extras, + }; + } +}