mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-15 15:11:43 -05:00
Merge pull request #1527 from ousttrue/fix/index_buffer_bytes
IndexBuffer のとりまわし
This commit is contained in:
commit
258cc1925b
304
Assets/UniGLTF/Runtime/UniGLTF/IO/BufferAccessor.cs
Normal file
304
Assets/UniGLTF/Runtime/UniGLTF/IO/BufferAccessor.cs
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.Collections;
|
||||
|
||||
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_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();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 27cdb60e5e9634743875e0655372e682
|
||||
guid: aac48ad8d56549248a1ef57ff815d151
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -157,15 +157,16 @@ namespace UniGLTF
|
|||
return segment.GetSubArray(view.byteOffset, view.byteLength);
|
||||
}
|
||||
|
||||
NativeArray<T> GetTypedFromBufferView<T>(int count, int byteOffset, glTFBufferView view) where T : struct
|
||||
NativeArray<byte> GetBytesFromBufferView(glTFBufferView view)
|
||||
{
|
||||
var segment = GetBytesFromBuffer(view.buffer);
|
||||
return segment.GetSubArray(view.byteOffset + byteOffset, count * Marshal.SizeOf<T>()).Reinterpret<T>(1);
|
||||
return segment.GetSubArray(view.byteOffset, view.byteLength);
|
||||
}
|
||||
|
||||
NativeArray<T> GetTypedFromAccessor<T>(glTFAccessor accessor, glTFBufferView view) where T : struct
|
||||
{
|
||||
return GetTypedFromBufferView<T>(accessor.count, accessor.byteOffset, view);
|
||||
var bytes = GetBytesFromBufferView(view);
|
||||
return bytes.GetSubArray(accessor.byteOffset, bytes.Length - accessor.byteOffset).Reinterpret<T>(1).GetSubArray(0, accessor.count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -176,76 +177,39 @@ namespace UniGLTF
|
|||
/// <param name="byteOffset"></param>
|
||||
/// <param name="componentType"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<int> GetIntIndicesFromView(glTFBufferView view, int count, int byteOffset, glComponentType componentType)
|
||||
BufferAccessor GetIntIndicesFromView(glTFBufferView view, int count, int byteOffset, glComponentType componentType)
|
||||
{
|
||||
var bytes = GetBytesFromBufferView(view);
|
||||
switch (componentType)
|
||||
{
|
||||
case glComponentType.UNSIGNED_BYTE:
|
||||
{
|
||||
return GetTypedFromBufferView<Byte>(count, byteOffset, view).Select(x => (int)(x));
|
||||
return new BufferAccessor(NativeArrayManager, bytes.GetSubArray(byteOffset, bytes.Length - byteOffset),
|
||||
AccessorValueType.UNSIGNED_BYTE, AccessorVectorType.SCALAR, count);
|
||||
}
|
||||
|
||||
case glComponentType.UNSIGNED_SHORT:
|
||||
{
|
||||
return GetTypedFromBufferView<UInt16>(count, byteOffset, view).Select(x => (int)(x));
|
||||
return new BufferAccessor(NativeArrayManager, bytes.GetSubArray(byteOffset, bytes.Length - byteOffset),
|
||||
AccessorValueType.UNSIGNED_SHORT, AccessorVectorType.SCALAR, count);
|
||||
}
|
||||
|
||||
case glComponentType.UNSIGNED_INT:
|
||||
{
|
||||
return GetTypedFromBufferView<UInt32>(count, byteOffset, view).Select(x => (int)(x));
|
||||
return new BufferAccessor(NativeArrayManager, bytes.GetSubArray(byteOffset, bytes.Length - byteOffset),
|
||||
AccessorValueType.UNSIGNED_INT, AccessorVectorType.SCALAR, count);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("GetIndices: unknown componenttype: " + componentType);
|
||||
}
|
||||
throw new NotImplementedException("GetIndices: unknown componenttype: " + componentType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get indices and cast to int
|
||||
/// </summary>
|
||||
/// <param name="accessor"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<int> GetIntIndicesFromAccessor(glTFAccessor accessor, out int count)
|
||||
public BufferAccessor GetIndicesFromAccessorIndex(int accessorIndex)
|
||||
{
|
||||
count = accessor.count;
|
||||
var accessor = GLTF.accessors[accessorIndex];
|
||||
var view = GLTF.bufferViews[accessor.bufferView];
|
||||
switch ((glComponentType)accessor.componentType)
|
||||
{
|
||||
case glComponentType.UNSIGNED_BYTE:
|
||||
{
|
||||
return GetTypedFromAccessor<Byte>(accessor, view).Select(x => (int)(x));
|
||||
}
|
||||
|
||||
case glComponentType.UNSIGNED_SHORT:
|
||||
{
|
||||
return GetTypedFromAccessor<UInt16>(accessor, view).Select(x => (int)(x));
|
||||
}
|
||||
|
||||
case glComponentType.UNSIGNED_INT:
|
||||
{
|
||||
return GetTypedFromAccessor<UInt32>(accessor, view).Select(x => (int)(x));
|
||||
}
|
||||
}
|
||||
throw new NotImplementedException("GetIndices: unknown componenttype: " + accessor.componentType);
|
||||
}
|
||||
|
||||
public int[] GetIndices(int accessorIndex)
|
||||
{
|
||||
int count;
|
||||
var result = GetIntIndicesFromAccessor(GLTF.accessors[accessorIndex], out count);
|
||||
var indices = new int[count];
|
||||
|
||||
// flip triangles
|
||||
var it = result.GetEnumerator();
|
||||
{
|
||||
for (int i = 0; i < count; i += 3)
|
||||
{
|
||||
it.MoveNext(); indices[i + 2] = it.Current;
|
||||
it.MoveNext(); indices[i + 1] = it.Current;
|
||||
it.MoveNext(); indices[i] = it.Current;
|
||||
}
|
||||
}
|
||||
|
||||
return indices;
|
||||
return GetIntIndicesFromView(view, accessor.count, accessor.byteOffset, accessor.componentType);
|
||||
}
|
||||
|
||||
public NativeArray<T> GetArrayFromAccessor<T>(int accessorIndex) where T : struct
|
||||
|
|
@ -263,14 +227,42 @@ namespace UniGLTF
|
|||
if (sparse != null && sparse.count > 0)
|
||||
{
|
||||
// override sparse values
|
||||
var indices = GetIntIndicesFromView(GLTF.bufferViews[sparse.indices.bufferView], sparse.count, sparse.indices.byteOffset, sparse.indices.componentType);
|
||||
var values = GetTypedFromBufferView<T>(sparse.count, sparse.values.byteOffset, GLTF.bufferViews[sparse.values.bufferView]);
|
||||
var _indices = GetIntIndicesFromView(GLTF.bufferViews[sparse.indices.bufferView], sparse.count, sparse.indices.byteOffset, sparse.indices.componentType);
|
||||
var bytes = GetBytesFromBufferView(GLTF.bufferViews[sparse.values.bufferView]);
|
||||
var values = bytes.GetSubArray(sparse.values.byteOffset, bytes.Length - sparse.values.byteOffset).Reinterpret<T>(1).GetSubArray(0, sparse.count);
|
||||
|
||||
var it = indices.GetEnumerator();
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
switch (_indices.ComponentType)
|
||||
{
|
||||
it.MoveNext();
|
||||
result[it.Current] = values[i];
|
||||
case AccessorValueType.UNSIGNED_BYTE:
|
||||
{
|
||||
var indices = _indices.Bytes;
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
result[indices[i]] = values[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AccessorValueType.UNSIGNED_SHORT:
|
||||
{
|
||||
var indices = _indices.Bytes.Reinterpret<ushort>(1);
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
result[indices[i]] = values[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
{
|
||||
var indices = _indices.Bytes.Reinterpret<int>(1);
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
result[indices[i]] = values[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
@ -300,14 +292,42 @@ namespace UniGLTF
|
|||
if (sparse != null && sparse.count > 0)
|
||||
{
|
||||
// override sparse values
|
||||
var indices = GetIntIndicesFromView(GLTF.bufferViews[sparse.indices.bufferView], sparse.count, sparse.indices.byteOffset, sparse.indices.componentType);
|
||||
var values = GetTypedFromBufferView<float>(sparse.count * vertexAccessor.TypeCount, sparse.values.byteOffset, GLTF.bufferViews[sparse.values.bufferView]);
|
||||
var _indices = GetIntIndicesFromView(GLTF.bufferViews[sparse.indices.bufferView], sparse.count, sparse.indices.byteOffset, sparse.indices.componentType);
|
||||
var bytes = GetBytesFromBufferView(GLTF.bufferViews[sparse.values.bufferView]);
|
||||
var values = bytes.GetSubArray(sparse.values.byteOffset, bytes.Length - sparse.values.byteOffset).Reinterpret<float>(1).GetSubArray(0, sparse.count * vertexAccessor.TypeCount);
|
||||
|
||||
var it = indices.GetEnumerator();
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
switch (_indices.ComponentType)
|
||||
{
|
||||
it.MoveNext();
|
||||
result[it.Current] = values[i];
|
||||
case AccessorValueType.UNSIGNED_BYTE:
|
||||
{
|
||||
var indices = _indices.Bytes;
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
result[indices[i]] = values[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AccessorValueType.UNSIGNED_SHORT:
|
||||
{
|
||||
var indices = _indices.Bytes.Reinterpret<ushort>(1);
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
result[indices[i]] = values[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
{
|
||||
var indices = _indices.Bytes.Reinterpret<int>(1);
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
result[indices[i]] = values[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,58 @@ namespace UniGLTF
|
|||
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// * flip triangle
|
||||
/// * add submesh offset
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <param name="offset"></param>
|
||||
void PushIndices(BufferAccessor src, int offset)
|
||||
{
|
||||
switch (src.ComponentType)
|
||||
{
|
||||
case AccessorValueType.UNSIGNED_BYTE:
|
||||
{
|
||||
var indices = src.Bytes;
|
||||
for (int i = 0; i < src.Count; i += 3)
|
||||
{
|
||||
_indices.Add(offset + indices[i + 2]);
|
||||
_indices.Add(offset + indices[i + 1]);
|
||||
_indices.Add(offset + indices[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AccessorValueType.UNSIGNED_SHORT:
|
||||
{
|
||||
var indices = src.Bytes.Reinterpret<ushort>(1);
|
||||
for (int i = 0; i < src.Count; i += 3)
|
||||
{
|
||||
_indices.Add(offset + indices[i + 2]);
|
||||
_indices.Add(offset + indices[i + 1]);
|
||||
_indices.Add(offset + indices[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
{
|
||||
// たぶん int で OK
|
||||
var indices = src.Bytes.Reinterpret<int>(1);
|
||||
for (int i = 0; i < src.Count; i += 3)
|
||||
{
|
||||
_indices.Add(offset + indices[i + 2]);
|
||||
_indices.Add(offset + indices[i + 1]);
|
||||
_indices.Add(offset + indices[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 頂点情報をMeshに対して送る
|
||||
/// </summary>
|
||||
|
|
@ -112,6 +164,25 @@ namespace UniGLTF
|
|||
return src;
|
||||
}
|
||||
|
||||
int GetIndicesCapacity(GltfData data, glTFMesh gltfMesh)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var primitive in gltfMesh.primitives)
|
||||
{
|
||||
if (primitive.indices == -1)
|
||||
{
|
||||
var positions = data.GLTF.accessors[primitive.attributes.POSITION];
|
||||
count += positions.count;
|
||||
}
|
||||
else
|
||||
{
|
||||
var accessor = data.GLTF.accessors[primitive.indices];
|
||||
count += accessor.count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 各 primitive の attribute の要素が同じでない。=> uv が有るものと無いものが混在するなど
|
||||
/// glTF 的にはありうる。
|
||||
|
|
@ -123,6 +194,8 @@ namespace UniGLTF
|
|||
/// <returns></returns>
|
||||
public void ImportMeshIndependentVertexBuffer(GltfData data, glTFMesh gltfMesh, IAxisInverter inverter)
|
||||
{
|
||||
_indices.Capacity = GetIndicesCapacity(data, gltfMesh);
|
||||
|
||||
foreach (var primitives in gltfMesh.primitives)
|
||||
{
|
||||
var vertexOffset = _vertices.Count;
|
||||
|
|
@ -232,9 +305,9 @@ namespace UniGLTF
|
|||
if (indexBufferCount >= 0)
|
||||
{
|
||||
var indexOffset = _indices.Count;
|
||||
var dataIndices = data.GetIndices(indexBufferCount);
|
||||
_indices.AddRange(dataIndices.Select(index => index + vertexOffset));
|
||||
_subMeshes.Add(new SubMeshDescriptor(indexOffset, dataIndices.Length));
|
||||
var dataIndices = data.GetIndicesFromAccessorIndex(indexBufferCount);
|
||||
PushIndices(dataIndices, vertexOffset);
|
||||
_subMeshes.Add(new SubMeshDescriptor(indexOffset, dataIndices.Count));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -268,6 +341,8 @@ namespace UniGLTF
|
|||
/// <returns></returns>
|
||||
public void ImportMeshSharingVertexBuffer(GltfData data, glTFMesh gltfMesh, IAxisInverter inverter)
|
||||
{
|
||||
_indices.Capacity = GetIndicesCapacity(data, gltfMesh);
|
||||
|
||||
{
|
||||
// 同じVertexBufferを共有しているので先頭のモノを使う
|
||||
var primitives = gltfMesh.primitives.First();
|
||||
|
|
@ -369,9 +444,9 @@ namespace UniGLTF
|
|||
else
|
||||
{
|
||||
var indexOffset = _indices.Count;
|
||||
var indices = data.GetIndices(primitive.indices);
|
||||
_indices.AddRange(indices);
|
||||
_subMeshes.Add(new SubMeshDescriptor(indexOffset, indices.Length));
|
||||
var indices = data.GetIndicesFromAccessorIndex(primitive.indices);
|
||||
PushIndices(indices, 0);
|
||||
_subMeshes.Add(new SubMeshDescriptor(indexOffset, indices.Count));
|
||||
}
|
||||
|
||||
// material
|
||||
|
|
|
|||
|
|
@ -134,32 +134,29 @@ namespace UniGLTF
|
|||
var axisInverter = Axes.X.Create();
|
||||
|
||||
var unityMesh = MeshExportList.Create(go);
|
||||
var (gltfMesh, blendShapeIndexMap) = meshExportSettings.DivideVertexBuffer
|
||||
? MeshExporter_DividedVertexBuffer.Export(data, unityMesh, Materials, axisInverter, meshExportSettings)
|
||||
: MeshExporter_SharedVertexBuffer.Export(data, unityMesh, Materials, axisInverter, meshExportSettings)
|
||||
;
|
||||
var (gltfMesh, blendShapeIndexMap) = MeshExporter_SharedVertexBuffer.Export(data, unityMesh, Materials, axisInverter, meshExportSettings);
|
||||
|
||||
using (var parsed = GltfData.CreateFromGltfDataForTest(data.Gltf, data.BinBytes))
|
||||
{
|
||||
|
||||
{
|
||||
var indices = parsed.GetIndices(gltfMesh.primitives[0].indices);
|
||||
Assert.AreEqual(0, indices[0]);
|
||||
var indices = parsed.GetIndicesFromAccessorIndex(gltfMesh.primitives[0].indices).Bytes.Reinterpret<int>(1);
|
||||
Assert.AreEqual(5, indices[0]);
|
||||
Assert.AreEqual(1, indices[1]);
|
||||
Assert.AreEqual(5, indices[2]);
|
||||
Assert.AreEqual(5, indices[3]);
|
||||
Assert.AreEqual(0, indices[2]);
|
||||
Assert.AreEqual(4, indices[3]);
|
||||
Assert.AreEqual(1, indices[4]);
|
||||
Assert.AreEqual(4, indices[5]);
|
||||
Assert.AreEqual(5, indices[5]);
|
||||
}
|
||||
|
||||
{
|
||||
var indices = parsed.GetIndices(gltfMesh.primitives[1].indices);
|
||||
Assert.AreEqual(1, indices[0]);
|
||||
var indices = parsed.GetIndicesFromAccessorIndex(gltfMesh.primitives[1].indices).Bytes.Reinterpret<int>(1);
|
||||
Assert.AreEqual(4, indices[0]);
|
||||
Assert.AreEqual(2, indices[1]);
|
||||
Assert.AreEqual(4, indices[2]);
|
||||
Assert.AreEqual(4, indices[3]);
|
||||
Assert.AreEqual(1, indices[2]);
|
||||
Assert.AreEqual(3, indices[3]);
|
||||
Assert.AreEqual(2, indices[4]);
|
||||
Assert.AreEqual(3, indices[5]);
|
||||
Assert.AreEqual(4, indices[5]);
|
||||
}
|
||||
|
||||
var positions = parsed.GetArrayFromAccessor<Vector3>(gltfMesh.primitives[0].attributes.POSITION);
|
||||
|
|
@ -185,21 +182,19 @@ namespace UniGLTF
|
|||
var axisInverter = Axes.X.Create();
|
||||
|
||||
var unityMesh = MeshExportList.Create(go);
|
||||
var (gltfMesh, blendShapeIndexMap) = meshExportSettings.DivideVertexBuffer
|
||||
? MeshExporter_DividedVertexBuffer.Export(data, unityMesh, Materials, axisInverter, meshExportSettings)
|
||||
: MeshExporter_SharedVertexBuffer.Export(data, unityMesh, Materials, axisInverter, meshExportSettings)
|
||||
;
|
||||
|
||||
var (gltfMesh, blendShapeIndexMap) = MeshExporter_DividedVertexBuffer.Export(data, unityMesh, Materials, axisInverter, meshExportSettings);
|
||||
|
||||
using (var parsed = GltfData.CreateFromGltfDataForTest(data.Gltf, data.BinBytes))
|
||||
{
|
||||
{
|
||||
var indices = parsed.GetIndices(gltfMesh.primitives[0].indices);
|
||||
Assert.AreEqual(0, indices[0]);
|
||||
var indices = parsed.GetIndicesFromAccessorIndex(gltfMesh.primitives[0].indices).Bytes.Reinterpret<int>(1);
|
||||
Assert.AreEqual(3, indices[0]);
|
||||
Assert.AreEqual(1, indices[1]);
|
||||
Assert.AreEqual(3, indices[2]);
|
||||
Assert.AreEqual(3, indices[3]);
|
||||
Assert.AreEqual(0, indices[2]);
|
||||
Assert.AreEqual(2, indices[3]);
|
||||
Assert.AreEqual(1, indices[4]);
|
||||
Assert.AreEqual(2, indices[5]);
|
||||
Assert.AreEqual(3, indices[5]);
|
||||
}
|
||||
{
|
||||
var positions = parsed.GetArrayFromAccessor<Vector3>(gltfMesh.primitives[0].attributes.POSITION);
|
||||
|
|
@ -207,13 +202,13 @@ namespace UniGLTF
|
|||
}
|
||||
|
||||
{
|
||||
var indices = parsed.GetIndices(gltfMesh.primitives[1].indices);
|
||||
Assert.AreEqual(0, indices[0]);
|
||||
var indices = parsed.GetIndicesFromAccessorIndex(gltfMesh.primitives[1].indices).Bytes.Reinterpret<int>(1);
|
||||
Assert.AreEqual(3, indices[0]);
|
||||
Assert.AreEqual(1, indices[1]);
|
||||
Assert.AreEqual(3, indices[2]);
|
||||
Assert.AreEqual(3, indices[3]);
|
||||
Assert.AreEqual(0, indices[2]);
|
||||
Assert.AreEqual(2, indices[3]);
|
||||
Assert.AreEqual(1, indices[4]);
|
||||
Assert.AreEqual(2, indices[5]);
|
||||
Assert.AreEqual(3, indices[5]);
|
||||
}
|
||||
{
|
||||
var positions = parsed.GetArrayFromAccessor<Vector3>(gltfMesh.primitives[1].attributes.POSITION);
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using UniGLTF;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
public static class BufferAccessorAdapter
|
||||
{
|
||||
public static int TypeCount(this string type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "SCALAR":
|
||||
return 1;
|
||||
case "VEC2":
|
||||
return 2;
|
||||
case "VEC3":
|
||||
return 3;
|
||||
case "VEC4":
|
||||
case "MAT2":
|
||||
return 4;
|
||||
case "MAT3":
|
||||
return 9;
|
||||
case "MAT4":
|
||||
return 16;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static int AddViewTo(this VrmLib.BufferAccessor self,
|
||||
ExportingGltfData data, int bufferIndex,
|
||||
int offset = 0, int count = 0)
|
||||
{
|
||||
var stride = self.Stride;
|
||||
if (count == 0)
|
||||
{
|
||||
count = self.Count;
|
||||
}
|
||||
var slice = self.Bytes.GetSubArray(offset * stride, count * stride);
|
||||
return data.AppendToBuffer(slice);
|
||||
}
|
||||
|
||||
static glTFAccessor CreateGltfAccessor(this VrmLib.BufferAccessor self,
|
||||
int viewIndex, int count = 0, int byteOffset = 0)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
count = self.Count;
|
||||
}
|
||||
return new glTFAccessor
|
||||
{
|
||||
bufferView = viewIndex,
|
||||
byteOffset = byteOffset,
|
||||
componentType = (glComponentType)self.ComponentType,
|
||||
type = self.AccessorType.ToString(),
|
||||
count = count,
|
||||
};
|
||||
}
|
||||
|
||||
public static int AddAccessorTo(this VrmLib.BufferAccessor self,
|
||||
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 = self.CreateGltfAccessor(viewIndex, count, offset * self.Stride);
|
||||
if (minMax != null)
|
||||
{
|
||||
minMax(self.Bytes, accessor);
|
||||
}
|
||||
gltf.accessors.Add(accessor);
|
||||
return accessorIndex;
|
||||
}
|
||||
|
||||
public static int AddAccessorTo(this VrmLib.BufferAccessor self,
|
||||
ExportingGltfData data, int bufferIndex,
|
||||
// GltfBufferTargetType targetType,
|
||||
bool useSparse,
|
||||
Action<NativeArray<byte>, glTFAccessor> minMax = null,
|
||||
int offset = 0, int count = 0)
|
||||
{
|
||||
if (self.ComponentType == VrmLib.AccessorValueType.FLOAT
|
||||
&& self.AccessorType == VrmLib.AccessorVectorType.VEC3
|
||||
)
|
||||
{
|
||||
var values = self.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)self.ComponentType,
|
||||
type = self.AccessorType.ToString(),
|
||||
count = self.Count,
|
||||
byteOffset = -1,
|
||||
sparse = new glTFSparse
|
||||
{
|
||||
count = sparseValuesWithIndex.Count,
|
||||
indices = new glTFSparseIndices
|
||||
{
|
||||
componentType = (glComponentType)VrmLib.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 = self.AddViewTo(data, bufferIndex, offset, count);
|
||||
return self.AddAccessorTo(data, viewIndex, minMax, 0, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9bdd7001c32368b4da917a0384e1ef86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -66,32 +66,29 @@ namespace UniVRM10
|
|||
foreach (var mesh in meshGroup.Meshes)
|
||||
{
|
||||
var morphTarget = mesh.MorphTargets[i];
|
||||
morphTarget.VertexBuffer.Positions.CopyToNativeSlice(
|
||||
new NativeSlice<Vector3>(
|
||||
blendShapePositions,
|
||||
blendShapePositionOffset,
|
||||
morphTarget.VertexBuffer.Positions.Count
|
||||
)
|
||||
);
|
||||
|
||||
// nullならdefault(0)のまま
|
||||
morphTarget.VertexBuffer.Normals?.CopyToNativeSlice(
|
||||
new NativeSlice<Vector3>(
|
||||
blendShapeNormals,
|
||||
blendShapeNormalOffset,
|
||||
morphTarget.VertexBuffer.Normals.Count
|
||||
)
|
||||
);
|
||||
|
||||
NativeArray<Vector3>.Copy(
|
||||
morphTarget.VertexBuffer.Positions.Bytes.Reinterpret<Vector3>(1),
|
||||
blendShapePositions.GetSubArray(blendShapePositionOffset, morphTarget.VertexBuffer.Positions.Count));
|
||||
|
||||
if (morphTarget.VertexBuffer.Normals != null)
|
||||
{
|
||||
// nullならdefault(0)のまま
|
||||
NativeArray<Vector3>.Copy(
|
||||
morphTarget.VertexBuffer.Normals.Bytes.Reinterpret<Vector3>(1),
|
||||
blendShapeNormals.GetSubArray(blendShapeNormalOffset, morphTarget.VertexBuffer.Normals.Count));
|
||||
}
|
||||
|
||||
blendShapePositionOffset += morphTarget.VertexBuffer.Positions.Count;
|
||||
blendShapeNormalOffset += morphTarget.VertexBuffer.Normals?.Count ?? morphTarget.VertexBuffer.Count;
|
||||
}
|
||||
|
||||
resultMesh.AddBlendShapeFrame(meshGroup.Meshes[0].MorphTargets[i].Name,
|
||||
100.0f,
|
||||
blendShapePositions.ToArray(),
|
||||
blendShapeNormals.ToArray(),
|
||||
null);
|
||||
100.0f,
|
||||
blendShapePositions.ToArray(),
|
||||
blendShapeNormals.ToArray(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -272,44 +272,44 @@ namespace UniVRM10
|
|||
return skin;
|
||||
}
|
||||
|
||||
private static VrmLib.BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, SkinJoints[] values)
|
||||
private static BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, SkinJoints[] values)
|
||||
{
|
||||
return ToBufferAccessor(arrayManager, values, VrmLib.AccessorValueType.UNSIGNED_SHORT, VrmLib.AccessorVectorType.VEC4);
|
||||
return ToBufferAccessor(arrayManager, values, AccessorValueType.UNSIGNED_SHORT, AccessorVectorType.VEC4);
|
||||
}
|
||||
|
||||
private static VrmLib.BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Color[] colors)
|
||||
private static BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Color[] colors)
|
||||
{
|
||||
return ToBufferAccessor(arrayManager, colors, VrmLib.AccessorValueType.FLOAT, VrmLib.AccessorVectorType.VEC4);
|
||||
return ToBufferAccessor(arrayManager, colors, AccessorValueType.FLOAT, AccessorVectorType.VEC4);
|
||||
}
|
||||
|
||||
private static VrmLib.BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Vector4[] vectors)
|
||||
private static BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Vector4[] vectors)
|
||||
{
|
||||
return ToBufferAccessor(arrayManager, vectors, VrmLib.AccessorValueType.FLOAT, VrmLib.AccessorVectorType.VEC4);
|
||||
return ToBufferAccessor(arrayManager, vectors, AccessorValueType.FLOAT, AccessorVectorType.VEC4);
|
||||
}
|
||||
|
||||
private static VrmLib.BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Vector3[] vectors)
|
||||
private static BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Vector3[] vectors)
|
||||
{
|
||||
return ToBufferAccessor(arrayManager, vectors, VrmLib.AccessorValueType.FLOAT, VrmLib.AccessorVectorType.VEC3);
|
||||
return ToBufferAccessor(arrayManager, vectors, AccessorValueType.FLOAT, AccessorVectorType.VEC3);
|
||||
}
|
||||
|
||||
private static VrmLib.BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Vector2[] vectors)
|
||||
private static BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Vector2[] vectors)
|
||||
{
|
||||
return ToBufferAccessor(arrayManager, vectors, VrmLib.AccessorValueType.FLOAT, VrmLib.AccessorVectorType.VEC2);
|
||||
return ToBufferAccessor(arrayManager, vectors, AccessorValueType.FLOAT, AccessorVectorType.VEC2);
|
||||
}
|
||||
|
||||
private static VrmLib.BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, int[] scalars)
|
||||
private static BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, int[] scalars)
|
||||
{
|
||||
return ToBufferAccessor(arrayManager, scalars, VrmLib.AccessorValueType.UNSIGNED_INT, VrmLib.AccessorVectorType.SCALAR);
|
||||
return ToBufferAccessor(arrayManager, scalars, AccessorValueType.UNSIGNED_INT, AccessorVectorType.SCALAR);
|
||||
}
|
||||
|
||||
private static VrmLib.BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Matrix4x4[] matrixes)
|
||||
private static BufferAccessor ToBufferAccessor(INativeArrayManager arrayManager, Matrix4x4[] matrixes)
|
||||
{
|
||||
return ToBufferAccessor(arrayManager, matrixes, VrmLib.AccessorValueType.FLOAT, VrmLib.AccessorVectorType.MAT4);
|
||||
return ToBufferAccessor(arrayManager, matrixes, AccessorValueType.FLOAT, AccessorVectorType.MAT4);
|
||||
}
|
||||
|
||||
private static VrmLib.BufferAccessor ToBufferAccessor<T>(INativeArrayManager arrayManager, T[] value, VrmLib.AccessorValueType valueType, VrmLib.AccessorVectorType vectorType) where T : struct
|
||||
private static BufferAccessor ToBufferAccessor<T>(INativeArrayManager arrayManager, T[] value, AccessorValueType valueType, AccessorVectorType vectorType) where T : struct
|
||||
{
|
||||
return new VrmLib.BufferAccessor(arrayManager,
|
||||
return new BufferAccessor(arrayManager,
|
||||
arrayManager.CreateNativeArray(value).Reinterpret<byte>(Marshal.SizeOf<T>()),
|
||||
valueType,
|
||||
vectorType,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace UniVRM10
|
|||
return index;
|
||||
}
|
||||
|
||||
int? AddAccessor<T>(VrmLib.BufferAccessor buffer) where T : struct
|
||||
int? AddAccessor<T>(BufferAccessor buffer) where T : struct
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,360 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 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;
|
||||
|
||||
public AccessorValueType ComponentType;
|
||||
|
||||
public AccessorVectorType AccessorType;
|
||||
|
||||
public int Stride => ComponentType.ByteSize() * AccessorType.TypeCount();
|
||||
|
||||
public int Count;
|
||||
|
||||
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 static BufferAccessor Create<T>(INativeArrayManager arrayManager, T[] list) where T : struct
|
||||
{
|
||||
var t = typeof(T);
|
||||
var bytes = arrayManager.CreateNativeArray<byte>(list.Length * Marshal.SizeOf(t));
|
||||
var span = bytes.Reinterpret<T>(1);
|
||||
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(arrayManager, bytes, componentType, accessorType, list.Length);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// バッファをNativeSliceへと書き込む
|
||||
/// </summary>
|
||||
public unsafe void CopyToNativeSlice<T>(NativeSlice<T> destArray) where T : unmanaged
|
||||
{
|
||||
var byteArray = NativeArrayUnsafeUtility.GetUnsafePtr(Bytes);
|
||||
UnsafeUtility.MemCpy((T*)destArray.GetUnsafePtr(), byteArray, Bytes.Length);
|
||||
}
|
||||
|
||||
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_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();
|
||||
}
|
||||
}
|
||||
|
||||
public void Resize(int count)
|
||||
{
|
||||
if (count < Count)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
ToByteLength(Stride * count);
|
||||
|
||||
Count = count;
|
||||
}
|
||||
|
||||
void ToByteLength(int byteLength)
|
||||
{
|
||||
var newBytes = ArrayManager.CreateNativeArray<byte>(byteLength);
|
||||
NativeArray<byte>.Copy(Bytes, newBytes);
|
||||
Bytes = newBytes;
|
||||
}
|
||||
|
||||
public void Extend(int count)
|
||||
{
|
||||
var oldLength = Bytes.Length;
|
||||
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 bytes = ArrayManager.Convert(a.Bytes.Reinterpret<UInt16>(1), (UInt16 x) => (UInt32)x).Reinterpret<byte>(Marshal.SizeOf<UInt32>());
|
||||
var accessor = new BufferAccessor(ArrayManager, bytes, AccessorValueType.UNSIGNED_INT, AccessorVectorType.SCALAR, a.Count);
|
||||
a = accessor;
|
||||
break;
|
||||
}
|
||||
|
||||
//uint to ushort (おそらく通ることはない)
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
{
|
||||
var bytes = ArrayManager.Convert(a.Bytes.Reinterpret<UInt32>(1), (UInt32 x) => (UInt16)x).Reinterpret<byte>(Marshal.SizeOf<UInt16>());
|
||||
var accessor = new BufferAccessor(ArrayManager, bytes, ComponentType, AccessorVectorType.SCALAR, a.Count);
|
||||
a = accessor;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Exception("Cannot Convert ComponentType");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 連結した新しいバッファを確保
|
||||
var oldLength = Bytes.Length;
|
||||
ToByteLength(oldLength + a.Bytes.Length);
|
||||
// 後ろにコピー
|
||||
NativeArray<byte>.Copy(a.Bytes, Bytes.GetSubArray(oldLength, Bytes.Length - oldLength));
|
||||
Count += a.Count;
|
||||
|
||||
if (offset > 0)
|
||||
{
|
||||
// 後半にoffsetを足す
|
||||
switch (ComponentType)
|
||||
{
|
||||
case AccessorValueType.UNSIGNED_SHORT:
|
||||
{
|
||||
var span = Bytes.GetSubArray(oldLength, Bytes.Length - oldLength).Reinterpret<UInt16>(1);
|
||||
var ushortOffset = (ushort)offset;
|
||||
for (int i = 0; i < span.Length; ++i)
|
||||
{
|
||||
span[i] += ushortOffset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
{
|
||||
var span = Bytes.GetSubArray(oldLength, Bytes.Length - oldLength).Reinterpret<UInt32>(1);
|
||||
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;
|
||||
}
|
||||
var start = Stride * skipFrames;
|
||||
return new BufferAccessor(ArrayManager, Bytes.GetSubArray(start, Bytes.Length - start), ComponentType, AccessorType, Count - skipFrames);
|
||||
}
|
||||
|
||||
public BufferAccessor CloneWithOffset(int offsetCount)
|
||||
{
|
||||
var offsetSize = Stride * offsetCount;
|
||||
var buffer = ArrayManager.CreateNativeArray<byte>(offsetSize + Bytes.Length);
|
||||
NativeArray<byte>.Copy(Bytes, buffer.GetSubArray(offsetSize, buffer.Length - offsetSize));
|
||||
return new BufferAccessor(ArrayManager, buffer, ComponentType, AccessorType, Count + offsetCount);
|
||||
}
|
||||
|
||||
public void AddTo(Dictionary<string, BufferAccessor> dict, string key)
|
||||
{
|
||||
dict.Add(key, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,139 +62,10 @@ namespace VrmLib
|
|||
|
||||
public BufferAccessor IndexBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// indicesの最大値が65535未満(-1を避ける)ならばushort 型で、
|
||||
/// そうでなければ int型で IndexBufferを代入する
|
||||
/// </summary>
|
||||
public void AssignIndexBuffer(NativeArray<int> indices)
|
||||
{
|
||||
bool isInt = false;
|
||||
foreach (var i in indices)
|
||||
{
|
||||
if (i >= short.MaxValue)
|
||||
{
|
||||
isInt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInt)
|
||||
{
|
||||
if (IndexBuffer.Stride != 4)
|
||||
{
|
||||
IndexBuffer.ComponentType = AccessorValueType.UNSIGNED_INT;
|
||||
if (IndexBuffer.AccessorType != AccessorVectorType.SCALAR)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
// 変換なし
|
||||
IndexBuffer.Assign(indices);
|
||||
}
|
||||
else
|
||||
{
|
||||
// int to ushort
|
||||
IndexBuffer.AssignAsShort(indices);
|
||||
}
|
||||
}
|
||||
|
||||
public TopologyType Topology = TopologyType.Triangles;
|
||||
|
||||
public List<Submesh> Submeshes { private set; get; } = new List<Submesh>();
|
||||
|
||||
public int SubmeshTotalDrawCount => Submeshes.Sum(x => x.DrawCount);
|
||||
|
||||
public IEnumerable<Triangle> GetTriangles(int i)
|
||||
{
|
||||
var indices = IndexBuffer.GetAsIntArray();
|
||||
|
||||
var submesh = Submeshes[i];
|
||||
var submeshEnd = submesh.Offset + submesh.DrawCount;
|
||||
for (int j = submesh.Offset; j < submeshEnd; j += 3)
|
||||
{
|
||||
var triangle = new Triangle(
|
||||
indices[j],
|
||||
indices[j + 1],
|
||||
indices[j + 2]
|
||||
);
|
||||
yield return triangle;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ValueTuple<int, Triangle>> Triangles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Topology != TopologyType.Triangles)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var indices = IndexBuffer.GetAsIntArray();
|
||||
|
||||
for (int i = 0; i < Submeshes.Count; ++i)
|
||||
{
|
||||
var submesh = Submeshes[i];
|
||||
var submeshEnd = submesh.Offset + submesh.DrawCount;
|
||||
for (int j = submesh.Offset; j < submeshEnd; j += 3)
|
||||
{
|
||||
var triangle = new Triangle(
|
||||
indices[j],
|
||||
indices[j + 1],
|
||||
indices[j + 2]
|
||||
);
|
||||
yield return (i, triangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GetSubmeshOverlapped<T>() where T : struct
|
||||
{
|
||||
var indices = IndexBuffer.GetSpan<ushort>();
|
||||
var offset = 0;
|
||||
var max = 0;
|
||||
foreach (var x in Submeshes)
|
||||
{
|
||||
var submeshIndices = indices.Slice(offset, x.DrawCount);
|
||||
var currentMax = 0;
|
||||
foreach (var y in submeshIndices)
|
||||
{
|
||||
if (y < max)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
currentMax = Math.Max(y, currentMax);
|
||||
}
|
||||
offset += x.DrawCount;
|
||||
max = currentMax;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsSubmeshOverlapped
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Submeshes.Count <= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (IndexBuffer.ComponentType)
|
||||
{
|
||||
case AccessorValueType.UNSIGNED_SHORT:
|
||||
return GetSubmeshOverlapped<ushort>();
|
||||
|
||||
case AccessorValueType.UNSIGNED_INT:
|
||||
return GetSubmeshOverlapped<uint>();
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MorphTarget> MorphTargets = new List<MorphTarget>();
|
||||
|
||||
public override string ToString()
|
||||
|
|
@ -238,11 +109,6 @@ namespace VrmLib
|
|||
Topology = topology;
|
||||
}
|
||||
|
||||
public void RemoveUnusedSubmesh()
|
||||
{
|
||||
Submeshes = Submeshes.Where(x => x.DrawCount != 0).ToList();
|
||||
}
|
||||
|
||||
// Skin.Normalize
|
||||
public void ApplyRotationAndScaling(Matrix4x4 m)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF;
|
||||
|
||||
namespace VrmLib
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user