diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeExporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeExporter.cs new file mode 100644 index 000000000..563b9d2e4 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeExporter.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace UniGLTF +{ + public static class BlendShapeExporter + { + public static gltfMorphTarget Export(glTF gltf, int gltfBuffer, Vector3[] positions, Vector3[] normals, bool useSparse) + { + var accessorCount = positions.Length; + if (normals != null && positions.Length != normals.Length) + { + throw new Exception(); + } + + int[] sparseIndices = default; + if (useSparse) + { + sparseIndices = Enumerable.Range(0, positions.Length).Where(x => positions[x] != Vector3.zero).ToArray(); + if (sparseIndices.Length == 0) + { + // sparse 対象がすべて [0, 0, 0] の場合 + // new glTFSparse + // { + // count = 0, + // } + // のようになる。 + // たぶん、仕様的にはあり。 + // 解釈できない場合あり。 + useSparse = false; + } + } + + if (useSparse) + { + if (sparseIndices == null) + { + throw new Exception(); + } + + // positions + var positionAccessorIndex = -1; + if (sparseIndices.Length > 0) + { + Debug.LogFormat("Sparse {0}/{1}", sparseIndices.Length, positions.Length); + var sparseIndicesViewIndex = gltf.ExtendBufferAndGetViewIndex(gltfBuffer, sparseIndices); + positionAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(gltfBuffer, accessorCount, positions, sparseIndices, sparseIndicesViewIndex, glBufferTarget.NONE); + } + + // normals + var normalAccessorIndex = -1; + if (normals != null) + { + var sparseNormalIndices = Enumerable.Range(0, positions.Length).Where(x => normals[x] != Vector3.zero).ToArray(); + if (sparseNormalIndices.Length > 0) + { + var sparseNormalIndicesViewIndex = gltf.ExtendBufferAndGetViewIndex(gltfBuffer, sparseNormalIndices); + normalAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(gltfBuffer, accessorCount, normals, sparseNormalIndices, sparseNormalIndicesViewIndex, glBufferTarget.NONE); + } + } + + return new gltfMorphTarget + { + POSITION = positionAccessorIndex, + NORMAL = normalAccessorIndex, + }; + } + else + { + // position + var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(gltfBuffer, positions, glBufferTarget.ARRAY_BUFFER); + gltf.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray(); + gltf.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray(); + + // normal + var normalAccessorIndex = -1; + if (normals != null) + { + normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(gltfBuffer, normals, glBufferTarget.ARRAY_BUFFER); + } + + return new gltfMorphTarget + { + POSITION = positionAccessorIndex, + NORMAL = normalAccessorIndex, + }; + } + } + } +} diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeExporter.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeExporter.cs.meta new file mode 100644 index 000000000..b407c6d75 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/BlendShapeExporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49e5f06c3492116409bc0c0745b5edf7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportUtil.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportUtil.cs index 19bd410da..9d3a7eac1 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportUtil.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExportUtil.cs @@ -15,37 +15,27 @@ namespace UniGLTF { public class BlendShapeBuffer { - readonly List m_positions; - readonly List m_normals; + readonly Vector3[] m_positions; + readonly Vector3[] m_normals; public BlendShapeBuffer(int reserve) { - m_positions = new List(reserve); - m_normals = new List(reserve); + m_positions = new Vector3[reserve]; + m_normals = new Vector3[reserve]; } - public void Push(Vector3 position, Vector3 normal) + public void Set(int index, Vector3 position, Vector3 normal) { - m_positions.Add(position); - m_normals.Add(normal); + m_positions[index] = position; + m_normals[index] = normal; } - public gltfMorphTarget ToGltf(glTF gltf, int bufferIndex, bool useNormal) + public gltfMorphTarget ToGltf(glTF gltf, int gltfBuffer, bool useNormal, bool useSparse) { - var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_positions.ToArray(), glBufferTarget.ARRAY_BUFFER); - gltf.accessors[positionAccessorIndex].min = m_positions.Aggregate(m_positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray(); - gltf.accessors[positionAccessorIndex].max = m_positions.Aggregate(m_positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray(); - - var normalAccessorIndex = -1; - if (useNormal) - { - normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_normals.ToArray(), glBufferTarget.ARRAY_BUFFER); - } - return new gltfMorphTarget - { - POSITION = positionAccessorIndex, - NORMAL = normalAccessorIndex, - }; + return BlendShapeExporter.Export(gltf, gltfBuffer, + m_positions, + useNormal ? m_normals : null, + useSparse); } } @@ -116,8 +106,10 @@ namespace UniGLTF public glTFPrimitives ToGltfPrimitive(glTF gltf, int bufferIndex, int materialIndex, IEnumerable indices) { var indicesAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, indices.Select(x => (uint)m_vertexIndexMap[x]).ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER); - var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_positions.ToArray(), glBufferTarget.ARRAY_BUFFER); - var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_normals.ToArray(), glBufferTarget.ARRAY_BUFFER); + var positions = m_positions.ToArray(); + var positionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, positions, glBufferTarget.ARRAY_BUFFER); + var normals = m_normals.ToArray(); + var normalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, normals, glBufferTarget.ARRAY_BUFFER); var uvAccessorIndex0 = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, m_uv.ToArray(), glBufferTarget.ARRAY_BUFFER); int? jointsAccessorIndex = default; diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs index f0a8159cf..bd7796073 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs @@ -7,7 +7,17 @@ namespace UniGLTF { public static class MeshExporter_DividedVertexBuffer { - public static (glTFMesh, Dictionary) Export(glTF gltf, int bufferIndex, + /// + /// Divide vertex buffer(Position, Normal, UV, VertexColor, Skinning and BlendShapes) by submesh usage, then export + /// + /// + /// + /// + /// + /// + /// + /// + public static (glTFMesh, Dictionary) Export(glTF gltf, int gltfBuffer, MeshExportInfo unityMesh, List unityMaterials, IAxisInverter axisInverter, MeshExportSettings settings) { @@ -16,7 +26,7 @@ namespace UniGLTF if (settings.ExportTangents) { - // support しない + // no support throw new NotImplementedException(); } @@ -40,15 +50,14 @@ namespace UniGLTF var indices = mesh.GetIndices(i); var hash = new HashSet(indices); - // mesh - // index の順に attributes を蓄える + // aggrigate vertex attributes var buffer = new MeshExportUtil.VertexBuffer(indices.Length, getJointIndex); usedIndices.Clear(); for (int k = 0; k < positions.Length; ++k) { if (hash.Contains(k)) { - // indices から参照される頂点だけを蓄える + // aggrigate indices usedIndices.Add(k); buffer.Push(k, axisInverter.InvertVector3(positions[k]), axisInverter.InvertVector3(normals[k]), uv[k].ReverseUV()); if (getJointIndex != null) @@ -75,23 +84,25 @@ namespace UniGLTF flipped.Add(t1); flipped.Add(t0); } - var gltfPrimitive = buffer.ToGltfPrimitive(gltf, bufferIndex, materialIndex, flipped); + var gltfPrimitive = buffer.ToGltfPrimitive(gltf, gltfBuffer, materialIndex, flipped); - // blendShape + // blendShape(morph target) for (int j = 0; j < mesh.blendShapeCount; ++j) { var blendShape = new MeshExportUtil.BlendShapeBuffer(indices.Length); - // index の順に attributes を蓄える + // aggriage morph target mesh.GetBlendShapeFrameVertices(j, 0, blendShapePositions, blendShapeNormals, null); + int l = 0; foreach (var k in usedIndices) { - blendShape.Push( + blendShape.Set(l++, axisInverter.InvertVector3(blendShapePositions[k]), axisInverter.InvertVector3(blendShapeNormals[k])); } - gltfPrimitive.targets.Add(blendShape.ToGltf(gltf, bufferIndex, !settings.ExportOnlyBlendShapePosition)); + gltfPrimitive.targets.Add(blendShape.ToGltf(gltf, gltfBuffer, !settings.ExportOnlyBlendShapePosition, + settings.UseSparseAccessorForMorphTarget)); } gltfMesh.primitives.Add(gltfPrimitive); diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs index 23aefbca8..f80edb35b 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs @@ -10,11 +10,12 @@ namespace UniGLTF { /// /// primitive 間で vertex を共有する形で Export する。 + /// UniVRM-0.71.0 以降は、MeshExporterDivided.Export もある。 /// - /// UniVRM-0.71.0 までの挙動 + /// * GLB/GLTF は shared(default) と divided を選択可能 + /// * VRM0 は shared 仕様 + /// * VRM1 は divided 仕様 /// - /// UniVRM-0.71.0 以降は、MeshExporterDivided.Export もある - /// /// /// /// /// @@ -159,12 +160,13 @@ namespace UniGLTF unityMesh.Mesh, j, settings.UseSparseAccessorForMorphTarget, settings.ExportOnlyBlendShapePosition, axisInverter); - if (morphTarget.POSITION < 0 && morphTarget.NORMAL < 0 && morphTarget.TANGENT < 0) + if (morphTarget.POSITION < 0) { + // Skip empty blendShape. + // Shift blendShape's index. continue; } - // maybe skip var blendShapeName = unityMesh.Mesh.GetBlendShapeName(j); blendShapeIndexMap.Add(j, exportBlendShapes++); targetNames.Add(blendShapeName); @@ -199,7 +201,7 @@ namespace UniGLTF } static gltfMorphTarget ExportMorphTarget(glTF gltf, int bufferIndex, - Mesh mesh, int j, + Mesh mesh, int blendShapeIndex, bool useSparseAccessorForMorphTarget, bool exportOnlyBlendShapePosition, IAxisInverter axisInverter) @@ -211,106 +213,41 @@ namespace UniGLTF var useNormal = usePosition && blendShapeNormals != null && blendShapeNormals.Length == blendShapeVertices.Length; // var useNormal = usePosition && blendShapeNormals != null && blendShapeNormals.Length == blendShapeVertices.Length && !exportOnlyBlendShapePosition; - var blendShapeTangents = mesh.tangents.Select(y => (Vector3)y).ToArray(); - //var useTangent = usePosition && blendShapeTangents != null && blendShapeTangents.Length == blendShapeVertices.Length; - var useTangent = false; + // var blendShapeTangents = mesh.tangents.Select(y => (Vector3)y).ToArray(); + // //var useTangent = usePosition && blendShapeTangents != null && blendShapeTangents.Length == blendShapeVertices.Length; + // var useTangent = false; - var frameCount = mesh.GetBlendShapeFrameCount(j); - mesh.GetBlendShapeFrameVertices(j, frameCount - 1, blendShapeVertices, blendShapeNormals, null); + var frameCount = mesh.GetBlendShapeFrameCount(blendShapeIndex); + mesh.GetBlendShapeFrameVertices(blendShapeIndex, frameCount - 1, blendShapeVertices, blendShapeNormals, null); - var blendShapePositionAccessorIndex = -1; - var blendShapeNormalAccessorIndex = -1; - var blendShapeTangentAccessorIndex = -1; - if (useSparseAccessorForMorphTarget) + // + // invert axis + // + for (int i = 0; i < blendShapeVertices.Length; ++i) { - var accessorCount = blendShapeVertices.Length; - var sparseIndices = Enumerable.Range(0, blendShapeVertices.Length) - .Where(x => UseSparse( - usePosition, blendShapeVertices[x], - useNormal, blendShapeNormals[x], - useTangent, blendShapeTangents[x])) - .ToArray() - ; - - if (sparseIndices.Length == 0) - { - usePosition = false; - useNormal = false; - useTangent = false; - } - else - { - Debug.LogFormat("Sparse {0}/{1}", sparseIndices.Length, mesh.vertexCount); - } - - var sparseIndicesViewIndex = -1; - if (usePosition) - { - sparseIndicesViewIndex = gltf.ExtendBufferAndGetViewIndex(bufferIndex, sparseIndices); - - blendShapeVertices = sparseIndices.Select(x => axisInverter.InvertVector3(blendShapeVertices[x])).ToArray(); - blendShapePositionAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount, - blendShapeVertices, - sparseIndices, sparseIndicesViewIndex, - glBufferTarget.NONE); - } - - if (useNormal) - { - blendShapeNormals = sparseIndices.Select(x => axisInverter.InvertVector3(blendShapeNormals[x])).ToArray(); - blendShapeNormalAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount, - blendShapeNormals, - sparseIndices, sparseIndicesViewIndex, - glBufferTarget.NONE); - } - - if (useTangent) - { - blendShapeTangents = sparseIndices.Select(x => axisInverter.InvertVector3(blendShapeTangents[x])).ToArray(); - blendShapeTangentAccessorIndex = gltf.ExtendSparseBufferAndGetAccessorIndex(bufferIndex, accessorCount, - blendShapeTangents, sparseIndices, sparseIndicesViewIndex, - glBufferTarget.NONE); - } + blendShapeVertices[i] = axisInverter.InvertVector3(blendShapeVertices[i]); } - else + for (int i = 0; i < blendShapeNormals.Length; ++i) { - for (int i = 0; i < blendShapeVertices.Length; ++i) blendShapeVertices[i] = axisInverter.InvertVector3(blendShapeVertices[i]); - if (usePosition) - { - blendShapePositionAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, - blendShapeVertices, - glBufferTarget.ARRAY_BUFFER); - } - - if (useNormal) - { - for (int i = 0; i < blendShapeNormals.Length; ++i) blendShapeNormals[i] = axisInverter.InvertVector3(blendShapeNormals[i]); - blendShapeNormalAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, - blendShapeNormals, - glBufferTarget.ARRAY_BUFFER); - } - - if (useTangent) - { - for (int i = 0; i < blendShapeTangents.Length; ++i) blendShapeTangents[i] = axisInverter.InvertVector3(blendShapeTangents[i]); - blendShapeTangentAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, - blendShapeTangents, - glBufferTarget.ARRAY_BUFFER); - } + blendShapeNormals[i] = axisInverter.InvertVector3(blendShapeNormals[i]); } - if (blendShapePositionAccessorIndex != -1) + var positions = mesh.vertices; + for (int i = 0; i < positions.Length; ++i) { - gltf.accessors[blendShapePositionAccessorIndex].min = blendShapeVertices.Aggregate(blendShapeVertices[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray(); - gltf.accessors[blendShapePositionAccessorIndex].max = blendShapeVertices.Aggregate(blendShapeVertices[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray(); + positions[i] = axisInverter.InvertVector3(positions[i]); } - return new gltfMorphTarget + var normals = mesh.normals; + for (int i = 0; i < normals.Length; ++i) { - POSITION = blendShapePositionAccessorIndex, - NORMAL = blendShapeNormalAccessorIndex, - TANGENT = blendShapeTangentAccessorIndex, - }; + normals[i] = axisInverter.InvertVector3(normals[i]); + } + + return BlendShapeExporter.Export(gltf, bufferIndex, + blendShapeVertices, + exportOnlyBlendShapePosition && useNormal ? null : blendShapeNormals, + useSparseAccessorForMorphTarget); } } } diff --git a/Assets/VRM10/Editor/ScriptedImporter/VrmScriptedImporterImpl.cs b/Assets/VRM10/Editor/ScriptedImporter/VrmScriptedImporterImpl.cs index 6c41ec630..09ac8dda3 100644 --- a/Assets/VRM10/Editor/ScriptedImporter/VrmScriptedImporterImpl.cs +++ b/Assets/VRM10/Editor/ScriptedImporter/VrmScriptedImporterImpl.cs @@ -62,9 +62,9 @@ namespace UniVRM10 return "cannot migrate"; } } - catch (Exception) + catch (Exception ex) { - return "migration error"; + return $"migration error: {ex}"; } parser = new GltfParser(); diff --git a/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs b/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs index 64c646c71..8153fae45 100644 --- a/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs +++ b/Assets/VRM10/Runtime/IO/Model/MeshImporterDivided.cs @@ -93,7 +93,15 @@ namespace UniVRM10 { var morphTarget = src.Meshes[meshIndex].MorphTargets[i]; positions.AddRange(morphTarget.VertexBuffer.Positions.GetSpan()); - normals.AddRange(morphTarget.VertexBuffer.Normals.GetSpan()); + if (morphTarget.VertexBuffer.Normals != null) + { + normals.AddRange(morphTarget.VertexBuffer.Normals.GetSpan()); + } + else + { + // fill zero + normals.AddRange(Enumerable.Range(0, morphTarget.VertexBuffer.Count).Select(x => Vector3.zero)); + } } dst.AddBlendShapeFrame(name, 100.0f, positions.ToArray(), normals.ToArray(), null); } diff --git a/Assets/VRM10/Runtime/IO/Model/MeshWriter.cs b/Assets/VRM10/Runtime/IO/Model/MeshWriter.cs index 7baaaa8f1..7f2565ad5 100644 --- a/Assets/VRM10/Runtime/IO/Model/MeshWriter.cs +++ b/Assets/VRM10/Runtime/IO/Model/MeshWriter.cs @@ -126,7 +126,7 @@ namespace UniVRM10 // blendShape for (int j = 0; j < mesh.MorphTargets.Count; ++j) { - var blendShape = new MeshExportUtil.BlendShapeBuffer(indices.Length); + var blendShape = new MeshExportUtil.BlendShapeBuffer(usedIndices.Count); // index の順に attributes を蓄える var morph = mesh.MorphTargets[j]; @@ -136,15 +136,16 @@ namespace UniVRM10 { blendShapeNormals = morph.VertexBuffer.Normals.GetSpan(); } + int l = 0; foreach (var k in usedIndices) { - blendShape.Push( + blendShape.Set(l++, blendShapePositions[k], blendShapeNormals.HasValue ? blendShapeNormals.Value[k] : UnityEngine.Vector3.zero ); } - gltfPrimitive.targets.Add(blendShape.ToGltf(storage.Gltf, bufferIndex, !option.removeMorphNormal)); + gltfPrimitive.targets.Add(blendShape.ToGltf(storage.Gltf, bufferIndex, !option.removeMorphNormal, option.sparse)); } yield return gltfPrimitive; diff --git a/Assets/VRM10/Runtime/Migration/RotateY180.cs b/Assets/VRM10/Runtime/Migration/RotateY180.cs index f4878cb63..327bc11dc 100644 --- a/Assets/VRM10/Runtime/Migration/RotateY180.cs +++ b/Assets/VRM10/Runtime/Migration/RotateY180.cs @@ -49,6 +49,11 @@ namespace UniVRM10 static void ReverseVector3Array(glTF gltf, int accessorIndex, HashSet used) { + if (accessorIndex == -1) + { + return; + } + if (!used.Add(accessorIndex)) { return;