diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/SkinJoint.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/SkinJoint.cs index 6e38e6642..5c2ec357f 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/SkinJoint.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/SkinJoint.cs @@ -2,6 +2,7 @@ using System; namespace UniGLTF { + [Serializable] public struct SkinJoints : IEquatable { public ushort Joint0; diff --git a/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs b/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs new file mode 100644 index 000000000..ac77288cf --- /dev/null +++ b/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs @@ -0,0 +1,65 @@ +using UniGLTF; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using UnityEngine; + +#if ENABLE_VRM10_BURST +using Unity.Burst; +#endif + +namespace UniVRM10 +{ + #if ENABLE_VRM10_BURST + [BurstCompile] + #endif + internal struct InterleaveMeshVerticesJob : IJobParallelFor + { + [WriteOnly] private NativeArray _vertices; + + [ReadOnly] private readonly NativeArray _positions; + + // default値を許容する + [ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray _normals; + [ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray _texCoords; + [ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray _colors; + [ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray _weights; + [ReadOnly, NativeDisableContainerSafetyRestriction] private readonly NativeArray _joints; + + public InterleaveMeshVerticesJob( + NativeArray vertices, + NativeArray positions, + NativeArray normals = default, + NativeArray texCoords = default, + NativeArray colors = default, + NativeArray weights = default, + NativeArray joints = default) + { + _vertices = vertices; + _positions = positions; + _normals = normals; + _texCoords = texCoords; + _colors = colors; + _weights = weights; + _joints = joints; + } + + public void Execute(int index) + { + _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 + ); + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs.meta b/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs.meta new file mode 100644 index 000000000..6a54c7ebc --- /dev/null +++ b/Assets/VRM10/Runtime/IO/Model/InterleaveMeshVerticesJob.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 01d2fcab0a3941fa9cf2e4f1a641105b +timeCreated: 1636688648 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/IO/Model/MeshImporter.cs b/Assets/VRM10/Runtime/IO/Model/MeshImporter.cs index 3e0c382e2..13a98092a 100644 --- a/Assets/VRM10/Runtime/IO/Model/MeshImporter.cs +++ b/Assets/VRM10/Runtime/IO/Model/MeshImporter.cs @@ -1,6 +1,9 @@ using System; using UniGLTF; +using Unity.Collections; +using Unity.Jobs; using UnityEngine; +using UnityEngine.Profiling; namespace UniVRM10 { @@ -12,55 +15,63 @@ namespace UniVRM10 /// /// /// - public static UnityEngine.Mesh LoadSharedMesh(VrmLib.Mesh src, VrmLib.Skin skin = null) + public static Mesh LoadSharedMesh(VrmLib.Mesh src, VrmLib.Skin skin = null) { - // submesh 方式 - var mesh = new UnityEngine.Mesh(); - if (src.IndexBuffer.Count > UInt16.MaxValue) + Profiler.BeginSample("MeshImporter.LoadSharedMesh"); + var mesh = new Mesh(); + if (src.IndexBuffer.Count > ushort.MaxValue) { mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; } - mesh.vertices = src.VertexBuffer.Positions.GetSpan().ToArray(); - mesh.normals = src.VertexBuffer.Normals?.GetSpan().ToArray(); - mesh.uv = src.VertexBuffer.TexCoords?.GetSpan().ToArray(); - mesh.colors = src.VertexBuffer.Colors?.GetSpan().ToArray(); - if (src.VertexBuffer.Weights != null && src.VertexBuffer.Joints != null) + var positions = src.VertexBuffer.Positions.GetNativeArray(Allocator.TempJob); + var normals = src.VertexBuffer.Normals?.GetNativeArray(Allocator.TempJob) ?? default; + var texCoords = src.VertexBuffer.TexCoords?.GetNativeArray(Allocator.TempJob) ?? default; + var colors = src.VertexBuffer.Colors?.GetNativeArray(Allocator.TempJob) ?? default; + var weights = src.VertexBuffer.Weights?.GetNativeArray(Allocator.TempJob) ?? default; + var joints = src.VertexBuffer.Joints?.GetNativeArray(Allocator.TempJob) ?? default; + + var vertices = new NativeArray(positions.Length, Allocator.TempJob); + + // JobとBindPoseの更新を並行して行う + var jobHandle = + new InterleaveMeshVerticesJob(vertices, positions, normals, texCoords, colors, weights, joints) + .Schedule(vertices.Length, 1); + + if (weights.IsCreated && joints.IsCreated) { - var boneWeights = new BoneWeight[mesh.vertexCount]; - if (src.VertexBuffer.Weights.Count != mesh.vertexCount || src.VertexBuffer.Joints.Count != mesh.vertexCount) + if (weights.Length != positions.Length || joints.Length != positions.Length) { throw new ArgumentException(); } - var weights = src.VertexBuffer.Weights.GetSpan(); - var joints = src.VertexBuffer.Joints.GetSpan(); if (skin != null) { mesh.bindposes = skin.InverseMatrices.GetSpan().ToArray(); } - - for (int i = 0; i < weights.Length; ++i) - { - var w = weights[i]; - boneWeights[i].weight0 = w.x; - boneWeights[i].weight1 = w.y; - boneWeights[i].weight2 = w.z; - boneWeights[i].weight3 = w.w; - } - for (int i = 0; i < joints.Length; ++i) - { - var j = joints[i]; - boneWeights[i].boneIndex0 = j.Joint0; - boneWeights[i].boneIndex1 = j.Joint1; - boneWeights[i].boneIndex2 = j.Joint2; - boneWeights[i].boneIndex3 = j.Joint3; - } - mesh.boneWeights = boneWeights; } + // Jobを完了 + jobHandle.Complete(); + + // 入力のNativeArrayを開放 + positions.Dispose(); + if (normals.IsCreated) normals.Dispose(); + if (texCoords.IsCreated) texCoords.Dispose(); + if (colors.IsCreated) colors.Dispose(); + if (weights.IsCreated) weights.Dispose(); + if (joints.IsCreated) joints.Dispose(); + + // 頂点を更新 + MeshVertex.SetVertexBufferParamsToMesh(mesh, vertices.Length); + mesh.SetVertexBufferData(vertices, 0, 0, vertices.Length); + + // 出力のNativeArrayを開放 + vertices.Dispose(); + + // submesh 方式 mesh.subMeshCount = src.Submeshes.Count; var triangles = src.IndexBuffer.GetAsIntList(); - for (int i = 0; i < src.Submeshes.Count; ++i) + for (var i = 0; i < src.Submeshes.Count; ++i) { var submesh = src.Submeshes[i]; mesh.SetTriangles(triangles.GetRange(submesh.Offset, submesh.DrawCount), i); @@ -68,16 +79,18 @@ namespace UniVRM10 foreach (var morphTarget in src.MorphTargets) { - var positions = + var morphTargetPositions = morphTarget.VertexBuffer.Positions != null ? morphTarget.VertexBuffer.Positions.GetSpan().ToArray() : new Vector3[mesh.vertexCount] // dummy ; - mesh.AddBlendShapeFrame(morphTarget.Name, 100.0f, positions, null, null); + mesh.AddBlendShapeFrame(morphTarget.Name, 100.0f, morphTargetPositions, null, null); } mesh.RecalculateBounds(); mesh.RecalculateTangents(); + + Profiler.EndSample(); return mesh; } diff --git a/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs b/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs index b2b108139..4f489e0ac 100644 --- a/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs +++ b/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using UniGLTF; using UnityEngine; +using UnityEngine.Profiling; +using Mesh = VrmLib.Mesh; namespace UniVRM10 { @@ -9,6 +11,8 @@ namespace UniVRM10 { public static UnityEngine.Mesh LoadDivided(VrmLib.MeshGroup src) { + Profiler.BeginSample("MeshImporterDivided.LoadDivided"); + var dst = new UnityEngine.Mesh(); if (src.Meshes.Sum(x => x.IndexBuffer.Count) > ushort.MaxValue) { @@ -23,32 +27,30 @@ namespace UniVRM10 var normals = new List(vertexCount); var uv = new List(vertexCount); var boneWeights = new List(vertexCount); - for (int meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex) + + foreach (var mesh in src.Meshes) { - var mesh = src.Meshes[meshIndex]; positions.AddRange(mesh.VertexBuffer.Positions.GetSpan()); normals.AddRange(mesh.VertexBuffer.Normals.GetSpan()); uv.AddRange(mesh.VertexBuffer.TexCoords.GetSpan()); - if (src.Skin != null) + if (src.Skin == null) continue; + var joints = mesh.VertexBuffer.Joints.GetSpan(); + var weights = mesh.VertexBuffer.Weights.GetSpan(); + for (var i = 0; i < mesh.VertexBuffer.Count; ++i) { - var j = mesh.VertexBuffer.Joints.GetSpan(); - var w = mesh.VertexBuffer.Weights.GetSpan(); - for (int i = 0; i < mesh.VertexBuffer.Count; ++i) + var joint = joints[i]; + var weight = weights[i]; + boneWeights.Add(new BoneWeight { - var jj = j[i]; - var ww = w[i]; - boneWeights.Add(new BoneWeight - { - boneIndex0 = jj.Joint0, - boneIndex1 = jj.Joint1, - boneIndex2 = jj.Joint2, - boneIndex3 = jj.Joint3, - weight0 = ww.x, - weight1 = ww.y, - weight2 = ww.z, - weight3 = ww.w, - }); - } + boneIndex0 = joint.Joint0, + boneIndex1 = joint.Joint1, + boneIndex2 = joint.Joint2, + boneIndex3 = joint.Joint3, + weight0 = weight.x, + weight1 = weight.y, + weight2 = weight.z, + weight3 = weight.w, + }); } } @@ -74,7 +76,7 @@ namespace UniVRM10 // dst.subMeshCount = src.Meshes.Count; var offset = 0; - for (int meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex) + for (var meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex) { var mesh = src.Meshes[meshIndex]; var indices = mesh.IndexBuffer.GetAsIntArray().Select(x => offset + x).ToArray(); @@ -89,12 +91,12 @@ namespace UniVRM10 // blendshape // var blendShapeCount = src.Meshes[0].MorphTargets.Count; - for (int i = 0; i < blendShapeCount; ++i) + for (var i = 0; i < blendShapeCount; ++i) { positions.Clear(); normals.Clear(); var name = src.Meshes[0].MorphTargets[i].Name; - for (int meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex) + for (var meshIndex = 0; meshIndex < src.Meshes.Count; ++meshIndex) { var morphTarget = src.Meshes[meshIndex].MorphTargets[i]; positions.AddRange(morphTarget.VertexBuffer.Positions.GetSpan()); @@ -111,6 +113,8 @@ namespace UniVRM10 dst.AddBlendShapeFrame(name, 100.0f, positions.ToArray(), normals.ToArray(), null); } + Profiler.EndSample(); + return dst; } } diff --git a/Assets/VRM10/Runtime/IO/Model/MeshVertex.cs b/Assets/VRM10/Runtime/IO/Model/MeshVertex.cs new file mode 100644 index 000000000..d5bdd34d5 --- /dev/null +++ b/Assets/VRM10/Runtime/IO/Model/MeshVertex.cs @@ -0,0 +1,70 @@ +using System.Runtime.InteropServices; +using UnityEditor.UI; +using UnityEngine; +using UnityEngine.Rendering; + +namespace UniVRM10 +{ + /// + /// インターリーブされたメッシュの頂点情報を表す構造体 + /// そのままGPUにアップロードされる + /// + [StructLayout(LayoutKind.Sequential)] + internal readonly struct MeshVertex + { + private readonly Vector3 _position; + private readonly Vector3 _normal; + private readonly Color _color; + private readonly Vector2 _texCoord; + private readonly float _boneWeight0; + private readonly float _boneWeight1; + private readonly float _boneWeight2; + private readonly float _boneWeight3; + private readonly ushort _boneIndex0; + private readonly ushort _boneIndex1; + private readonly ushort _boneIndex2; + private readonly ushort _boneIndex3; + + public MeshVertex( + Vector3 position, + Vector3 normal, + Vector2 texCoord, + Color color, + ushort boneIndex0, + ushort boneIndex1, + ushort boneIndex2, + ushort boneIndex3, + float boneWeight0, + float boneWeight1, + float boneWeight2, + float boneWeight3) + { + _position = position; + _normal = normal; + _texCoord = texCoord; + _color = color; + _boneIndex0 = boneIndex0; + _boneIndex1 = boneIndex1; + _boneIndex2 = boneIndex2; + _boneIndex3 = boneIndex3; + _boneWeight0 = boneWeight0; + _boneWeight1 = boneWeight1; + _boneWeight2 = boneWeight2; + _boneWeight3 = boneWeight3; + } + + private static readonly VertexAttributeDescriptor[] vertexAttributeDescriptor = { + new VertexAttributeDescriptor(VertexAttribute.Position), + new VertexAttributeDescriptor(VertexAttribute.Normal), + new VertexAttributeDescriptor(VertexAttribute.Color, dimension: 4), + new VertexAttributeDescriptor(VertexAttribute.TexCoord0, dimension: 2), + new VertexAttributeDescriptor(VertexAttribute.BlendWeight, dimension: 4), + new VertexAttributeDescriptor(VertexAttribute.BlendIndices, VertexAttributeFormat.UInt16, 4), + }; + + public static void SetVertexBufferParamsToMesh(Mesh mesh, int length) + { + mesh.SetVertexBufferParams(length, vertexAttributeDescriptor); + } + } +} \ No newline at end of file diff --git a/Assets/VRM10/Runtime/IO/Model/MeshVertex.cs.meta b/Assets/VRM10/Runtime/IO/Model/MeshVertex.cs.meta new file mode 100644 index 000000000..0e80ae0bd --- /dev/null +++ b/Assets/VRM10/Runtime/IO/Model/MeshVertex.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 78c70746c10e46bc97de0a7afedb23c7 +timeCreated: 1636625951 \ No newline at end of file diff --git a/Assets/VRM10/Runtime/VRM10.asmdef b/Assets/VRM10/Runtime/VRM10.asmdef index b65ef4c5a..e8a38f04c 100644 --- a/Assets/VRM10/Runtime/VRM10.asmdef +++ b/Assets/VRM10/Runtime/VRM10.asmdef @@ -8,7 +8,8 @@ "GUID:bce005214fa49654d93927908c15b1f2", "GUID:0aaf403bd13871a44b7127aef2695ff8", "GUID:b7aa47b240b57de44a4b2021c143c9bf", - "GUID:f2ca1407928ebdc4bbe7765cc278be44" + "GUID:f2ca1407928ebdc4bbe7765cc278be44", + "GUID:2665a8d13d1b3f18800f46e256720795" ], "includePlatforms": [], "excludePlatforms": [], @@ -17,6 +18,12 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [], + "versionDefines": [ + { + "name": "com.unity.burst", + "expression": "0.0.1", + "define": "ENABLE_VRM10_BURST" + } + ], "noEngineReferences": false } \ No newline at end of file diff --git a/Assets/VRM10/vrmlib/Runtime/BufferAccessor.cs b/Assets/VRM10/vrmlib/Runtime/BufferAccessor.cs index 0d6336e7e..90b6e5405 100644 --- a/Assets/VRM10/vrmlib/Runtime/BufferAccessor.cs +++ b/Assets/VRM10/vrmlib/Runtime/BufferAccessor.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Numerics; using System.Runtime.InteropServices; using UniGLTF; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; namespace VrmLib { @@ -103,6 +105,25 @@ namespace VrmLib return SpanLike.Wrap(Bytes); } + /// + /// バッファをNativeArrayに変換して返す + /// 開放の責務は使い手側にある点に注意 + /// + public unsafe NativeArray GetNativeArray(Allocator allocator, bool checkStride = true) 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); + UnsafeUtility.MemCpy(nativeArray.GetUnsafePtr(), byteArray + Bytes.Offset, Bytes.Count); + return nativeArray; + } + } + public void Assign(T[] values) where T : struct { if (Marshal.SizeOf(typeof(T)) != Stride) diff --git a/Assets/VRM10/vrmlib/Runtime/VrmLib.asmdef b/Assets/VRM10/vrmlib/Runtime/VrmLib.asmdef index d3ffc1a7f..0e41a1f25 100644 --- a/Assets/VRM10/vrmlib/Runtime/VrmLib.asmdef +++ b/Assets/VRM10/vrmlib/Runtime/VrmLib.asmdef @@ -5,7 +5,7 @@ ], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": false,