From b59e1bfa62e88b8ea443be6a1440da62e709e1e2 Mon Sep 17 00:00:00 2001 From: ousttrue Date: Thu, 9 Jun 2022 15:12:42 +0900 Subject: [PATCH] =?UTF-8?q?MeshContext=20=E3=82=92=20MeshData=20=E3=81=A8?= =?UTF-8?q?=20MeshUploader=20=E3=81=AB=E8=B2=AC=E5=8B=99=E5=88=86=E9=9B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/UniGLTF/IO/ImporterContext.cs | 8 +- .../IO/MeshIO/{MeshContext.cs => MeshData.cs} | 413 ++++++------------ .../UniGLTF/IO/MeshIO/MeshData.cs.meta | 11 + .../Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs | 161 +++++++ ...shContext.cs.meta => MeshUploader.cs.meta} | 0 .../Runtime/IO/Model/MeshImporterShared.cs | 2 +- 6 files changed, 315 insertions(+), 280 deletions(-) rename Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/{MeshContext.cs => MeshData.cs} (75%) create mode 100644 Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs.meta create mode 100644 Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs rename Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/{MeshContext.cs.meta => MeshUploader.cs.meta} (100%) diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs index 8ff75af30..cba4f708c 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs @@ -174,8 +174,8 @@ namespace UniGLTF var index = i; using (MeasureTime("ReadMesh")) { - var meshContext = await awaitCaller.Run(() => MeshContext.CreateFromGltf(Data, index, inverter)); - var meshWithMaterials = await BuildMeshAsync(awaitCaller, MeasureTime, meshContext, index); + var meshData = await awaitCaller.Run(() => MeshData.CreateFromGltf(Data, index, inverter)); + var meshWithMaterials = await BuildMeshAsync(awaitCaller, MeasureTime, meshData, index); Meshes.Add(meshWithMaterials); } } @@ -289,11 +289,11 @@ namespace UniGLTF return Task.FromResult(null); } - async Task BuildMeshAsync(IAwaitCaller awaitCaller, Func MeasureTime, MeshContext meshContext, int i) + async Task BuildMeshAsync(IAwaitCaller awaitCaller, Func MeasureTime, MeshData meshData, int i) { using (MeasureTime("BuildMesh")) { - var meshWithMaterials = await meshContext.BuildMeshAsync(awaitCaller, MaterialFactory.GetMaterial); + var meshWithMaterials = await MeshUploader.BuildMeshAndUploadAsync(awaitCaller, meshData, MaterialFactory.GetMaterial); var mesh = meshWithMaterials.Mesh; // mesh name diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshContext.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs similarity index 75% rename from Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshContext.cs rename to Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs index 7794c92c2..89082b9ec 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshContext.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs @@ -1,30 +1,32 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Rendering; -using VRMShaders; namespace UniGLTF { - internal class MeshContext + internal class MeshData { - private const float FrameWeight = 100.0f; - private readonly List _vertices = new List(); + public List Vertices => _vertices; // IReadOnlyList だと mesh.SetVertexBufferData の overload 解決ができない private readonly List _skinnedMeshVertices = new List(); + public List SkinnedMeshVertices => _skinnedMeshVertices; // IReadOnlyList だと mesh.SetVertexBufferData の overload 解決ができない private readonly List _indices = new List(); + public List Indices => _indices; // IReadOnlyList だと mesh.SetIndexBufferData の overload 解決ができない private readonly List _subMeshes = new List(); + public IReadOnlyList SubMeshes => _subMeshes; private readonly List _materialIndices = new List(); + public IReadOnlyList MaterialIndices => _materialIndices; private readonly List _blendShapes = new List(); + public IReadOnlyList BlendShapes => _blendShapes; - private bool HasNormal { get; set; } = true; + public bool HasNormal { get; private set; } = true; - private string Name { get; } + public string Name { get; } - MeshContext(string name, int meshIndex) + MeshData(string name, int meshIndex) { if (string.IsNullOrEmpty(name)) { @@ -34,53 +36,61 @@ namespace UniGLTF Name = name; } + /// + /// バッファ共有方式の判定 + /// + /// * バッファ共用方式は VertexBuffer が同じでSubMeshの index buffer がスライドしていく方式 + /// * バッファがひとつのとき + /// * すべての primitive の attribute が 同一の accessor を使用している時 + /// + /// private static bool HasSharedVertexBuffer(glTFMesh gltfMesh) { glTFAttributes lastAttributes = null; - var sharedAttributes = true; foreach (var prim in gltfMesh.primitives) { if (lastAttributes != null && !prim.attributes.Equals(lastAttributes)) { - sharedAttributes = false; - break; + return false; } - lastAttributes = prim.attributes; } - - return sharedAttributes; + return true; } - public static MeshContext CreateFromGltf(GltfData data, int meshIndex, IAxisInverter inverter) + /// + /// glTF から 頂点バッファと index バッファ、BlendShape を蓄える。 + /// 右手系と左手系の反転(ZもしくはX軸の反転)も実行する。 + /// + public static MeshData CreateFromGltf(GltfData data, int meshIndex, IAxisInverter inverter) { - Profiler.BeginSample("ReadMesh"); + Profiler.BeginSample("MeshData.CreateFromGltf"); var gltfMesh = data.GLTF.meshes[meshIndex]; - var meshContext = new MeshContext(gltfMesh.name, meshIndex); + var meshData = new MeshData(gltfMesh.name, meshIndex); if (HasSharedVertexBuffer(gltfMesh)) { - meshContext.ImportMeshSharingVertexBuffer(data, gltfMesh, inverter); + meshData.ImportMeshSharingVertexBuffer(data, gltfMesh, inverter); } else { - meshContext.ImportMeshIndependentVertexBuffer(data, gltfMesh, inverter); + meshData.ImportMeshIndependentVertexBuffer(data, gltfMesh, inverter); } - meshContext.RenameBlendShape(gltfMesh); + meshData.RenameBlendShape(gltfMesh); - meshContext.DropUnusedVertices(); + meshData.DropUnusedVertices(); + + meshData.AddDefaultMaterial(); Profiler.EndSample(); - return meshContext; + return meshData; } /// /// * flip triangle /// * add submesh offset /// - /// - /// private void PushIndices(BufferAccessor src, int offset) { switch (src.ComponentType) @@ -127,85 +137,6 @@ namespace UniGLTF } } - /// - /// 頂点情報をMeshに対して送る - /// - /// - private void UploadMeshVertices(Mesh mesh) - { - var vertexAttributeDescriptor = MeshVertex.GetVertexAttributeDescriptor(); - - // Weight情報等は存在しないパターンがあり、かつこの存在の有無によって内部的に条件分岐が走ってしまうため、 - // Streamを分けて必要に応じてアップロードする - if (_skinnedMeshVertices.Count > 0) - { - vertexAttributeDescriptor = vertexAttributeDescriptor.Concat(SkinnedMeshVertex - .GetVertexAttributeDescriptor().Select( - attr => - { - attr.stream = 1; - return attr; - })).ToArray(); - } - - mesh.SetVertexBufferParams(_vertices.Count, vertexAttributeDescriptor); - - mesh.SetVertexBufferData(_vertices, 0, 0, _vertices.Count); - if (_skinnedMeshVertices.Count > 0) - { - mesh.SetVertexBufferData(_skinnedMeshVertices, 0, 0, _skinnedMeshVertices.Count, 1); - } - } - - /// - /// インデックス情報をMeshに対して送る - /// - /// - private void UploadMeshIndices(Mesh mesh) - { - mesh.SetIndexBufferParams(_indices.Count, IndexFormat.UInt32); - mesh.SetIndexBufferData(_indices, 0, 0, _indices.Count); - mesh.subMeshCount = _subMeshes.Count; - for (var i = 0; i < _subMeshes.Count; i++) - { - mesh.SetSubMesh(i, _subMeshes[i]); - } - } - - private BlendShape GetOrCreateBlendShape(int i) - { - if (i < _blendShapes.Count && _blendShapes[i] != null) - { - return _blendShapes[i]; - } - - while (_blendShapes.Count <= i) - { - _blendShapes.Add(null); - } - - var blendShape = new BlendShape(i.ToString()); - _blendShapes[i] = blendShape; - return blendShape; - } - - private static (float x, float y, float z, float w) NormalizeBoneWeight( - (float x, float y, float z, float w) src) - { - var sum = src.x + src.y + src.z + src.w; - if (sum == 0) - { - return src; - } - - var f = 1.0f / sum; - src.x *= f; - src.y *= f; - src.z *= f; - src.w *= f; - return src; - } - (int VertexCapacity, int IndexCapacity) GetCapacity(GltfData data, glTFMesh gltfMesh) { var vertexCount = 0; @@ -228,6 +159,109 @@ namespace UniGLTF return (vertexCount, indexCount); } + /// + /// 各種頂点属性が使われているかどうかをチェックし、使われていなかったらフラグを切る + /// MEMO: O(1)で検知する手段がありそう + /// + private void CheckAttributeUsages(glTFPrimitives primitives) + { + if (!primitives.HasNormal()) HasNormal = false; + } + + private static (float x, float y, float z, float w) NormalizeBoneWeight( + (float x, float y, float z, float w) src) + { + var sum = src.x + src.y + src.z + src.w; + if (sum == 0) + { + return src; + } + + var f = 1.0f / sum; + src.x *= f; + src.y *= f; + src.z *= f; + src.w *= f; + return src; + } + + private BlendShape GetOrCreateBlendShape(int i) + { + if (i < _blendShapes.Count && _blendShapes[i] != null) + { + return _blendShapes[i]; + } + + while (_blendShapes.Count <= i) + { + _blendShapes.Add(null); + } + + var blendShape = new BlendShape(i.ToString()); + _blendShapes[i] = blendShape; + return blendShape; + } + + private void RenameBlendShape(glTFMesh gltfMesh) + { + if (!gltf_mesh_extras_targetNames.TryGet(gltfMesh, out var targetNames)) return; + for (var i = 0; i < _blendShapes.Count; i++) + { + if (i >= targetNames.Count) + { + Debug.LogWarning($"invalid primitive.extras.targetNames length"); + break; + } + + _blendShapes[i].Name = targetNames[i]; + } + } + + /// + /// https://github.com/vrm-c/UniVRM/issues/610 + /// + /// VertexBuffer の後ろに未使用頂点がある場合に削除する + /// + private void DropUnusedVertices() + { + Profiler.BeginSample("MeshData.DropUnusedVertices"); + var maxIndex = _indices.Max(); + Truncate(_vertices, maxIndex); + Truncate(_skinnedMeshVertices, maxIndex); + foreach (var blendShape in _blendShapes) + { + Truncate(blendShape.Positions, maxIndex); + Truncate(blendShape.Normals, maxIndex); + Truncate(blendShape.Tangents, maxIndex); + } + + Profiler.EndSample(); + } + + private static void Truncate(List list, int maxIndex) + { + if (list == null) + { + return; + } + + var count = maxIndex + 1; + if (list.Count > count) + { + // Debug.LogWarning($"remove {count} to {list.Count}"); + list.RemoveRange(count, list.Count - count); + } + } + + private void AddDefaultMaterial() + { + if (!_materialIndices.Any()) + { + // add default material + _materialIndices.Add(0); + } + } + /// /// 各 primitive の attribute の要素が同じでない。=> uv が有るものと無いものが混在するなど /// glTF 的にはありうる。 @@ -369,15 +403,6 @@ namespace UniGLTF } } - /// - /// 各種頂点属性が使われているかどうかをチェックし、使われていなかったらフラグを切る - /// MEMO: O(1)で検知する手段がありそう - /// - private void CheckAttributeUsages(glTFPrimitives primitives) - { - if (!primitives.HasNormal()) HasNormal = false; - } - /// /// /// 各primitiveが同じ attribute を共有している場合専用のローダー。 @@ -519,167 +544,5 @@ namespace UniGLTF _materialIndices.Add(primitive.material); } } - - private void RenameBlendShape(glTFMesh gltfMesh) - { - if (!gltf_mesh_extras_targetNames.TryGet(gltfMesh, out var targetNames)) return; - for (var i = 0; i < _blendShapes.Count; i++) - { - if (i >= targetNames.Count) - { - Debug.LogWarning($"invalid primitive.extras.targetNames length"); - break; - } - - _blendShapes[i].Name = targetNames[i]; - } - } - - private static void Truncate(List list, int maxIndex) - { - if (list == null) - { - return; - } - - var count = maxIndex + 1; - if (list.Count > count) - { - // Debug.LogWarning($"remove {count} to {list.Count}"); - list.RemoveRange(count, list.Count - count); - } - } - - private void AddDefaultMaterial() - { - if (!_materialIndices.Any()) - { - // add default material - _materialIndices.Add(0); - } - } - - /// - /// https://github.com/vrm-c/UniVRM/issues/610 - /// - /// VertexBuffer の後ろに未使用頂点がある場合に削除する - /// - private void DropUnusedVertices() - { - Profiler.BeginSample("MeshContext.DropUnusedVertices"); - var maxIndex = _indices.Max(); - Truncate(_vertices, maxIndex); - Truncate(_skinnedMeshVertices, maxIndex); - foreach (var blendShape in _blendShapes) - { - Truncate(blendShape.Positions, maxIndex); - Truncate(blendShape.Normals, maxIndex); - Truncate(blendShape.Tangents, maxIndex); - } - - Profiler.EndSample(); - } - - private static async Task BuildBlendShapeAsync(IAwaitCaller awaitCaller, Mesh mesh, BlendShape blendShape, - Vector3[] emptyVertices) - { - Vector3[] positions = null; - Vector3[] normals = null; - await awaitCaller.Run(() => - { - positions = blendShape.Positions.ToArray(); - if (blendShape.Normals != null) - { - normals = blendShape.Normals.ToArray(); - } - }); - - Profiler.BeginSample("MeshImporter.BuildBlendShapeAsync"); - if (blendShape.Positions.Count > 0) - { - if (blendShape.Positions.Count == mesh.vertexCount) - { - mesh.AddBlendShapeFrame(blendShape.Name, FrameWeight, - blendShape.Positions.ToArray(), - normals.Length == mesh.vertexCount && normals.Length == positions.Length ? normals : null, - null - ); - } - else - { - Debug.LogWarningFormat( - "May be partial primitive has blendShape. Require separate mesh or extend blend shape, but not implemented: {0}", - blendShape.Name); - } - } - else - { - // Debug.LogFormat("empty blendshape: {0}.{1}", mesh.name, blendShape.Name); - // add empty blend shape for keep blend shape index - mesh.AddBlendShapeFrame(blendShape.Name, FrameWeight, - emptyVertices, - null, - null - ); - } - - Profiler.EndSample(); - } - - public async Task BuildMeshAsync( - IAwaitCaller awaitCaller, - Func materialFromIndex) - { - Profiler.BeginSample("MeshImporter.BuildMesh"); - AddDefaultMaterial(); - - //Debug.Log(prims.ToJson()); - var mesh = new Mesh - { - name = Name - }; - - UploadMeshVertices(mesh); - await awaitCaller.NextFrame(); - - UploadMeshIndices(mesh); - await awaitCaller.NextFrame(); - - // NOTE: mesh.vertices では自動的に行われていたが、SetVertexBuffer では行われないため、明示的に呼び出す. - mesh.RecalculateBounds(); - await awaitCaller.NextFrame(); - - if (!HasNormal) - { - mesh.RecalculateNormals(); - await awaitCaller.NextFrame(); - } - - mesh.RecalculateTangents(); - await awaitCaller.NextFrame(); - - var result = new MeshWithMaterials - { - Mesh = mesh, - Materials = _materialIndices.Select(materialFromIndex).ToArray() - }; - await awaitCaller.NextFrame(); - - if (_blendShapes.Count > 0) - { - var emptyVertices = new Vector3[mesh.vertexCount]; - foreach (var blendShape in _blendShapes) - { - await BuildBlendShapeAsync(awaitCaller, mesh, blendShape, emptyVertices); - } - } - Profiler.EndSample(); - - Profiler.BeginSample("Mesh.UploadMeshData"); - mesh.UploadMeshData(false); - Profiler.EndSample(); - - return result; - } } -} +} \ No newline at end of file diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs.meta new file mode 100644 index 000000000..800ac75fa --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4ef93605dcfe9014fbf96a3b681795ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs new file mode 100644 index 000000000..541f6fd6d --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs @@ -0,0 +1,161 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Profiling; +using UnityEngine.Rendering; +using VRMShaders; + +namespace UniGLTF +{ + internal static class MeshUploader + { + private const float FrameWeight = 100.0f; + + /// + /// 頂点情報をMeshに対して送る + /// + public static void UploadMeshVertices(MeshData data, Mesh mesh) + { + var vertexAttributeDescriptor = MeshVertex.GetVertexAttributeDescriptor(); + + // Weight情報等は存在しないパターンがあり、かつこの存在の有無によって内部的に条件分岐が走ってしまうため、 + // Streamを分けて必要に応じてアップロードする + if (data.SkinnedMeshVertices.Count > 0) + { + vertexAttributeDescriptor = vertexAttributeDescriptor.Concat(SkinnedMeshVertex + .GetVertexAttributeDescriptor().Select( + attr => + { + attr.stream = 1; + return attr; + })).ToArray(); + } + + mesh.SetVertexBufferParams(data.Vertices.Count, vertexAttributeDescriptor); + + mesh.SetVertexBufferData(data.Vertices, 0, 0, data.Vertices.Count); + if (data.SkinnedMeshVertices.Count > 0) + { + mesh.SetVertexBufferData(data.SkinnedMeshVertices, 0, 0, data.SkinnedMeshVertices.Count, 1); + } + } + + /// + /// インデックス情報をMeshに対して送る + /// + /// + private static void UploadMeshIndices(MeshData data, Mesh mesh) + { + mesh.SetIndexBufferParams(data.Indices.Count, IndexFormat.UInt32); + mesh.SetIndexBufferData(data.Indices, 0, 0, data.Indices.Count); + mesh.subMeshCount = data.SubMeshes.Count; + for (var i = 0; i < data.SubMeshes.Count; i++) + { + mesh.SetSubMesh(i, data.SubMeshes[i]); + } + } + + private static async Task BuildBlendShapeAsync(IAwaitCaller awaitCaller, Mesh mesh, BlendShape blendShape, + Vector3[] emptyVertices) + { + Vector3[] positions = null; + Vector3[] normals = null; + await awaitCaller.Run(() => + { + positions = blendShape.Positions.ToArray(); + if (blendShape.Normals != null) + { + normals = blendShape.Normals.ToArray(); + } + }); + + Profiler.BeginSample("MeshUploader.BuildBlendShapeAsync"); + if (blendShape.Positions.Count > 0) + { + if (blendShape.Positions.Count == mesh.vertexCount) + { + mesh.AddBlendShapeFrame(blendShape.Name, FrameWeight, + blendShape.Positions.ToArray(), + normals.Length == mesh.vertexCount && normals.Length == positions.Length ? normals : null, + null + ); + } + else + { + Debug.LogWarningFormat( + "May be partial primitive has blendShape. Require separate mesh or extend blend shape, but not implemented: {0}", + blendShape.Name); + } + } + else + { + // Debug.LogFormat("empty blendshape: {0}.{1}", mesh.name, blendShape.Name); + // add empty blend shape for keep blend shape index + mesh.AddBlendShapeFrame(blendShape.Name, FrameWeight, + emptyVertices, + null, + null + ); + } + + Profiler.EndSample(); + } + + public static async Task BuildMeshAndUploadAsync( + IAwaitCaller awaitCaller, + MeshData data, + Func materialFromIndex) + { + Profiler.BeginSample("MeshUploader.BuildMesh"); + + //Debug.Log(prims.ToJson()); + var mesh = new Mesh + { + name = data.Name + }; + + UploadMeshVertices(data, mesh); + await awaitCaller.NextFrame(); + + UploadMeshIndices(data, mesh); + await awaitCaller.NextFrame(); + + // NOTE: mesh.vertices では自動的に行われていたが、SetVertexBuffer では行われないため、明示的に呼び出す. + mesh.RecalculateBounds(); + await awaitCaller.NextFrame(); + + if (!data.HasNormal) + { + mesh.RecalculateNormals(); + await awaitCaller.NextFrame(); + } + + mesh.RecalculateTangents(); + await awaitCaller.NextFrame(); + + var result = new MeshWithMaterials + { + Mesh = mesh, + Materials = data.MaterialIndices.Select(materialFromIndex).ToArray() + }; + await awaitCaller.NextFrame(); + + if (data.BlendShapes.Count > 0) + { + var emptyVertices = new Vector3[mesh.vertexCount]; + foreach (var blendShape in data.BlendShapes) + { + await BuildBlendShapeAsync(awaitCaller, mesh, blendShape, emptyVertices); + } + } + Profiler.EndSample(); + + Profiler.BeginSample("Mesh.UploadMeshData"); + mesh.UploadMeshData(false); + Profiler.EndSample(); + + return result; + } + } +} diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshContext.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs.meta similarity index 100% rename from Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshContext.cs.meta rename to Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs.meta diff --git a/Assets/VRM10/Runtime/IO/Model/MeshImporterShared.cs b/Assets/VRM10/Runtime/IO/Model/MeshImporterShared.cs index ff63f1537..246c0baf2 100644 --- a/Assets/VRM10/Runtime/IO/Model/MeshImporterShared.cs +++ b/Assets/VRM10/Runtime/IO/Model/MeshImporterShared.cs @@ -20,7 +20,7 @@ namespace UniVRM10 /// public static Mesh LoadSharedMesh(VrmLib.Mesh src, Skin skin = null) { - Profiler.BeginSample("MeshImporter.LoadSharedMesh"); + Profiler.BeginSample("MeshImporterShared.LoadSharedMesh"); var mesh = new Mesh(); var positions = src.VertexBuffer.Positions.Bytes.Reinterpret(1);