diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs index 83aaa9bbf..f73c6de45 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshData.cs @@ -12,9 +12,10 @@ namespace UniGLTF { private NativeArray _vertices; public NativeArray Vertices => _vertices.GetSubArray(0, _currentVertexCount); - private NativeArray _skinnedMeshVertices; - public NativeArray SkinnedMeshVertices => _skinnedMeshVertices.GetSubArray(0, _currentVertexCount); int _currentVertexCount = 0; + private NativeArray _skinnedMeshVertices; + public NativeArray SkinnedMeshVertices => _skinnedMeshVertices.GetSubArray(0, _currentSkinCount); + int _currentSkinCount = 0; private NativeArray _indices; public NativeArray Indices => _indices.GetSubArray(0, _currentIndexCount); @@ -29,6 +30,7 @@ namespace UniGLTF public bool HasNormal { get; private set; } = true; public string Name { get; private set; } + public bool AssignBoneWeight { get; private set; } public MeshData(int vertexCapacity, int indexCapacity) { @@ -47,16 +49,19 @@ namespace UniGLTF void Clear() { _currentVertexCount = 0; + _currentSkinCount = 0; _currentIndexCount = 0; _subMeshes.Clear(); _materialIndices.Clear(); _blendShapes.Clear(); Name = null; HasNormal = false; + AssignBoneWeight = false; } /// - /// バッファ共有方式の判定 + /// バッファ共有方式(vrm-0.x)の判定。 + /// import の後方互換性のためで、vrm-1.0 export では使いません。 /// /// * バッファ共用方式は VertexBuffer が同じでSubMeshの index buffer がスライドしていく方式 /// * バッファがひとつのとき @@ -120,8 +125,8 @@ namespace UniGLTF } /// - /// * flip triangle - /// * add submesh offset + /// * flip triangle(gltfとtriangleの CW と CCW が異なる) + /// * add submesh offset(gltfのprimitiveは、頂点バッファが分かれているので連結。連結すると index が変わる(offset)) /// private void PushIndices(BufferAccessor src, int offset) { @@ -240,10 +245,14 @@ namespace UniGLTF private void DropUnusedVertices() { Profiler.BeginSample("MeshData.DropUnusedVertices"); - var maxIndex = _indices.Max(); + var maxIndex = Indices.Max(); if (maxIndex + 1 < _currentVertexCount) { - _currentIndexCount = maxIndex + 1; + _currentVertexCount = maxIndex + 1; + } + if (maxIndex + 1 < _currentSkinCount) + { + _currentSkinCount = maxIndex + 1; } foreach (var blendShape in _blendShapes) { @@ -251,7 +260,6 @@ namespace UniGLTF Truncate(blendShape.Normals, maxIndex); Truncate(blendShape.Tangents, maxIndex); } - Profiler.EndSample(); } @@ -279,16 +287,18 @@ namespace UniGLTF } } - private void AddVertex(MeshVertex vertex, SkinnedMeshVertex? skin) + private void AddVertex(MeshVertex vertex) { _vertices[_currentVertexCount] = vertex; - if (skin.HasValue) - { - _skinnedMeshVertices[_currentVertexCount] = skin.Value; - } _currentVertexCount += 1; } + private void AddSkin(SkinnedMeshVertex skin) + { + _skinnedMeshVertices[_currentSkinCount] = skin; + _currentSkinCount += 1; + } + /// /// 各 primitive の attribute の要素が同じでない。=> uv が有るものと無いものが混在するなど /// glTF 的にはありうる。 @@ -314,6 +324,7 @@ namespace UniGLTF var texCoords1 = primitives.GetTexCoords1(data, positions.Length); var colors = primitives.GetColors(data, positions.Length); var skinning = SkinningInfo.Create(data, gltfMesh, primitives); + AssignBoneWeight = skinning.ShouldSetRendererNodeAsBone ; CheckAttributeUsages(primitives); @@ -348,8 +359,12 @@ namespace UniGLTF texCoord0, texCoord1, color - ), - skinning.GetSkinnedVertex(i)); + )); + var skin = skinning.GetSkinnedVertex(i); + if (skin.HasValue) + { + AddSkin(skin.Value); + } } // blendshape @@ -441,6 +456,7 @@ namespace UniGLTF var texCoords1 = primitives.GetTexCoords1(data, positions.Length); var colors = primitives.GetColors(data, positions.Length); var skinning = SkinningInfo.Create(data, gltfMesh, primitives); + AssignBoneWeight = skinning.ShouldSetRendererNodeAsBone ; CheckAttributeUsages(primitives); @@ -472,8 +488,13 @@ namespace UniGLTF normal, texCoord0, texCoord1, - color), - skinning.GetSkinnedVertex(i)); + color)); + var skin = + skinning.GetSkinnedVertex(i); + if (skin.HasValue) + { + AddSkin(skin.Value); + } } // blendshape diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs index f3b65e5f0..fac86a104 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_DividedVertexBuffer.cs @@ -34,6 +34,10 @@ namespace UniGLTF var normals = mesh.normals; var uv = mesh.uv; var boneWeights = mesh.boneWeights; + if (boneWeights.All(x => x.weight0 == 0 && x.weight1 == 0 && x.weight2 == 0 && x.weight3 == 0)) + { + boneWeights = null; + } var colors = mesh.colors; Func getJointIndex = null; diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs index e1885be1f..6da415fcd 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshExporter_SharedVertexBuffer.cs @@ -57,15 +57,23 @@ namespace UniGLTF colorAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.colors, glBufferTarget.ARRAY_BUFFER); } - var boneweights = mesh.boneWeights; - var weightAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneweights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER); - var jointsAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneweights.Select(y => - new UShort4( - (ushort)unityMesh.GetJointIndex(y.boneIndex0), - (ushort)unityMesh.GetJointIndex(y.boneIndex1), - (ushort)unityMesh.GetJointIndex(y.boneIndex2), - (ushort)unityMesh.GetJointIndex(y.boneIndex3)) - ).ToArray(), glBufferTarget.ARRAY_BUFFER); + var boneWeights = mesh.boneWeights; + var weightAccessorIndex = -1; + var jointsAccessorIndex = -1; + if (boneWeights.All(x => x.weight0 == 0 && x.weight1 == 0 && x.weight2 == 0 && x.weight3 == 0)) + { + } + else + { + weightAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneWeights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER); + jointsAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneWeights.Select(y => + new UShort4( + (ushort)unityMesh.GetJointIndex(y.boneIndex0), + (ushort)unityMesh.GetJointIndex(y.boneIndex1), + (ushort)unityMesh.GetJointIndex(y.boneIndex2), + (ushort)unityMesh.GetJointIndex(y.boneIndex3)) + ).ToArray(), glBufferTarget.ARRAY_BUFFER); + } var attributes = new glTFAttributes { diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs index adfb0c100..6190804c5 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshUploader.cs @@ -137,7 +137,8 @@ namespace UniGLTF var result = new MeshWithMaterials { Mesh = mesh, - Materials = data.MaterialIndices.Select(materialFromIndex).ToArray() + Materials = data.MaterialIndices.Select(materialFromIndex).ToArray(), + ShouldSetRendererNodeAsBone = data.AssignBoneWeight, }; await awaitCaller.NextFrame(); diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithMaterials.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithMaterials.cs index 42c2e11d4..0b6e46aba 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithMaterials.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/MeshWithMaterials.cs @@ -9,7 +9,12 @@ namespace UniGLTF public Mesh Mesh; public Material[] Materials; + /// + /// SkinningInfo.ShouldSetRendererNodeAsBone を伝播させる。 + /// + public bool ShouldSetRendererNodeAsBone = false; + // 複数のノードから参照されうる - public List Renderers=new List(); // SkinnedMeshRenderer or MeshRenderer + public List Renderers = new List(); // SkinnedMeshRenderer or MeshRenderer } } diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/SkinningInfo.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/SkinningInfo.cs index 33fae084a..526d11abf 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/SkinningInfo.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshIO/SkinningInfo.cs @@ -5,14 +5,69 @@ namespace UniGLTF public JointsAccessor.Getter Joints; public WeightsAccessor.Getter Weights; + /// + /// gltfMesh に morphTarget が有る場合に、Unity では boneWeight の有無と無関係に UnityEngine.SkinnedMeshRenderer を使います。 + /// そのため `boneWeight が無い` UnityEngine.SkinnedMeshRenderer となる場合があります。 + /// boneWeight の無い SkinnedMeshRenderer の rootBone は、 + /// + /// * boundingBox の中心 + /// * boneWeight の無いボーンに対するスキニング + /// + /// が兼用になるためメッシュが正しく描画されない場合があります。 + /// この問題の対策として、全頂点に対して boneWeight = 1, boneJoint = 0 を付与します。 + /// この boneWeight を利用するために AddComponent する段階で、 + /// + /// * mesh.bindMatrices + /// * SkinnedMeshRenderer.bones = new Transform[]{ renderer.transform }; + /// + /// が必要です。この変数はその指示です。 + /// + public bool ShouldSetRendererNodeAsBone { get; private set; } + public static SkinningInfo Create(GltfData data, glTFMesh mesh, glTFPrimitives primitives) { + var hasMorphTarget = HasMorphTarget(mesh); + var positions = data.GLTF.accessors[primitives.attributes.POSITION]; - return new SkinningInfo + var skinning = new SkinningInfo { Joints = primitives.GetJoints(data, positions.count), Weights = primitives.GetWeights(data, positions.count), }; + + if (skinning.Joints != null) + { + // use SkinnedMeshRenderer + return skinning; + } + else if (!hasMorphTarget) + { + // use MeshRenderer + return skinning; + } + else + { + // use SkinnedMeshRenderer without boneWeight. + // https://github.com/vrm-c/UniVRM/issues/1675 + return new SkinningInfo + { + ShouldSetRendererNodeAsBone = true, + Joints = _ => (0, 0, 0, 0), + Weights = _ => (1, 0, 0, 0), // assign weight 1 + }; + } + } + + static bool HasMorphTarget(glTFMesh mesh) + { + foreach (var prim in mesh.primitives) + { + if (prim.targets != null && prim.targets.Count > 0) + { + return true; + } + } + return false; } private static (float x, float y, float z, float w) NormalizeBoneWeight( @@ -32,7 +87,7 @@ namespace UniGLTF return src; } - public SkinnedMeshVertex GetSkinnedVertex(int i) + public SkinnedMeshVertex? GetSkinnedVertex(int i) { if (Joints == null) { diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/NodeImporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/NodeImporter.cs index 48f646e2e..df8e4832e 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/NodeImporter.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/NodeImporter.cs @@ -119,6 +119,20 @@ namespace UniGLTF renderer.sharedMaterials = mesh.Materials; // invisible in loading renderer.enabled = false; + + if (mesh.ShouldSetRendererNodeAsBone ) + { + renderer.bones = new[] { renderer.transform }; + + // + // calc default matrices + // https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html + // + var meshCoords = renderer.transform; + var calculatedBindPoses = renderer.bones.Select(bone => bone.worldToLocalMatrix * meshCoords.localToWorldMatrix).ToArray(); + mesh.Mesh.bindposes = calculatedBindPoses; + } + mesh.Renderers.Add(renderer); } }