diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/Parser/GlbLowLevelParser.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/Parser/GlbLowLevelParser.cs index c1b8798d9..2207bc6cd 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/Parser/GlbLowLevelParser.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/Parser/GlbLowLevelParser.cs @@ -83,6 +83,7 @@ namespace UniGLTF RestoreOlderVersionValues(json, GLTF); FixMeshNameUnique(GLTF); + FixBlendShapeNameUnique(GLTF); foreach (var image in GLTF.images) { image.uri = PrepareUri(image.uri); @@ -121,6 +122,61 @@ namespace UniGLTF } } + // https://github.com/vrm-c/UniVRM/issues/2619 + private static void FixBlendShapeNameUnique(glTF GLTF) + { + foreach (var mesh in GLTF.meshes) + { + /// https://github.com/KhronosGroup/glTF/pull/1631/files + if (gltf_mesh_extras_targetNames.TryGet(mesh, out var targetNames)) + { + var used = new HashSet(); + int rename = 0; + for (int i = 0; i < targetNames.Count; ++i) + { + var target_name = targetNames[i]; + if (string.IsNullOrEmpty(target_name)) + { + // no name + targetNames[i] = $"__{i}__"; + UniGLTFLogger.Log($"rename blendshape: {mesh.name}[{i}]{target_name} => {targetNames[i]}"); + rename += 1; + } + else if (used.Contains(target_name)) + { + // rename + var uname = $"__{i}__{target_name}"; + targetNames[i] = uname; + UniGLTFLogger.Log($"rename blendshape: {mesh.name}[{i}]{target_name} => {targetNames[i]}"); + rename += 1; + } + used.Add(targetNames[i]); + } + if (rename > 0) + { + + // var extrans = new Dictionary() + // { + // {"targetNames", targetNames }, + var f = new JsonFormatter(); + f.BeginMap(); + f.Key("targetNames"); + { + f.BeginList(); + foreach (var name in targetNames) + { + f.Value(name); + } + f.EndList(); + } + f.EndMap(); + var json = f.ToString(); + mesh.extras = new UniGLTF.glTFExtensionImport(JsonParser.Parse(json)); + } + } + } + } + private static void RenameImageFromTexture(glTF GLTF, int i) { foreach (var texture in GLTF.textures) diff --git a/Assets/UniGLTF/Runtime/UniJSON/Json/JsonParser.cs b/Assets/UniGLTF/Runtime/UniJSON/Json/JsonParser.cs index a43262214..1da036317 100644 --- a/Assets/UniGLTF/Runtime/UniJSON/Json/JsonParser.cs +++ b/Assets/UniGLTF/Runtime/UniJSON/Json/JsonParser.cs @@ -53,7 +53,7 @@ namespace UniJSON } default: - throw new ParserException(segment + " is not valid json start"); + throw new ParserException(segment + " is not valid json start(maybe invalid ',')"); } } diff --git a/Assets/UniGLTF/Tests/UniGLTF/GltfLoadTests.cs b/Assets/UniGLTF/Tests/UniGLTF/GltfLoadTests.cs index 58e0f7e76..e9d7aa201 100644 --- a/Assets/UniGLTF/Tests/UniGLTF/GltfLoadTests.cs +++ b/Assets/UniGLTF/Tests/UniGLTF/GltfLoadTests.cs @@ -233,5 +233,103 @@ namespace UniGLTF } } } + + // https://github.khronos.org/glTF-Tutorials/gltfTutorial/gltfTutorial_017_SimpleMorphTarget.html + [Test] + public void Preprocess_AutoNodeName() + { + var json = @" +{ + ""scene"": 0, + ""scenes"" : [ + { + ""nodes"" : [ 0 ] + } + ], + + ""nodes"" : [ + { + ""mesh"" : 0 + } + ], + + ""meshes"" : [ + { + ""primitives"" : [ { + ""attributes"" : { + ""POSITION"" : 1 + }, + ""indices"" : 0 + } ] + } + ], + + ""buffers"" : [ + { + ""uri"" : ""data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA="", + ""byteLength"" : 44 + } + ], + ""bufferViews"" : [ + { + ""buffer"" : 0, + ""byteOffset"" : 0, + ""byteLength"" : 6, + ""target"" : 34963 + }, + { + ""buffer"" : 0, + ""byteOffset"" : 8, + ""byteLength"" : 36, + ""target"" : 34962 + } + ], + ""accessors"" : [ + { + ""bufferView"" : 0, + ""byteOffset"" : 0, + ""componentType"" : 5123, + ""count"" : 3, + ""type"" : ""SCALAR"", + ""max"" : [ 2 ], + ""min"" : [ 0 ] + }, + { + ""bufferView"" : 1, + ""byteOffset"" : 0, + ""componentType"" : 5126, + ""count"" : 3, + ""type"" : ""VEC3"", + ""max"" : [ 1.0, 1.0, 0.0 ], + ""min"" : [ 0.0, 0.0, 0.0 ] + } + ], + + ""asset"" : { + ""version"" : ""2.0"" + } +} + "; + + var gltf = GlbLowLevelParser.ParseGltf("tmp", json, null, null, default); + Assert.AreEqual(1, gltf.GLTF.nodes.Count); + Assert.AreEqual("0", gltf.GLTF.nodes[0].name); + } + + // https://github.khronos.org/glTF-Tutorials/gltfTutorial/gltfTutorial_017_SimpleMorphTarget.html + [Test] + public void Preprocess_DupMorphTargetName() + { + var asset = UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/UniGLTF/Tests/UniGLTF/gltfTutorial_017_SimpleMorphTarget.txt"); + Assert.True(asset); + var gltf = GlbLowLevelParser.ParseGltf("tmp", asset.text, null, null, default); + Assert.AreEqual(1, gltf.GLTF.nodes.Count); + Assert.AreEqual("0", gltf.GLTF.nodes[0].name); + Assert.True(gltf_mesh_extras_targetNames.TryGet(gltf.GLTF.meshes[0], out List targetNames)); + // [dup_name, dup_name] => [dup_name, __1__dup_name] + Assert.AreEqual(2, targetNames.Count); + Assert.AreEqual("dup_name", targetNames[0]); + Assert.AreEqual("__1__dup_name", targetNames[1]); + } } } diff --git a/Assets/UniGLTF/Tests/UniGLTF/gltfTutorial_017_SimpleMorphTarget.txt b/Assets/UniGLTF/Tests/UniGLTF/gltfTutorial_017_SimpleMorphTarget.txt new file mode 100644 index 000000000..93654b4df --- /dev/null +++ b/Assets/UniGLTF/Tests/UniGLTF/gltfTutorial_017_SimpleMorphTarget.txt @@ -0,0 +1,198 @@ +{ + "scene": 0, + "scenes":[ + { + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0 + } + ], + "meshes":[ + { + "primitives":[ + { + "attributes":{ + "POSITION":1 + }, + "targets":[ + { + "POSITION":2 + }, + { + "POSITION":3 + } + ], + "indices":0 + } + ], + "weights":[ + 1.0, + 0.5 + ], + "extras":{ + "targetNames": [ + "dup_name", + "dup_name" + ] + } + } + ], + + "animations":[ + { + "samplers":[ + { + "input":4, + "interpolation":"LINEAR", + "output":5 + } + ], + "channels":[ + { + "sampler":0, + "target":{ + "node":0, + "path":"weights" + } + } + ] + } + ], + + "buffers":[ + { + "uri":"data:application/gltf-buffer;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAAAA=", + "byteLength":116 + }, + { + "uri":"data:application/gltf-buffer;base64,AAAAAAAAgD8AAABAAABAQAAAgEAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAA", + "byteLength":60 + } + ], + "bufferViews":[ + { + "buffer":0, + "byteOffset":0, + "byteLength":6, + "target":34963 + }, + { + "buffer":0, + "byteOffset":8, + "byteLength":108, + "byteStride":12, + "target":34962 + }, + { + "buffer":1, + "byteOffset":0, + "byteLength":20 + }, + { + "buffer":1, + "byteOffset":20, + "byteLength":40 + } + ], + "accessors":[ + { + "bufferView":0, + "byteOffset":0, + "componentType":5123, + "count":3, + "type":"SCALAR", + "max":[ + 2 + ], + "min":[ + 0 + ] + }, + { + "bufferView":1, + "byteOffset":0, + "componentType":5126, + "count":3, + "type":"VEC3", + "max":[ + 1.0, + 0.5, + 0.0 + ], + "min":[ + 0.0, + 0.0, + 0.0 + ] + }, + { + "bufferView":1, + "byteOffset":36, + "componentType":5126, + "count":3, + "type":"VEC3", + "max":[ + 0.0, + 1.0, + 0.0 + ], + "min":[ + -1.0, + 0.0, + 0.0 + ] + }, + { + "bufferView":1, + "byteOffset":72, + "componentType":5126, + "count":3, + "type":"VEC3", + "max":[ + 1.0, + 1.0, + 0.0 + ], + "min":[ + 0.0, + 0.0, + 0.0 + ] + }, + { + "bufferView":2, + "byteOffset":0, + "componentType":5126, + "count":5, + "type":"SCALAR", + "max":[ + 4.0 + ], + "min":[ + 0.0 + ] + }, + { + "bufferView":3, + "byteOffset":0, + "componentType":5126, + "count":10, + "type":"SCALAR", + "max":[ + 1.0 + ], + "min":[ + 0.0 + ] + } + ], + + "asset":{ + "version":"2.0" + } +} diff --git a/Assets/UniGLTF/Tests/UniGLTF/gltfTutorial_017_SimpleMorphTarget.txt.meta b/Assets/UniGLTF/Tests/UniGLTF/gltfTutorial_017_SimpleMorphTarget.txt.meta new file mode 100644 index 000000000..fae89f1c0 --- /dev/null +++ b/Assets/UniGLTF/Tests/UniGLTF/gltfTutorial_017_SimpleMorphTarget.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ff5b21eda4deae14583275d904ae3d08 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: