From a3ce0a4f59896f50e8b8332f199f3c18c60ff17d Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 22 Dec 2024 17:37:34 +0900 Subject: [PATCH] feat: add support for same name blendShape in a fbx --- Internal/MeshInfo2/MeshInfo2.cs | 45 ++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/Internal/MeshInfo2/MeshInfo2.cs b/Internal/MeshInfo2/MeshInfo2.cs index a9ba9d39..0b0f3bab 100644 --- a/Internal/MeshInfo2/MeshInfo2.cs +++ b/Internal/MeshInfo2/MeshInfo2.cs @@ -14,6 +14,7 @@ using UnityEngine.Rendering; using Debug = System.Diagnostics.Debug; using Object = UnityEngine.Object; +using Random = UnityEngine.Random; namespace Anatawa12.AvatarOptimizer.Processors.SkinnedMeshes { @@ -201,14 +202,9 @@ private void ReadBones(Mesh mesh) private void ReadBlendShapes(Mesh mesh) { - BlendShapes.Clear(); - Profiler.BeginSample("Save Applied Weights"); - for (var blendShape = 0; blendShape < mesh.blendShapeCount; blendShape++) - BlendShapes.Add((mesh.GetBlendShapeName(blendShape), 0.0f)); - Profiler.EndSample(); - Profiler.BeginSample("New Reading Method"); - var buffer = new BlendShapeBuffer(mesh); + BlendShapes.Clear(); + var buffer = new BlendShapeBuffer(mesh, BlendShapes); for (var vertex = 0; vertex < Vertices.Count; vertex++) { Vertices[vertex].BlendShapeBuffer = buffer; @@ -1203,7 +1199,7 @@ public class BlendShapeBuffer : IReferenceCount public readonly NativeArray[] DeltaTangents; public readonly int VertexCount; - public BlendShapeBuffer(Mesh sourceMesh) + public BlendShapeBuffer(Mesh sourceMesh, List<(string name, float weight)> blendShapes) { Profiler.BeginSample("BlendShapeBuffer:Create"); var totalFrames = 0; @@ -1254,12 +1250,43 @@ public BlendShapeBuffer(Mesh sourceMesh) Profiler.EndSample(); } - Shapes.Add(name, new BlendShapeShape(frameInfos)); + if (!Shapes.TryAdd(name, new BlendShapeShape(frameInfos))) + { + // duplicated blendShape name detected. + // This can be generated with 3ds Max or other tools. + // Rename blendShape a little to avoid conflict. + name = $"{name}-nameConflict-{GetShortRandom()}"; + Shapes.Add(name, new BlendShapeShape(frameInfos)); + } + blendShapes.Add((name, 0.0f)); Profiler.EndSample(); } Profiler.EndSample(); } + private static string GetShortRandom() + { + // generate 4-char base64 string + // with 4 of 64 characters, it has 64^4 = 16777216 possibilities. + // When we create 100 times, the possibility of collision is one in about 3390 + var chars = new char[4]; + + for (var i = 0; i < chars.Length; i++) + chars[i] = Base64Char(Random.Range(0, 64)); + return new string(chars); + + static char Base64Char(int value) => value switch + { + < 0 => throw new ArgumentOutOfRangeException(nameof(value), $"{value}"), + < 10 => (char)('0' + value), + < 36 => (char)('A' + value - 10), + < 62 => (char)('a' + value - 36), + 62 => '-', + 63 => '_', + _ => throw new ArgumentOutOfRangeException(nameof(value), $"{value}"), + }; + } + // create empty private BlendShapeBuffer() {