Merge pull request #1527 from ousttrue/fix/index_buffer_bytes

IndexBuffer のとりまわし
This commit is contained in:
ousttrue 2022-02-16 13:50:28 +09:00 committed by GitHub
commit 258cc1925b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 529 additions and 801 deletions

View 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);
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 27cdb60e5e9634743875e0655372e682
guid: aac48ad8d56549248a1ef57ff815d151
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9bdd7001c32368b4da917a0384e1ef86
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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)
{

View File

@ -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,535ushort.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);
}
}
}

View File

@ -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)
{

View File

@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
namespace VrmLib
{