Files
AR-Menu/Library/PackageCache/com.unity.cloud.gltfast@db5a82ec0b47/Runtime/Scripts/Schema/Accessor.cs
2025-11-30 08:35:03 +02:00

370 lines
15 KiB
C#

// SPDX-FileCopyrightText: 2023 Unity Technologies and the glTFast authors
// SPDX-License-Identifier: Apache-2.0
using System;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Assertions;
// GLTF_EXPORT
using UnityEngine.Rendering;
namespace GLTFast.Schema
{
/// <inheritdoc/>
[Serializable]
public class Accessor : AccessorBase<AccessorSparse> { }
/// <inheritdoc/>
[Serializable]
public abstract class AccessorBase<TSparse> : AccessorBase
where TSparse : AccessorSparseBase
{
/// <inheritdoc cref="Sparse"/>
public TSparse sparse;
/// <inheritdoc cref="AccessorBase.Sparse"/>
public override AccessorSparseBase Sparse => sparse;
/// <inheritdoc />
internal override void UnsetSparse()
{
sparse = null;
}
}
/// <summary>
/// An accessor defines a method for retrieving data as typed arrays from
/// within a buffer view.
/// See <a href="https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#accessors">.
/// accessor in the glTF 2.0 specification</a>.
/// </summary>
[Serializable]
public abstract class AccessorBase : NamedObject
{
/// <summary>
/// The index of the bufferView.
/// If this is undefined, look in the sparse object for the index and value buffer views.
/// </summary>
public int bufferView = -1;
/// <summary>
/// The offset relative to the start of the bufferView in bytes.
/// This must be a multiple of the size of the component datatype.
/// </summary>
public int byteOffset;
/// <summary>
/// The datatype of components in the attribute.
/// All valid values correspond to WebGL enums.
/// The corresponding typed arrays are: `Int8Array`, `Uint8Array`, `Int16Array`,
/// `Uint16Array`, `Uint32Array`, and `Float32Array`, respectively.
/// 5125 (UNSIGNED_INT) is only allowed when the accessor contains indices
/// i.e., the accessor is only referenced by `primitive.indices`.
/// </summary>
public GltfComponentType componentType;
/// <summary>
/// Specifies whether integer data values should be normalized
/// (`true`) to [0, 1] (for unsigned types) or [-1, 1] (for signed types),
/// or converted directly (`false`) when they are accessed.
/// Must be `false` when accessor is used for animation data.
/// </summary>
public bool normalized;
/// <summary>
/// The number of attributes referenced by this accessor, not to be confused
/// with the number of bytes or number of components.
/// </summary>
public int count;
/// <summary>
/// Specifies if the attribute is a scalar, vector, or matrix,
/// and the number of elements in the vector or matrix.
/// </summary>
// Field is public for unified serialization only. Warn via Obsolete attribute.
[Obsolete("Use GetAttributeType and SetAttributeType for access.")]
public string type;
[NonSerialized]
GltfAccessorAttributeType m_TypeEnum = GltfAccessorAttributeType.Undefined;
/// <summary>
/// <see cref="GltfAccessorAttributeType"/> typed/cached getter from the <see cref="type"/> string.
/// </summary>
/// <returns>The Accessor's attribute type, if it could be retrieved correctly. <see cref="GltfAccessorAttributeType.Undefined"/> otherwise</returns>
public GltfAccessorAttributeType GetAttributeType()
{
if (m_TypeEnum != GltfAccessorAttributeType.Undefined)
return m_TypeEnum;
#pragma warning disable CS0618 // Type or member is obsolete
if (Enum.TryParse(type, true, out m_TypeEnum))
{
type = null;
return m_TypeEnum;
}
type = null;
#pragma warning restore CS0618 // Type or member is obsolete
return GltfAccessorAttributeType.Undefined;
}
/// <summary>
/// <see cref="GltfAccessorAttributeType"/> typed setter for the <see cref="type"/> string.
/// </summary>
/// <param name="attributeType">Attribute type</param>
public void SetAttributeType(GltfAccessorAttributeType attributeType)
{
m_TypeEnum = attributeType;
#pragma warning disable CS0618 // Type or member is obsolete
type = null;
#pragma warning restore CS0618 // Type or member is obsolete
}
/// <summary>
/// Maximum value of each component in this attribute.
/// Both min and max arrays have the same length.
/// The length is determined by the value of the type property;
/// it can be 1, 2, 3, 4, 9, or 16.
///
/// When `componentType` is `5126` (FLOAT) each array value must be stored as
/// double-precision JSON number with numerical value which is equal to
/// buffer-stored single-precision value to avoid extra runtime conversions.
///
/// `normalized` property has no effect on array values: they always correspond
/// to the actual values stored in the buffer. When accessor is sparse, this
/// property must contain max values of accessor data with sparse substitution
/// applied.
/// </summary>
public float[] max;
/// <summary>
/// Minimum value of each component in this attribute.
/// Both min and max arrays have the same length. The length is determined by
/// the value of the type property; it can be 1, 2, 3, 4, 9, or 16.
///
/// When `componentType` is `5126` (FLOAT) each array value must be stored as
/// double-precision JSON number with numerical value which is equal to
/// buffer-stored single-precision value to avoid extra runtime conversions.
///
/// `normalized` property has no effect on array values: they always correspond
/// to the actual values stored in the buffer. When accessor is sparse, this
/// property must contain min values of accessor data with sparse substitution
/// applied.
/// </summary>
public float[] min;
/// <summary>
/// Sparse storage of attributes that deviate from their initialization value.
/// </summary>
public abstract AccessorSparseBase Sparse { get; }
/// <summary>
/// Sets <see cref="Sparse"/> to null.
/// </summary>
internal abstract void UnsetSparse();
/// <summary>
/// Provides size of components by type
/// </summary>
/// <param name="componentType">glTF component type</param>
/// <returns>Component size in bytes</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when value of <see cref="componentType"/> is unknown</exception>
public static int GetComponentTypeSize(GltfComponentType componentType)
{
switch (componentType)
{
case GltfComponentType.Byte:
case GltfComponentType.UnsignedByte:
return 1;
case GltfComponentType.Short:
case GltfComponentType.UnsignedShort:
return 2;
case GltfComponentType.Float:
case GltfComponentType.UnsignedInt:
return 4;
default:
throw new ArgumentOutOfRangeException(nameof(componentType), componentType, null);
}
}
/// <summary>
/// Converts Unity vertex attribute format to glTF component type.
/// </summary>
/// <param name="format">vertex attribute format</param>
/// <returns>glTF component type</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the value of <see cref="format"/> is unknown.</exception>
public static GltfComponentType GetComponentType(VertexAttributeFormat format)
{
switch (format)
{
case VertexAttributeFormat.Float32:
case VertexAttributeFormat.Float16:
return GltfComponentType.Float;
case VertexAttributeFormat.UNorm8:
case VertexAttributeFormat.UInt8:
return GltfComponentType.UnsignedByte;
case VertexAttributeFormat.SNorm8:
case VertexAttributeFormat.SInt8:
return GltfComponentType.Byte;
case VertexAttributeFormat.UNorm16:
case VertexAttributeFormat.UInt16:
return GltfComponentType.UnsignedShort;
case VertexAttributeFormat.SNorm16:
case VertexAttributeFormat.SInt16:
return GltfComponentType.Short;
case VertexAttributeFormat.UInt32:
case VertexAttributeFormat.SInt32:
return GltfComponentType.UnsignedInt;
default:
throw new ArgumentOutOfRangeException(nameof(format), format, null);
}
}
/// <summary>
/// Get one-dimensional glTF attribute type by number of components per elements.
/// Note that this does not support matrix types.
/// </summary>
/// <param name="dimension">Number of components per element</param>
/// <returns>Corresponding one-dimensional glTF attribute type</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="dimension"/> is not between 1 and 4.</exception>
public static GltfAccessorAttributeType GetAccessorAttributeType(int dimension)
{
if (dimension < 1 || dimension > 4)
{
throw new ArgumentOutOfRangeException(nameof(dimension), dimension, null);
}
return (GltfAccessorAttributeType)dimension;
}
/// <summary>
/// Get number of components of glTF attribute type.
/// </summary>
/// <param name="type">glTF attribute type</param>
/// <returns>Number of components</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the value of <see cref="type"/> is unknown.</exception>
public static int GetAccessorAttributeTypeLength(GltfAccessorAttributeType type)
{
switch (type)
{
case GltfAccessorAttributeType.SCALAR:
return 1;
case GltfAccessorAttributeType.VEC2:
return 2;
case GltfAccessorAttributeType.VEC3:
return 3;
case GltfAccessorAttributeType.VEC4:
case GltfAccessorAttributeType.MAT2:
return 4;
case GltfAccessorAttributeType.MAT3:
return 9;
case GltfAccessorAttributeType.MAT4:
return 16;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
/// <summary>
/// For 3D positional data, returns accessor's bounding box. Applies coordinate system transform (glTF to Unity)
/// </summary>
/// <returns>Bounding box enclosing the minimum and maximum values</returns>
public Bounds? TryGetBounds()
{
Assert.AreEqual(GltfAccessorAttributeType.VEC3, GetAttributeType());
if (min != null && min.Length > 2 && max != null && max.Length > 2)
{
var maxBounds = new float3(-min[0], max[1], max[2]);
var minBounds = new float3(-max[0], min[1], min[2]);
if (normalized)
{
switch (componentType)
{
case GltfComponentType.Byte:
maxBounds = math.max(maxBounds / sbyte.MaxValue, -1);
minBounds = math.max(minBounds / sbyte.MaxValue, -1);
break;
case GltfComponentType.UnsignedByte:
maxBounds /= byte.MaxValue;
minBounds /= byte.MaxValue;
break;
case GltfComponentType.Short:
maxBounds = math.max(maxBounds / short.MaxValue, -1);
minBounds = math.max(minBounds / short.MaxValue, -1);
break;
case GltfComponentType.UnsignedShort:
maxBounds /= ushort.MaxValue;
minBounds /= ushort.MaxValue;
break;
case GltfComponentType.UnsignedInt:
maxBounds /= uint.MaxValue;
minBounds /= uint.MaxValue;
break;
}
}
return new Bounds
{
max = maxBounds,
min = minBounds
};
}
return null;
}
/// <summary>
/// True if the accessor is <a href="https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#sparse-accessors">sparse</a>
/// </summary>
public bool IsSparse => Sparse != null;
/// <summary>
/// Byte size of one element
/// </summary>
public int ElementByteSize => GetAccessorAttributeTypeLength(GetAttributeType()) * GetComponentTypeSize(componentType);
/// <summary>
/// Overall, byte size.
/// Ignores interleaved or sparse accessors
/// </summary>
public int ByteSize => ElementByteSize * count;
internal void GltfSerialize(JsonWriter writer)
{
writer.AddObject();
if (bufferView >= 0)
{
writer.AddProperty("bufferView", bufferView);
}
writer.AddProperty("componentType", (int)componentType);
writer.AddProperty("count", count);
Assert.AreNotEqual(GltfAccessorAttributeType.Undefined, m_TypeEnum);
writer.AddProperty("type", m_TypeEnum.ToString());
if (byteOffset > 0)
{
writer.AddProperty("byteOffset", byteOffset);
}
if (normalized)
{
writer.AddProperty("normalized", normalized);
}
if (max != null)
{
writer.AddArrayProperty("max", max);
}
if (min != null)
{
writer.AddArrayProperty("min", min);
}
if (Sparse != null)
{
writer.AddProperty("sparse");
Sparse.GltfSerialize(writer);
writer.Close();
}
writer.Close();
}
}
}