Files
ReFuel.Gltf/ReFuel.Gltf/GltfMesh.cs
T
2025-10-12 14:33:16 +03:00

251 lines
8.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Text.Json;
namespace ReFuel.Gltf
{
public record struct GltfMeshAttribute
{
private GltfSmartId<GltfAccessor> 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<GltfAccessor>(document.Accessors)
{
Id = accessorIndex,
};
ReadOnlySpan<char> full = name.AsSpan();
ReadOnlySpan<char> head;
ReadOnlySpan<char> tail;
int indexOfUnderscore = name.IndexOf('_');
if (indexOfUnderscore == -1)
{
head = full;
tail = ReadOnlySpan<char>.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<GltfAccessor> index;
private GltfSmartId<GltfMaterial> material;
public List<GltfMeshAttribute> Attributes { get; } = new List<GltfMeshAttribute>();
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<GltfIdObject> Targets { get; private set; }
internal GltfMeshPrimitive(GltfDocument document) : base(document)
{
Targets = new GltfRefList<GltfIdObject>(null!);
index = new GltfSmartId<GltfAccessor>(document.Accessors);
material = new GltfSmartId<GltfMaterial>(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<GltfMeshPrimitive> Primitives { get; } = new List<GltfMeshPrimitive>();
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<float> values = new List<float>();
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();
}
}
}