diff --git a/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs b/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs index d3249c45d..ddbb76b53 100644 --- a/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs +++ b/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs @@ -18,39 +18,36 @@ namespace UniVRM10 #endif internal struct InterleaveMeshVerticesJob : IJobParallelFor { - [WriteOnly, NativeDisableParallelForRestriction] - private NativeArray _vertices; + [WriteOnly] + private NativeSlice _vertices; [ReadOnly] - private readonly NativeArray _positions; + private readonly NativeSlice _positions; // default値を許容する [ReadOnly, NativeDisableContainerSafetyRestriction] - private readonly NativeArray _normals; + private readonly NativeSlice _normals; [ReadOnly, NativeDisableContainerSafetyRestriction] - private readonly NativeArray _texCoords; + private readonly NativeSlice _texCoords; [ReadOnly, NativeDisableContainerSafetyRestriction] - private readonly NativeArray _colors; + private readonly NativeSlice _colors; [ReadOnly, NativeDisableContainerSafetyRestriction] - private readonly NativeArray _weights; + private readonly NativeSlice _weights; [ReadOnly, NativeDisableContainerSafetyRestriction] - private readonly NativeArray _joints; - - private readonly int _verticesOffset; + private readonly NativeSlice _joints; public InterleaveMeshVerticesJob( - NativeArray vertices, + NativeSlice vertices, NativeArray positions, NativeArray normals = default, NativeArray texCoords = default, NativeArray colors = default, NativeArray weights = default, - NativeArray joints = default, - int verticesOffset = 0) + NativeArray joints = default) { _vertices = vertices; _positions = positions; @@ -59,24 +56,23 @@ namespace UniVRM10 _colors = colors; _weights = weights; _joints = joints; - _verticesOffset = verticesOffset; } public void Execute(int index) { - _vertices[index + _verticesOffset] = new MeshVertex( + _vertices[index] = new MeshVertex( _positions[index], - _normals.IsCreated ? _normals[index] : Vector3.zero, - _texCoords.IsCreated ? _texCoords[index] : Vector2.zero, - _colors.IsCreated ? _colors[index] : Color.white, - _joints.IsCreated ? _joints[index].Joint0 : (ushort)0, - _joints.IsCreated ? _joints[index].Joint1 : (ushort)0, - _joints.IsCreated ? _joints[index].Joint2 : (ushort)0, - _joints.IsCreated ? _joints[index].Joint3 : (ushort)0, - _weights.IsCreated ? _weights[index].x : 0, - _weights.IsCreated ? _weights[index].y : 0, - _weights.IsCreated ? _weights[index].z : 0, - _weights.IsCreated ? _weights[index].w : 0 + _normals.Length > 0 ? _normals[index] : Vector3.zero, + _texCoords.Length > 0 ? _texCoords[index] : Vector2.zero, + _colors.Length > 0 ? _colors[index] : Color.white, + _joints.Length > 0 ? _joints[index].Joint0 : (ushort)0, + _joints.Length > 0 ? _joints[index].Joint1 : (ushort)0, + _joints.Length > 0 ? _joints[index].Joint2 : (ushort)0, + _joints.Length > 0 ? _joints[index].Joint3 : (ushort)0, + _weights.Length > 0 ? _weights[index].x : 0, + _weights.Length > 0 ? _weights[index].y : 0, + _weights.Length > 0 ? _weights[index].z : 0, + _weights.Length > 0 ? _weights[index].w : 0 ); } } diff --git a/Assets/VRM10/Runtime/IO/Model/MeshImporter.cs b/Assets/VRM10/Runtime/IO/Model/MeshImporter.cs index 22d5e9845..113d54653 100644 --- a/Assets/VRM10/Runtime/IO/Model/MeshImporter.cs +++ b/Assets/VRM10/Runtime/IO/Model/MeshImporter.cs @@ -23,7 +23,6 @@ namespace UniVRM10 Profiler.BeginSample("MeshImporter.LoadSharedMesh"); var mesh = new Mesh(); - // これらのNativeArrayはJobによって開放される var positions = src.VertexBuffer.Positions.AsNativeArray(Allocator.TempJob); var normals = src.VertexBuffer.Normals?.AsNativeArray(Allocator.TempJob) ?? default; var texCoords = src.VertexBuffer.TexCoords?.AsNativeArray(Allocator.TempJob) ?? default; @@ -37,6 +36,7 @@ namespace UniVRM10 var jobHandle = new InterleaveMeshVerticesJob(vertices, positions, normals, texCoords, colors, weights, joints) .Schedule(vertices.Length, 1); + JobHandle.ScheduleBatchedJobs(); // BindPoseを更新 if (weights.IsCreated && joints.IsCreated) @@ -73,13 +73,13 @@ namespace UniVRM10 switch (src.IndexBuffer.ComponentType) { case AccessorValueType.UNSIGNED_SHORT: - var shortIndices = src.IndexBuffer.AsNativeArray(Allocator.Temp); + var shortIndices = src.IndexBuffer.AsNativeArray(Allocator.Temp); mesh.SetIndexBufferParams(shortIndices.Length, IndexFormat.UInt16); mesh.SetIndexBufferData(shortIndices, 0, 0, shortIndices.Length); shortIndices.Dispose(); break; case AccessorValueType.UNSIGNED_INT: - var intIndices = src.IndexBuffer.AsNativeArray(Allocator.Temp); + var intIndices = src.IndexBuffer.AsNativeArray(Allocator.Temp); mesh.SetIndexBufferParams(intIndices.Length, IndexFormat.UInt32); mesh.SetIndexBufferData(intIndices, 0, 0, intIndices.Length); intIndices.Dispose(); diff --git a/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs b/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs index a64c48409..bd57d6b95 100644 --- a/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs +++ b/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs @@ -6,92 +6,58 @@ using Unity.Collections; using Unity.Jobs; using UnityEngine; using UnityEngine.Profiling; +using UnityEngine.Rendering; +using VrmLib; +using Mesh = UnityEngine.Mesh; namespace UniVRM10 { public static class MeshImporterDivided { - public static Mesh LoadDivided(VrmLib.MeshGroup src) + public static Mesh LoadDivided(MeshGroup meshGroup) { Profiler.BeginSample("MeshImporterDivided.LoadDivided"); - - var dst = new UnityEngine.Mesh(); - if (src.Meshes.Sum(x => x.IndexBuffer.Count) > ushort.MaxValue) - { - dst.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; - } - // 頂点バッファを構築 - var vertexCount = src.Meshes.Sum(x => x.VertexBuffer.Count); - var vertices = new NativeArray(vertexCount, Allocator.TempJob); + var vertexCount = meshGroup.Meshes.Sum(mesh => mesh.VertexBuffer.Count); + var indexCount = meshGroup.Meshes.Sum(mesh => mesh.IndexBuffer.Count); - var jobDisposables = new List(); - - // JobのSchedule - JobHandle jobHandle = default; + var resultMesh = new Mesh(); + + // 頂点バッファ・BindPoseを構築して更新 + UpdateVerticesAndBindPose(meshGroup, vertexCount, resultMesh); + + // インデックスバッファを構築して更新 + UpdateIndices(meshGroup, vertexCount, indexCount, resultMesh); + + // SubMeshを更新 + resultMesh.subMeshCount = meshGroup.Meshes.Count; var indexOffset = 0; - foreach (var mesh in src.Meshes) + for (var i = 0; i < meshGroup.Meshes.Count; ++i) { - // これらのNativeArrayはJobによって開放される - var positions = mesh.VertexBuffer.Positions.AsNativeArray(Allocator.TempJob); - var normals = mesh.VertexBuffer.Normals.AsNativeArray(Allocator.TempJob); - var texCoords = mesh.VertexBuffer.TexCoords.AsNativeArray(Allocator.TempJob); - var weights = src.Skin != null ? mesh.VertexBuffer.Weights.AsNativeArray(Allocator.TempJob) : default; - var joints = src.Skin != null ? mesh.VertexBuffer.Joints.AsNativeArray(Allocator.TempJob) : default; - if (positions.IsCreated) jobDisposables.Add(positions); - if (normals.IsCreated) jobDisposables.Add(normals); - if (texCoords.IsCreated) jobDisposables.Add(texCoords); - if (weights.IsCreated) jobDisposables.Add(weights); - if (joints.IsCreated) jobDisposables.Add(joints); - - jobHandle = new InterleaveMeshVerticesJob(vertices, positions, normals, texCoords, default, weights, joints, indexOffset) - .Schedule(mesh.VertexBuffer.Count, 1, jobHandle); - indexOffset += mesh.VertexBuffer.Count; + var mesh = meshGroup.Meshes[i]; + resultMesh.SetSubMesh(i, new SubMeshDescriptor(indexOffset, mesh.IndexBuffer.Count)); + indexOffset += mesh.IndexBuffer.Count; } - // Jobと並行してBindposeの更新を行う - if (src.Skin != null) - { - dst.bindposes = src.Skin.InverseMatrices.GetSpan().ToArray(); - } - - // Jobを完了 - jobHandle.Complete(); - - foreach (var disposable in jobDisposables) - { - disposable.Dispose(); - } + // 各種データを再構築 + Profiler.BeginSample("Bounds"); + resultMesh.RecalculateBounds(); + Profiler.EndSample(); + Profiler.BeginSample("Tangents"); + resultMesh.RecalculateTangents(); + Profiler.EndSample(); - MeshVertex.SetVertexBufferParamsToMesh(dst, vertices.Length); - dst.SetVertexBufferData(vertices, 0, 0, vertices.Length); - - vertices.Dispose(); - - // triangles - dst.subMeshCount = src.Meshes.Count; - var offset = 0; - for (var meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex) - { - var mesh = src.Meshes[meshIndex]; - var indices = mesh.IndexBuffer.GetAsIntArray().Select(x => offset + x).ToArray(); - dst.SetTriangles(indices, meshIndex); - offset += mesh.VertexBuffer.Count; - } - - dst.RecalculateBounds(); - dst.RecalculateTangents(); - - // blendshape - var blendShapeCount = src.Meshes[0].MorphTargets.Count; + // BlendShapeを更新 + Profiler.BeginSample("BlendShape"); + var blendShapeCount = meshGroup.Meshes[0].MorphTargets.Count; var blendShapePositions = new List(); var blendShapeNormals = new List(); for (var i = 0; i < blendShapeCount; ++i) { blendShapePositions.Clear(); blendShapeNormals.Clear(); - var name = src.Meshes[0].MorphTargets[i].Name; - foreach (var mesh in src.Meshes) + var name = meshGroup.Meshes[0].MorphTargets[i].Name; + foreach (var mesh in meshGroup.Meshes) { var morphTarget = mesh.MorphTargets[i]; blendShapePositions.AddRange(morphTarget.VertexBuffer.Positions.GetSpan()); @@ -102,15 +68,207 @@ namespace UniVRM10 else { // fill zero - blendShapeNormals.AddRange(Enumerable.Range(0, morphTarget.VertexBuffer.Count).Select(x => Vector3.zero)); + blendShapeNormals.AddRange(Enumerable.Range(0, morphTarget.VertexBuffer.Count) + .Select(x => Vector3.zero)); } } - dst.AddBlendShapeFrame(name, 100.0f, blendShapePositions.ToArray(), blendShapeNormals.ToArray(), null); + + resultMesh.AddBlendShapeFrame(name, 100.0f, blendShapePositions.ToArray(), blendShapeNormals.ToArray(), + null); + } + Profiler.EndSample(); + + Profiler.EndSample(); + + return resultMesh; + } + + /// + /// インデックスバッファを更新する + /// MEMO: 出力に対するushortを考慮することをやめればかなりシンプルに書ける + /// + private static void UpdateIndices(MeshGroup meshGroup, int vertexCount, int indexCount, Mesh resultMesh) + { + Profiler.BeginSample("MeshImporterDivided.UpdateIndices"); + + JobHandle jobHandle = default; + + var disposables = new List(); + + // 出力をushortにするべきかどうかを判別 + if (vertexCount < ushort.MaxValue) + { + var indices = new NativeArray(indexCount, Allocator.TempJob); + disposables.Add(indices); + var indexOffset = 0; + var vertexOffset = 0; + foreach (var mesh in meshGroup.Meshes) + { + switch (mesh.IndexBuffer.ComponentType) + { + case AccessorValueType.SHORT: + { + // unsigned short -> unsigned short + var source = mesh.IndexBuffer.AsNativeArray(Allocator.TempJob); + disposables.Add(source); + jobHandle = new CopyIndicesJobs.Ushort2Ushort( + (ushort)vertexOffset, + new NativeSlice(source), + new NativeSlice(indices, indexOffset, mesh.IndexBuffer.Count)) + .Schedule(mesh.IndexBuffer.Count, 1, jobHandle); + break; + } + case AccessorValueType.UNSIGNED_INT: + { + // unsigned int -> unsigned short + var source = mesh.IndexBuffer.AsNativeArray(Allocator.TempJob); + disposables.Add(source); + jobHandle = new CopyIndicesJobs.Uint2Ushort( + (ushort)vertexOffset, + source, + new NativeSlice(indices, indexOffset, mesh.IndexBuffer.Count)) + .Schedule(mesh.IndexBuffer.Count, 1, jobHandle); + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + + vertexOffset += mesh.VertexBuffer.Count; + indexOffset += mesh.IndexBuffer.Count; + } + + jobHandle.Complete(); + + resultMesh.SetIndexBufferParams(indexCount, IndexFormat.UInt16); + resultMesh.SetIndexBufferData(indices, 0, 0, indexCount); + } + else + { + var indices = new NativeArray(indexCount, Allocator.TempJob); + disposables.Add(indices); + var indexOffset = 0; + var vertexOffset = 0; + foreach (var mesh in meshGroup.Meshes) + { + switch (mesh.IndexBuffer.ComponentType) + { + case AccessorValueType.SHORT: + { + // unsigned short -> unsigned int + var source = mesh.IndexBuffer.AsNativeArray(Allocator.TempJob); + disposables.Add(source); + jobHandle = new CopyIndicesJobs.Ushort2Uint( + (uint)vertexOffset, + source, + new NativeSlice(indices, indexOffset, mesh.IndexBuffer.Count)) + .Schedule(mesh.IndexBuffer.Count, 1, jobHandle); + break; + } + case AccessorValueType.UNSIGNED_INT: + { + // unsigned int -> unsigned int + var source = mesh.IndexBuffer.AsNativeArray(Allocator.TempJob); + disposables.Add(source); + jobHandle = new CopyIndicesJobs.UInt2UInt( + (uint)vertexOffset, + source, + new NativeSlice(indices, indexOffset, mesh.IndexBuffer.Count)) + .Schedule(mesh.IndexBuffer.Count, 1, jobHandle); + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + + vertexOffset += mesh.VertexBuffer.Count; + indexOffset += mesh.IndexBuffer.Count; + } + + jobHandle.Complete(); + + resultMesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32); + resultMesh.SetIndexBufferData(indices, 0, 0, indexCount); + } + + foreach (var disposable in disposables) + { + disposable.Dispose(); + } + + Profiler.EndSample(); + } + + /// + /// メッシュの頂点情報の更新を行う際、MainThreadが空くため、その間にBindPoseの更新も行う + /// + private static void UpdateVerticesAndBindPose( + MeshGroup meshGroup, + int vertexCount, + Mesh resultMesh) + { + Profiler.BeginSample("MeshImporterDivided.UpdateVerticesAndBindPose"); + + var disposables = new List(); + + // JobのSchedule + var vertices = new NativeArray(vertexCount, Allocator.TempJob); + disposables.Add(vertices); + + var indexOffset = 0; + JobHandle interleaveVertexJob = default; + + foreach (var mesh in meshGroup.Meshes) + { + var positions = mesh.VertexBuffer.Positions.AsNativeArray(Allocator.TempJob); + var normals = mesh.VertexBuffer.Normals.AsNativeArray(Allocator.TempJob); + var texCoords = mesh.VertexBuffer.TexCoords.AsNativeArray(Allocator.TempJob); + var weights = meshGroup.Skin != null + ? mesh.VertexBuffer.Weights.AsNativeArray(Allocator.TempJob) + : default; + var joints = meshGroup.Skin != null + ? mesh.VertexBuffer.Joints.AsNativeArray(Allocator.TempJob) + : default; + if (positions.IsCreated) disposables.Add(positions); + if (normals.IsCreated) disposables.Add(normals); + if (texCoords.IsCreated) disposables.Add(texCoords); + if (weights.IsCreated) disposables.Add(weights); + if (joints.IsCreated) disposables.Add(joints); + + interleaveVertexJob = new InterleaveMeshVerticesJob( + new NativeSlice(vertices, indexOffset, mesh.VertexBuffer.Count), + positions, + normals, + texCoords, + default, + weights, + joints) + .Schedule(mesh.VertexBuffer.Count, 1, interleaveVertexJob); + indexOffset += mesh.VertexBuffer.Count; + } + + JobHandle.ScheduleBatchedJobs(); + + // 並行してBindposeの更新を行う + if (meshGroup.Skin != null) + { + resultMesh.bindposes = meshGroup.Skin.InverseMatrices.GetSpan().ToArray(); + } + + // Jobを完了 + interleaveVertexJob.Complete(); + + // VertexBufferを設定 + MeshVertex.SetVertexBufferParamsToMesh(resultMesh, vertices.Length); + resultMesh.SetVertexBufferData(vertices, 0, 0, vertices.Length); + + // 各種バッファを破棄 + foreach (var disposable in disposables) + { + disposable.Dispose(); } Profiler.EndSample(); - - return dst; } } -} +} \ No newline at end of file diff --git a/Assets/VRM10/vrmlib/Runtime/BufferAccessor.cs b/Assets/VRM10/vrmlib/Runtime/BufferAccessor.cs index 6d5be079f..5ea2defb7 100644 --- a/Assets/VRM10/vrmlib/Runtime/BufferAccessor.cs +++ b/Assets/VRM10/vrmlib/Runtime/BufferAccessor.cs @@ -109,13 +109,8 @@ namespace VrmLib /// バッファをNativeArrayに変換して返す /// 開放の責務は使い手側にある点に注意 /// - public unsafe NativeArray AsNativeArray(Allocator allocator, bool checkStride = true) where T : struct + public unsafe NativeArray AsNativeArray(Allocator allocator) where T : struct { - if (checkStride && Marshal.SizeOf(typeof(T)) != Stride) - { - throw new Exception("different sizeof(T) with stride"); - } - fixed (byte* byteArray = Bytes.Array) { var nativeArray = new NativeArray(Bytes.Count / Marshal.SizeOf(), allocator);