Merge pull request #2712 from ousttrue/fix/unique_morphname

[importer] blendshape の名前重複をリネームする
This commit is contained in:
ousttrue
2025-08-22 16:47:36 +09:00
committed by GitHub
5 changed files with 360 additions and 1 deletions

View File

@@ -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<string>();
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<string, JsonNode>()
// {
// {"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)

View File

@@ -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 ',')");
}
}

View File

@@ -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<TextAsset>("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<string> 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]);
}
}
}

View File

@@ -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"
}
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ff5b21eda4deae14583275d904ae3d08
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: