351 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Collections;
using UnityEngine;
namespace UniGLTF
{
public enum AccessorVectorType
{
SCALAR,
VEC2,
VEC3,
VEC4,
MAT2,
MAT3,
MAT4,
}
public static class GltfAccessorTypeExtensions
{
public static int TypeCount(this AccessorVectorType type)
{
switch (type)
{
case AccessorVectorType.SCALAR:
return 1;
case AccessorVectorType.VEC2:
return 2;
case AccessorVectorType.VEC3:
return 3;
case AccessorVectorType.VEC4:
case AccessorVectorType.MAT2:
return 4;
case AccessorVectorType.MAT3:
return 9;
case AccessorVectorType.MAT4:
return 16;
default:
throw new NotImplementedException();
}
}
}
public enum AccessorValueType : int
{
BYTE = 5120, // signed ?
UNSIGNED_BYTE = 5121,
SHORT = 5122,
UNSIGNED_SHORT = 5123,
//INT = 5124,
UNSIGNED_INT = 5125,
FLOAT = 5126,
}
public static class GltfComponentTypeExtensions
{
public static int ByteSize(this AccessorValueType t)
{
switch (t)
{
case AccessorValueType.BYTE: return 1;
case AccessorValueType.UNSIGNED_BYTE: return 1;
case AccessorValueType.SHORT: return 2;
case AccessorValueType.UNSIGNED_SHORT: return 2;
case AccessorValueType.UNSIGNED_INT: return 4;
case AccessorValueType.FLOAT: return 4;
default: throw new ArgumentException();
}
}
}
public class BufferAccessor
{
public INativeArrayManager ArrayManager { get; }
public NativeArray<byte> Bytes { get; private set; }
public AccessorValueType ComponentType { get; private set; }
public AccessorVectorType AccessorType { get; private set; }
public int Stride => ComponentType.ByteSize() * AccessorType.TypeCount();
public int Count { get; private set; }
public int ByteLength => Stride * Count;
public BufferAccessor(INativeArrayManager arrayManager, NativeArray<byte> bytes, AccessorValueType componentType, AccessorVectorType accessorType, int count)
{
ArrayManager = arrayManager;
Bytes = bytes;
ComponentType = componentType;
AccessorType = accessorType;
Count = count;
}
public override string ToString()
{
return $"{Stride}stride x{Count}";
}
public NativeArray<T> GetSpan<T>(bool checkStride = true) where T : struct
{
if (checkStride && Marshal.SizeOf(typeof(T)) != Stride)
{
throw new Exception("different sizeof(T) with stride");
}
return Bytes.Reinterpret<T>(1);
}
public void Assign<T>(T[] values) where T : struct
{
if (Marshal.SizeOf(typeof(T)) != Stride)
{
throw new Exception("invalid element size");
}
Bytes = ArrayManager.CreateNativeArray<byte>(Stride * values.Length);
Count = values.Length;
Bytes.Reinterpret<T>(1).CopyFrom(values);
}
public void Assign<T>(NativeArray<T> values) where T : struct
{
if (Marshal.SizeOf(typeof(T)) != Stride)
{
throw new Exception("invalid element size");
}
Bytes = ArrayManager.CreateNativeArray<byte>(Marshal.SizeOf<T>() * values.Length);
NativeArray<T>.Copy(values, Bytes.Reinterpret<T>(1));
Count = values.Length;
}
// for index buffer
public void AssignAsShort(NativeArray<int> values)
{
if (AccessorType != AccessorVectorType.SCALAR)
{
throw new NotImplementedException();
}
ComponentType = AccessorValueType.UNSIGNED_SHORT;
Bytes = ArrayManager.Convert(values, (int x) => (ushort)x).Reinterpret<Byte>(Marshal.SizeOf<ushort>());
Count = values.Length;
}
// Index用
public NativeArray<int> GetAsIntArray()
{
if (AccessorType != AccessorVectorType.SCALAR)
{
throw new InvalidOperationException("not scalar");
}
switch (ComponentType)
{
case AccessorValueType.UNSIGNED_BYTE:
return ArrayManager.Convert(Bytes, (byte x) => (int)x);
case AccessorValueType.UNSIGNED_SHORT:
return ArrayManager.Convert(Bytes.Reinterpret<ushort>(1), (ushort x) => (int)x);
case AccessorValueType.UNSIGNED_INT:
return Bytes.Reinterpret<Int32>(1);
default:
throw new NotImplementedException();
}
}
// BoneWeight用
public NativeArray<Vector4> GetAsVector4Array()
{
if (AccessorType != AccessorVectorType.VEC4)
{
throw new InvalidOperationException("not VEC4");
}
switch (ComponentType)
{
case AccessorValueType.FLOAT:
return Bytes.Reinterpret<Vector4>(1);
case AccessorValueType.UNSIGNED_BYTE:
return ArrayManager.Convert(Bytes.Reinterpret<Byte4>(1), (Byte4 b) => new Vector4(b.x / 255.0f, b.y / 255.0f, b.z / 255.0f, b.w / 255.0f));
case AccessorValueType.UNSIGNED_SHORT:
return ArrayManager.Convert(Bytes.Reinterpret<UShort4>(1), (UShort4 b) => new Vector4(b.x / 65535.0f, b.y / 65535.0f, b.z / 65535.0f, b.w / 65535.0f));
default:
throw new NotImplementedException();
}
}
// BoneJoint用
public NativeArray<SkinJoints> GetAsSkinJointsArray()
{
if (AccessorType != AccessorVectorType.VEC4)
{
throw new InvalidOperationException("not VEC4");
}
switch (ComponentType)
{
case AccessorValueType.UNSIGNED_BYTE:
return ArrayManager.Convert(Bytes.Reinterpret<Byte4>(1), (Byte4 b) => new SkinJoints(b.x, b.y, b.z, b.w));
case AccessorValueType.UNSIGNED_SHORT:
return Bytes.Reinterpret<SkinJoints>(1);
default:
throw new NotImplementedException();
}
}
public BufferAccessor Skip(int skipFrames)
{
skipFrames = Math.Min(Count, skipFrames);
if (skipFrames == 0)
{
return this;
}
var start = Stride * skipFrames;
return new BufferAccessor(ArrayManager, Bytes.GetSubArray(start, Bytes.Length - start), ComponentType, AccessorType, Count - skipFrames);
}
int AddViewTo(ExportingGltfData data, int bufferIndex,
int offset = 0, int count = 0)
{
var stride = Stride;
if (count == 0)
{
count = Count;
}
var slice = Bytes.GetSubArray(offset * stride, count * stride);
return data.AppendToBuffer(slice);
}
glTFAccessor CreateGltfAccessor(int viewIndex, int count = 0, int byteOffset = 0)
{
if (count == 0)
{
count = Count;
}
return new glTFAccessor
{
bufferView = viewIndex,
byteOffset = byteOffset,
componentType = (glComponentType)ComponentType,
type = AccessorType.ToString(),
count = count,
};
}
int AddAccessorTo(ExportingGltfData data, int viewIndex,
Action<NativeArray<byte>, glTFAccessor> minMax = null,
int offset = 0, int count = 0)
{
var gltf = data.Gltf;
var accessorIndex = gltf.accessors.Count;
var accessor = CreateGltfAccessor(viewIndex, count, offset * Stride);
if (minMax != null)
{
minMax(Bytes, accessor);
}
gltf.accessors.Add(accessor);
return accessorIndex;
}
public int AddAccessorTo(ExportingGltfData data, int bufferIndex,
// GltfBufferTargetType targetType,
bool useSparse,
Action<NativeArray<byte>, glTFAccessor> minMax = null,
int offset = 0, int count = 0)
{
if (ComponentType == AccessorValueType.FLOAT
&& AccessorType == AccessorVectorType.VEC3
)
{
var values = GetSpan<Vector3>();
// 巨大ポリゴンのモデル対策にValueTupleの型をushort -> uint へ
var sparseValuesWithIndex = new List<ValueTuple<int, Vector3>>();
for (int i = 0; i < values.Length; ++i)
{
var v = values[i];
if (v != Vector3.zero)
{
sparseValuesWithIndex.Add((i, v));
}
}
//var status = $"{sparseIndices.Count * 14}/{values.Length * 12}";
if (useSparse
&& sparseValuesWithIndex.Count > 0 // avoid empty sparse
&& sparseValuesWithIndex.Count * 16 < values.Length * 12)
{
// use sparse
using (var sparseIndexBin = new NativeArray<byte>(sparseValuesWithIndex.Count * 4, Allocator.Persistent))
using (var sparseValueBin = new NativeArray<byte>(sparseValuesWithIndex.Count * 12, Allocator.Persistent))
{
var sparseIndexSpan = sparseIndexBin.Reinterpret<Int32>(1);
var sparseValueSpan = sparseValueBin.Reinterpret<Vector3>(1);
for (int i = 0; i < sparseValuesWithIndex.Count; ++i)
{
var (index, value) = sparseValuesWithIndex[i];
sparseIndexSpan[i] = index;
sparseValueSpan[i] = value;
}
var sparseIndexView = data.AppendToBuffer(sparseIndexBin);
var sparseValueView = data.AppendToBuffer(sparseValueBin);
var accessorIndex = data.Gltf.accessors.Count;
var accessor = new glTFAccessor
{
componentType = (glComponentType)ComponentType,
type = AccessorType.ToString(),
count = Count,
byteOffset = -1,
sparse = new glTFSparse
{
count = sparseValuesWithIndex.Count,
indices = new glTFSparseIndices
{
componentType = (glComponentType)AccessorValueType.UNSIGNED_INT,
bufferView = sparseIndexView,
},
values = new glTFSparseValues
{
bufferView = sparseValueView,
},
}
};
if (minMax != null)
{
minMax(sparseValueBin, accessor);
}
data.Gltf.accessors.Add(accessor);
return accessorIndex;
}
}
}
var viewIndex = AddViewTo(data, bufferIndex, offset, count);
return AddAccessorTo(data, viewIndex, minMax, 0, count);
}
}
}