mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-12 21:45:06 -05:00
490 lines
16 KiB
C#
490 lines
16 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Numerics;
|
||
using System.Runtime.InteropServices;
|
||
using UniGLTF;
|
||
using Unity.Collections;
|
||
using Unity.Collections.LowLevel.Unsafe;
|
||
|
||
namespace VrmLib
|
||
{
|
||
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 4;
|
||
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 ArraySegment<byte> Bytes;
|
||
|
||
public AccessorValueType ComponentType;
|
||
|
||
public AccessorVectorType AccessorType;
|
||
|
||
public int Stride => ComponentType.ByteSize() * AccessorType.TypeCount();
|
||
|
||
public int Count;
|
||
|
||
public int ByteLength => Stride * Count;
|
||
|
||
public override string ToString()
|
||
{
|
||
return $"{Stride}stride x{Count}";
|
||
}
|
||
|
||
public SpanLike<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 SpanLike.Wrap<T>(Bytes);
|
||
}
|
||
|
||
/// <summary>
|
||
/// バッファをNativeArrayに変換して返す
|
||
/// 開放の責務は使い手側にある点に注意
|
||
/// </summary>
|
||
public unsafe NativeArray<T> AsNativeArray<T>(Allocator allocator) where T : struct
|
||
{
|
||
fixed (byte* byteArray = Bytes.Array)
|
||
{
|
||
var nativeArray = new NativeArray<T>(Bytes.Count / Marshal.SizeOf<T>(), allocator);
|
||
UnsafeUtility.MemCpy(nativeArray.GetUnsafePtr(), byteArray + Bytes.Offset, Bytes.Count);
|
||
return nativeArray;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// バッファをNativeSliceへと書き込む
|
||
/// </summary>
|
||
public unsafe void CopyToNativeSlice<T>(NativeSlice<T> destArray) where T : unmanaged
|
||
{
|
||
fixed (byte* byteArray = Bytes.Array)
|
||
{
|
||
UnsafeUtility.MemCpy((T*)destArray.GetUnsafePtr(), byteArray + Bytes.Offset, Bytes.Count);
|
||
}
|
||
}
|
||
|
||
public void Assign<T>(T[] values) where T : struct
|
||
{
|
||
if (Marshal.SizeOf(typeof(T)) != Stride)
|
||
{
|
||
throw new Exception("invalid element size");
|
||
}
|
||
var array = new byte[Stride * values.Length];
|
||
Bytes = new ArraySegment<byte>(array);
|
||
values.ToBytes(Bytes);
|
||
Count = values.Length;
|
||
}
|
||
|
||
public void Assign<T>(SpanLike<T> values) where T : struct
|
||
{
|
||
if (Marshal.SizeOf(typeof(T)) != Stride)
|
||
{
|
||
throw new Exception("invalid element size");
|
||
}
|
||
Bytes = values.Bytes;
|
||
Count = values.Length;
|
||
}
|
||
|
||
// for index buffer
|
||
public void AssignAsShort(SpanLike<int> values)
|
||
{
|
||
if (AccessorType != AccessorVectorType.SCALAR)
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
ComponentType = AccessorValueType.UNSIGNED_SHORT;
|
||
|
||
Bytes = new ArraySegment<byte>(new byte[Stride * values.Length]);
|
||
var span = GetSpan<ushort>();
|
||
Count = values.Length;
|
||
for (int i = 0; i < values.Length; ++i)
|
||
{
|
||
span[i] = (ushort)values[i];
|
||
}
|
||
}
|
||
|
||
// Index用
|
||
public int[] GetAsIntArray()
|
||
{
|
||
if (AccessorType != AccessorVectorType.SCALAR)
|
||
{
|
||
throw new InvalidOperationException("not scalar");
|
||
}
|
||
switch (ComponentType)
|
||
{
|
||
case AccessorValueType.UNSIGNED_SHORT:
|
||
{
|
||
var span = SpanLike.Wrap<UInt16>(Bytes);
|
||
var array = new int[span.Length];
|
||
for (int i = 0; i < span.Length; ++i)
|
||
{
|
||
array[i] = span[i];
|
||
}
|
||
return array;
|
||
}
|
||
|
||
case AccessorValueType.UNSIGNED_INT:
|
||
return SpanLike.Wrap<Int32>(Bytes).ToArray();
|
||
|
||
default:
|
||
throw new NotImplementedException();
|
||
}
|
||
}
|
||
|
||
public List<int> GetAsIntList()
|
||
{
|
||
if (AccessorType != AccessorVectorType.SCALAR)
|
||
{
|
||
throw new InvalidOperationException("not scalar");
|
||
}
|
||
switch (ComponentType)
|
||
{
|
||
case AccessorValueType.UNSIGNED_SHORT:
|
||
{
|
||
var span = SpanLike.Wrap<UInt16>(Bytes);
|
||
var array = new List<int>(Count);
|
||
if (span.Length != Count)
|
||
{
|
||
for (int i = 0; i < Count; ++i)
|
||
{
|
||
array.Add(span[i]);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Spanが動かない?WorkAround
|
||
var bytes = Bytes.ToArray();
|
||
var offset = 0;
|
||
for (int i = 0; i < Count; ++i)
|
||
{
|
||
array.Add(BitConverter.ToUInt16(bytes, offset));
|
||
offset += 2;
|
||
}
|
||
}
|
||
return array;
|
||
}
|
||
|
||
case AccessorValueType.UNSIGNED_INT:
|
||
return SpanLike.Wrap<Int32>(Bytes).ToArray().ToList();
|
||
|
||
default:
|
||
throw new NotImplementedException();
|
||
}
|
||
}
|
||
|
||
// Joints用
|
||
public UShort4[] GetAsUShort4()
|
||
{
|
||
if (AccessorType != AccessorVectorType.VEC4)
|
||
{
|
||
throw new InvalidOperationException("not vec4");
|
||
}
|
||
switch (ComponentType)
|
||
{
|
||
case AccessorValueType.UNSIGNED_SHORT:
|
||
return SpanLike.Wrap<UShort4>(Bytes).ToArray();
|
||
|
||
case AccessorValueType.UNSIGNED_BYTE:
|
||
{
|
||
var array = new UShort4[Count];
|
||
var span = SpanLike.Wrap<Byte4>(Bytes);
|
||
for (int i = 0; i < span.Length; ++i)
|
||
{
|
||
array[i] = new UShort4(span[i].x, span[i].y, span[i].z, span[i].w);
|
||
}
|
||
return array;
|
||
}
|
||
|
||
default:
|
||
throw new NotImplementedException();
|
||
}
|
||
}
|
||
|
||
// Weigt用
|
||
public Vector4[] GetAsVector4()
|
||
{
|
||
if (AccessorType != AccessorVectorType.VEC4)
|
||
{
|
||
throw new InvalidOperationException("not vec4");
|
||
}
|
||
switch (ComponentType)
|
||
{
|
||
case AccessorValueType.FLOAT:
|
||
return SpanLike.Wrap<Vector4>(Bytes).ToArray();
|
||
|
||
default:
|
||
throw new NotImplementedException();
|
||
}
|
||
}
|
||
|
||
public void Resize(int count)
|
||
{
|
||
if (count < Count)
|
||
{
|
||
throw new Exception();
|
||
}
|
||
ToByteLength(Stride * count);
|
||
|
||
Count = count;
|
||
}
|
||
|
||
void ToByteLength(int byteLength)
|
||
{
|
||
var newBytes = new byte[byteLength];
|
||
Buffer.BlockCopy(Bytes.Array, Bytes.Offset, newBytes, 0, Bytes.Count);
|
||
Bytes = new ArraySegment<byte>(newBytes);
|
||
}
|
||
|
||
public void Extend(int count)
|
||
{
|
||
var oldLength = Bytes.Count;
|
||
ToByteLength(oldLength + Stride * count);
|
||
Count += count;
|
||
}
|
||
|
||
//
|
||
// ArraySegment<byte> を新規に確保して置き換える
|
||
//
|
||
public void Append(BufferAccessor a, int offset = -1)
|
||
{
|
||
if (AccessorType != a.AccessorType)
|
||
{
|
||
System.Console.WriteLine(AccessorType.ToString() + "!=" + a.AccessorType.ToString());
|
||
throw new Exception("different AccessorType");
|
||
}
|
||
|
||
// UNSIGNED_SHORT <-> UNSIGNED_INT の変換を許容して処理を続行
|
||
// 統合メッシュのprimitiveのIndexBufferが65,535(ushort.MaxValue)を超える場合や、変換前にindexBuffer.ComponetTypeがushortとuint混在する場合など
|
||
if (ComponentType != a.ComponentType)
|
||
{
|
||
switch (a.ComponentType)
|
||
{
|
||
//ushort to uint
|
||
case AccessorValueType.UNSIGNED_SHORT:
|
||
{
|
||
var src = SpanLike.Wrap<UInt16>(a.Bytes).Slice(0, a.Count);
|
||
var bytes = new byte[src.Length * 4];
|
||
var dst = SpanLike.Wrap<UInt32>(new ArraySegment<byte>(bytes));
|
||
for (int i = 0; i < src.Length; ++i)
|
||
{
|
||
dst[i] = (uint)src[i];
|
||
}
|
||
var accessor = new BufferAccessor(new ArraySegment<byte>(bytes), AccessorValueType.UNSIGNED_INT, AccessorVectorType.SCALAR, a.Count);
|
||
a = accessor;
|
||
|
||
break;
|
||
}
|
||
|
||
//uint to ushort (おそらく通ることはない)
|
||
case AccessorValueType.UNSIGNED_INT:
|
||
{
|
||
var src = SpanLike.Wrap<UInt32>(a.Bytes).Slice(0, a.Count);
|
||
var bytes = new byte[src.Length * 2];
|
||
var dst = SpanLike.Wrap<UInt16>(new ArraySegment<byte>(bytes));
|
||
for (int i = 0; i < src.Length; ++i)
|
||
{
|
||
dst[i] = (ushort)src[i];
|
||
}
|
||
var accessor = new BufferAccessor(new ArraySegment<byte>(bytes), ComponentType, AccessorVectorType.SCALAR, a.Count);
|
||
a = accessor;
|
||
break;
|
||
}
|
||
|
||
default:
|
||
throw new Exception("Cannot Convert ComponentType");
|
||
|
||
}
|
||
}
|
||
|
||
// 連結した新しいバッファを確保
|
||
var oldLength = Bytes.Count;
|
||
ToByteLength(oldLength + a.Bytes.Count);
|
||
// 後ろにコピー
|
||
Buffer.BlockCopy(a.Bytes.Array, a.Bytes.Offset, Bytes.Array, Bytes.Offset + oldLength, a.Bytes.Count);
|
||
Count += a.Count;
|
||
|
||
if (offset > 0)
|
||
{
|
||
// 後半にoffsetを足す
|
||
switch (ComponentType)
|
||
{
|
||
case AccessorValueType.UNSIGNED_SHORT:
|
||
{
|
||
var span = SpanLike.Wrap<UInt16>(Bytes.Slice(oldLength));
|
||
var ushortOffset = (ushort)offset;
|
||
for (int i = 0; i < span.Length; ++i)
|
||
{
|
||
span[i] += ushortOffset;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case AccessorValueType.UNSIGNED_INT:
|
||
{
|
||
var span = SpanLike.Wrap<UInt32>(Bytes.Slice(oldLength));
|
||
var uintOffset = (uint)offset;
|
||
for (int i = 0; i < span.Length; ++i)
|
||
{
|
||
span[i] += uintOffset;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
throw new NotImplementedException();
|
||
}
|
||
}
|
||
}
|
||
|
||
public BufferAccessor Skip(int skipFrames)
|
||
{
|
||
skipFrames = Math.Min(Count, skipFrames);
|
||
if (skipFrames == 0)
|
||
{
|
||
return this;
|
||
}
|
||
|
||
return new BufferAccessor(Bytes.Slice(Stride * skipFrames), ComponentType, AccessorType, Count - skipFrames);
|
||
}
|
||
|
||
public BufferAccessor CloneWithOffset(int offsetCount)
|
||
{
|
||
var offsetSize = Stride * offsetCount;
|
||
var buffer = new byte[offsetSize + Bytes.Count];
|
||
|
||
Buffer.BlockCopy(Bytes.Array, Bytes.Offset, buffer, offsetSize, Bytes.Count);
|
||
|
||
return new BufferAccessor(new ArraySegment<byte>(buffer), ComponentType, AccessorType, Count + offsetCount);
|
||
}
|
||
|
||
public BufferAccessor(ArraySegment<byte> bytes, AccessorValueType componentType, AccessorVectorType accessorType, int count)
|
||
{
|
||
Bytes = bytes;
|
||
ComponentType = componentType;
|
||
AccessorType = accessorType;
|
||
Count = count;
|
||
}
|
||
|
||
public static BufferAccessor Create<T>(T[] list) where T : struct
|
||
{
|
||
var t = typeof(T);
|
||
var bytes = new byte[list.Length * Marshal.SizeOf(t)];
|
||
var span = SpanLike.Wrap<T>(new ArraySegment<byte>(bytes));
|
||
for (int i = 0; i < list.Length; ++i)
|
||
{
|
||
span[i] = list[i];
|
||
}
|
||
AccessorValueType componentType = default(AccessorValueType);
|
||
AccessorVectorType accessorType = default(AccessorVectorType);
|
||
if (t == typeof(Vector2))
|
||
{
|
||
componentType = AccessorValueType.FLOAT;
|
||
accessorType = AccessorVectorType.VEC2;
|
||
}
|
||
else if (t == typeof(Vector3))
|
||
{
|
||
componentType = AccessorValueType.FLOAT;
|
||
accessorType = AccessorVectorType.VEC3;
|
||
}
|
||
else if (t == typeof(Vector4))
|
||
{
|
||
componentType = AccessorValueType.FLOAT;
|
||
accessorType = AccessorVectorType.VEC4;
|
||
}
|
||
else if (t == typeof(Quaternion))
|
||
{
|
||
componentType = AccessorValueType.FLOAT;
|
||
accessorType = AccessorVectorType.VEC4;
|
||
}
|
||
else if (t == typeof(SkinJoints))
|
||
{
|
||
componentType = AccessorValueType.UNSIGNED_SHORT;
|
||
accessorType = AccessorVectorType.VEC4;
|
||
}
|
||
else if (t == typeof(int))
|
||
{
|
||
componentType = AccessorValueType.UNSIGNED_INT;
|
||
accessorType = AccessorVectorType.SCALAR;
|
||
}
|
||
else
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
return new BufferAccessor(
|
||
new ArraySegment<byte>(bytes), componentType, accessorType, list.Length);
|
||
}
|
||
|
||
public void AddTo(Dictionary<string, BufferAccessor> dict, string key)
|
||
{
|
||
dict.Add(key, this);
|
||
}
|
||
}
|
||
}
|