diff --git a/CHANGELOG.md b/CHANGELOG.md index 41df904..750a131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ## Next Version +### Features +* Add option to keep MMD blendshapes intact. [(more)](https://github.com/d4rkc0d3r/d4rkAvatarOptimizer/issues/32) + ### Bug Fixes * Apparently `sampler` is also a way to declare a `SamplerState`. So now the optimizer also replaces those. * Parse and ignore const keyword in shader function parameters. diff --git a/Editor/d4rkAvatarOptimizer.cs b/Editor/d4rkAvatarOptimizer.cs index d84b190..6cf988b 100644 --- a/Editor/d4rkAvatarOptimizer.cs +++ b/Editor/d4rkAvatarOptimizer.cs @@ -33,6 +33,7 @@ public class d4rkAvatarOptimizer : MonoBehaviour public bool MergeSameDimensionTextures = true; public bool MergeBackFaceCullingWithCullingOff = false; public bool MergeDifferentRenderQueue = false; + public bool KeepMMDBlendShapes = false; public bool DeleteUnusedComponents = true; public bool DeleteUnusedGameObjects = false; public bool MergeSimpleTogglesAsBlendTree = true; @@ -145,6 +146,66 @@ public void Optimize() private static HashSet convertedMeshRendererPaths = new HashSet(); private static Dictionary movingParentMap = new Dictionary(); private static Dictionary transformFromOldPath = new Dictionary(); + // blendshape names come from https://www.deviantart.com/xoriu/art/MMD-Facial-Expressions-Chart-341504917 + private static HashSet MMDBlendShapes = new HashSet() + { + "まばたき", "Blink", + "笑い", "Smile", + "ウィンク", "Wink", + "ウィンク右", "Wink-a", + "ウィンク2", "Wink-b", + "ウィンク2右", "Wink-c", + "なごみ", "Howawa", + "はぅ", "> <", + "びっくり", "Ha!!!", + "じと目", "Jito-eye", + "キリッ", "Kiri-eye", + "はちゅ目", "O O", + "星目", "EyeStar", + "はぁと", "EyeHeart", + "瞳小", "EyeSmall", + "瞳縦潰れ", "EyeSmall-v", + "光下", "EyeUnderli", + "恐ろしい子!", "EyeFunky", + "ハイライト消", "EyeHi-off", + "映り込み消", "EyeRef-off", + "喜び", "Joy", + "わぉ?!", "Wao?!", + "なごみω", "Howawa ω", + "悲しむ", "Wail", + "敵意", "Hostility", + "あ", "a", + "い", "i", + "う", "u", + "え", "e", + "お", "o", + "あ2", "a 2", + "ん", "n", + "▲", "Mouse_1", + "∧", "Mouse_2", + "□", "□", + "ワ", "Wa", + "ω", "Omega", + "ω□", "ω□", + "にやり", "Niyari", + "にやり2", "Niyari2", + "にっこり", "Smile", + "ぺろっ", "Pero", + "てへぺろ", "Bero-tehe", + "てへぺろ2", "Bero-tehe2", + "口角上げ", "MouseUP", + "口角下げ", "MouseDW", + "口横広げ", "MouseWD", + "歯無し上", "ToothAnon", + "歯無し下", "ToothBnon", + "真面目", "Serious", + "困る", "Trouble", + "にこり", "Smily", + "怒り", "Get angry", + "上", "UP", + "下", "Down", + }; + private static float progressBar = 0; private void DisplayProgressBar(string text) @@ -992,21 +1053,28 @@ public void CalculateUsedBlendShapePaths() var mesh = skinnedMeshRenderer.sharedMesh; if (mesh == null) continue; - var blendShapeIDs = new List(); - blendShapesToBake[skinnedMeshRenderer] = blendShapeIDs; + var blendShapeIDsToBake = new List(); + blendShapesToBake[skinnedMeshRenderer] = blendShapeIDsToBake; string path = GetPathToRoot(skinnedMeshRenderer) + "/blendShape."; for (int i = 0; i < mesh.blendShapeCount; i++) { - if (skinnedMeshRenderer.GetBlendShapeWeight(i) != 0 && !usedBlendShapes.Contains(path + mesh.GetBlendShapeName(i))) + var name = mesh.GetBlendShapeName(i); + if (KeepMMDBlendShapes && MMDBlendShapes.Contains(name)) + { + usedBlendShapes.Add(path + name); + hasUsedBlendShapes.Add(skinnedMeshRenderer); + continue; + } + if (skinnedMeshRenderer.GetBlendShapeWeight(i) != 0 && !usedBlendShapes.Contains(path + name)) { if (mesh.GetBlendShapeFrameCount(i) > 1) { - usedBlendShapes.Add(path + mesh.GetBlendShapeName(i)); + usedBlendShapes.Add(path + name); hasUsedBlendShapes.Add(skinnedMeshRenderer); } else { - blendShapeIDs.Add(i); + blendShapeIDsToBake.Add(i); } } } @@ -1037,10 +1105,13 @@ public List> FindMergeableBlendShapes(IEn string path = GetPathToRoot(skinnedMeshRenderer) + "/blendShape."; for (int i = 0; i < mesh.blendShapeCount; i++) { + var name = mesh.GetBlendShapeName(i); + if (KeepMMDBlendShapes && MMDBlendShapes.Contains(name)) + continue; if (mesh.GetBlendShapeFrameCount(i) == 1) { - validPaths.Add(path + mesh.GetBlendShapeName(i)); - ratios[0][path + mesh.GetBlendShapeName(i)] = skinnedMeshRenderer.GetBlendShapeWeight(i); + validPaths.Add(path + name); + ratios[0][path + name] = skinnedMeshRenderer.GetBlendShapeWeight(i); } } } diff --git a/Editor/d4rkAvatarOptimizerEditor.cs b/Editor/d4rkAvatarOptimizerEditor.cs index def9c97..143d7f9 100644 --- a/Editor/d4rkAvatarOptimizerEditor.cs +++ b/Editor/d4rkAvatarOptimizerEditor.cs @@ -926,6 +926,7 @@ public override void OnInspectorGUI() GUI.enabled = true; Toggle("Merge Same Ratio Blend Shapes", ref optimizer.MergeSameRatioBlendShapes); Toggle("Merge Simple Toggles as BlendTree", ref optimizer.MergeSimpleTogglesAsBlendTree); + Toggle("Keep MMD Blend Shapes", ref optimizer.KeepMMDBlendShapes); Toggle("Delete Unused Components", ref optimizer.DeleteUnusedComponents); Toggle("Delete Unused GameObjects", ref optimizer.DeleteUnusedGameObjects); Toggle("Use Ring Finger as Foot Collider", ref optimizer.UseRingFingerAsFootCollider); diff --git a/README.md b/README.md index f8d98b5..4ab4e45 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ For example you have two animations. The first animates `A` to 100, `B` to 50 an ## Merge Simple Toggles as BlendTree Tries to find layers in the FXLayer that have exactly two states with one transition each that has a simple bool condition. The optimizer will then merge all layers like that into one by using a large direct blend tree. You can read about this technique [here](https://notes.sleightly.dev/dbt-combining/). +## Keep MMD Blend Shapes +When enabled the optimizer will keep the blend shapes that are used by MMD animations from getting removed or merged. ## Delete Unused Components Deletes all components that are turned off and never get enabled by animations. It also deletes phys bone colliders that are not referenced by any used phys bone components. ## Delete Unused Game Objects diff --git a/package.json b/package.json index 86d705f..ed24197 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "d4rkpl4y3r.d4rkavataroptimizer", "displayName": "d4rkAvatarOptimizer", - "version": "2.0.1", + "version": "2.1.0-alpha.0", "unity": "2019.4", "description": "An optimizer aiming to reduce mesh & material count and more of VRChat 3.0 avatars.", "dependencies": {},