using System; using System.Collections.Generic; using System.Text.Json; namespace ReFuel.Gltf { public record struct GltfMeshAttribute { private GltfSmartId accessor; public GltfAccessor Accessor { get => accessor.Value; set => accessor.Value = value; } public string Name { get; } public int Index { get; } public GltfAttributeType Type { get; } public GltfMeshAttribute(GltfDocument document, string name, int accessorIndex) { accessor = new GltfSmartId(document.Accessors) { Id = accessorIndex, }; ReadOnlySpan full = name.AsSpan(); ReadOnlySpan head; ReadOnlySpan tail; int indexOfUnderscore = name.IndexOf('_'); if (indexOfUnderscore == -1) { head = full; tail = ReadOnlySpan.Empty; } else { head = full.Slice(0, indexOfUnderscore); tail = full.Slice(indexOfUnderscore+1); } switch (head.ToString()) { case "POSITION": Name = "POSITION"; Index = 0; Type = GltfAttributeType.Position; break; case "NORMAL": Name = "NORMAL"; Index = 0; Type = GltfAttributeType.Normal; break; case "TANGENT": Name = "TANGENT"; Index = 0; Type = GltfAttributeType.Tangent; break; case "TEXCOORD": Name = "TEXCOORD"; Index = int.Parse(tail); Type = GltfAttributeType.TexCoord; break; case "COLOR": Name = "COLOR"; Index = int.Parse(tail); Type = GltfAttributeType.Color; break; case "JOINTS": Name = "JOINTS"; Index = int.Parse(tail); Type = GltfAttributeType.Joints; break; case "WEIGHTS": Name = "WEIGHTS"; Index = int.Parse(tail); Type = GltfAttributeType.Weights; break; default: Name = tail.ToString(); Index = 0; Type = GltfAttributeType.Custom; break; } } } public class GltfMeshPrimitive : GltfObject { public override GltfObjectKind Kind => GltfObjectKind.MeshPrimitive; private GltfSmartId index; private GltfSmartId material; public List Attributes { get; } = new List(); public GltfAccessor? Index { get => index.IsSet ? index : null; set { if (value is null) index.Reset(); else index.Value = value; } } public GltfMaterial? Material { get => material.IsSet ? material : null; set { if (value is null) material.Reset(); else material.Value = value; } } public GltfMeshMode Mode { get; set; } = GltfMeshMode.Triangles; public GltfRefList Targets { get; private set; } internal GltfMeshPrimitive(GltfDocument document) : base(document) { Targets = new GltfRefList(null!); index = new GltfSmartId(document.Accessors); material = new GltfSmartId(document.Materials); } internal override void Deserialize(JsonElement element) { AssertObject(element); if (!element.TryGetProperty("attributes", out JsonElement attributesElement)) RequiredProperty("attributes"); else if (attributesElement.ValueKind != JsonValueKind.Object) PropertyFormatFail("attributes", "expected a dictionary."); foreach (JsonProperty property in attributesElement.EnumerateObject()) { if (!property.Value.TryGetInt32(out int i)) { PropertyFormatFail("attributes", "expected dictionary values to be integers."); } Attributes.Add(new GltfMeshAttribute(Document, property.Name, i)); } if (element.TryGetProperty("indices", out JsonElement indicesElement)) { if (!indicesElement.TryGetInt32(out int index)) PropertyFormatFail("indices", "expected an integer value."); this.index.Id = index; } if (element.TryGetProperty("material", out JsonElement materialElement)) { if (!materialElement.TryGetInt32(out int index)) PropertyFormatFail("material", "expected an integer value."); this.material.Id = index; } // TODO: materials. // TODO: morph targets. if (element.TryGetProperty("mode", out JsonElement modeElement)) { if (!modeElement.TryGetInt32(out int mode)) PropertyFormatFail("mode", "expected integer value."); Mode = (GltfMeshMode)mode; } DeserializeCommon(element); } internal override void Serialize(Utf8JsonWriter writer) { throw new NotImplementedException(); } } public class GltfMesh : GltfIdObject { public override GltfObjectKind Kind => GltfObjectKind.Mesh; public string? Name { get; set; } public float[]? Weights { get; set; } public List Primitives { get; } = new List(); internal GltfMesh(GltfDocument document) : base(document) { } internal override void Deserialize(JsonElement element) { if (element.ValueKind != JsonValueKind.Object) throw new Exception("Expected an object."); if (element.TryGetProperty("name", out JsonElement nameElement)) { Name = nameElement.GetString(); } if (element.TryGetProperty("weights", out JsonElement weightsArray)) { if (weightsArray.ValueKind != JsonValueKind.Array) throw new Exception("Expected an array."); List values = new List(); foreach (JsonElement item in weightsArray.EnumerateArray()) { if (!item.TryGetSingle(out float v)) throw new Exception("expected a number"); values.Add(v); } Weights = values.ToArray(); } else { Weights = null; } if (element.TryGetProperty("primitives", out JsonElement primitivesElement)) { if (primitivesElement.ValueKind != JsonValueKind.Array) PropertyFormatFail("primitives", "expected an array."); foreach (JsonElement child in primitivesElement.EnumerateArray()) { GltfMeshPrimitive primitive = new GltfMeshPrimitive(Document); primitive.Deserialize(child); Primitives.Add(primitive); } } } internal override void Serialize(Utf8JsonWriter writer) { throw new NotImplementedException(); } } }