diff --git a/CHANGELOG-PRERELEASE.md b/CHANGELOG-PRERELEASE.md index 60f63d20a..afe3f9043 100644 --- a/CHANGELOG-PRERELEASE.md +++ b/CHANGELOG-PRERELEASE.md @@ -11,6 +11,10 @@ The format is based on [Keep a Changelog]. - Support for Multi Frame BlendShapes `#333` ### Changed +- Auto FreezeBlendShape now freezes meaningless BlendShape `#334` + - If you removed some vertices with RemoveMeshInBox or RemoveMeshWithBlendShape, some BlendShape may transform no vertices + - Auto FreeseBlendShae now freezez such a BlendShapes +- Auto FreezeBlendShape now freezes vertices even if already FreezeBlendShape is configured. `#334` ### Deprecated diff --git a/CHANGELOG.md b/CHANGELOG.md index bb5da74d0..d70962484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ The format is based on [Keep a Changelog]. - Support for Multi Frame BlendShapes `#333` ### Changed +- Auto FreezeBlendShape now freezes meaningless BlendShape `#334` + - If you removed some vertices with RemoveMeshInBox or RemoveMeshWithBlendShape, some BlendShape may transform no vertices + - Auto FreeseBlendShae now freezez such a BlendShapes +- Auto FreezeBlendShape now freezes vertices even if already FreezeBlendShape is configured. `#334` ### Deprecated diff --git a/Editor/EditSkinnedMeshComponentUtil.cs b/Editor/EditSkinnedMeshComponentUtil.cs index 90df5dd73..176ab5276 100644 --- a/Editor/EditSkinnedMeshComponentUtil.cs +++ b/Editor/EditSkinnedMeshComponentUtil.cs @@ -231,6 +231,7 @@ private T CheckRecursive(Func compute) [typeof(MergeToonLitMaterial)] = x => new MergeToonLitMaterialProcessor((MergeToonLitMaterial)x), [typeof(RemoveMeshInBox)] = x => new RemoveMeshInBoxProcessor((RemoveMeshInBox)x), [typeof(RemoveMeshByBlendShape)] = x => new RemoveMeshByBlendShapeProcessor((RemoveMeshByBlendShape)x), + [typeof(InternalAutoFreezeMeaninglessBlendShape)] = x => new InternalAutoFreezeMeaninglessBlendShapeProcessor((InternalAutoFreezeMeaninglessBlendShape)x), }; private static IEditSkinnedMeshProcessor CreateProcessor(EditSkinnedMeshComponent mergePhysBone) => diff --git a/Editor/Processors/SkinnedMeshes/EditSkinnedMeshProcessor.cs b/Editor/Processors/SkinnedMeshes/EditSkinnedMeshProcessor.cs index c227e4e66..49d321a11 100644 --- a/Editor/Processors/SkinnedMeshes/EditSkinnedMeshProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/EditSkinnedMeshProcessor.cs @@ -9,7 +9,7 @@ namespace Anatawa12.AvatarOptimizer.Processors.SkinnedMeshes internal abstract class EditSkinnedMeshProcessor : IEditSkinnedMeshProcessor where TComponent : EditSkinnedMeshComponent { - public abstract int ProcessOrder { get; } + public abstract EditSkinnedMeshProcessorOrder ProcessOrder { get; } public virtual IEnumerable Dependencies => Array.Empty(); protected TComponent Component { get; } public SkinnedMeshRenderer Target { get; } @@ -38,7 +38,7 @@ public override bool Equals(object obj) => internal interface IEditSkinnedMeshProcessor { - int ProcessOrder { get; } + EditSkinnedMeshProcessorOrder ProcessOrder { get; } IEnumerable Dependencies { get; } SkinnedMeshRenderer Target { get; } EditSkinnedMeshComponent Component { get; } @@ -47,6 +47,14 @@ internal interface IEditSkinnedMeshProcessor [NotNull] IMeshInfoComputer GetComputer([NotNull] IMeshInfoComputer upstream); } + enum EditSkinnedMeshProcessorOrder : int + { + Generation = int.MinValue, + RemovingMesh = -20000, + AutoConfigureFreezeBlendShape = -10000 - 1, + AfterRemoveMesh = -10000, + } + internal interface IMeshInfoComputer { (string name, float weight)[] BlendShapes(); diff --git a/Editor/Processors/SkinnedMeshes/FreezeBlendShapeProcessor.cs b/Editor/Processors/SkinnedMeshes/FreezeBlendShapeProcessor.cs index 19b8cffbc..5ecf7d493 100644 --- a/Editor/Processors/SkinnedMeshes/FreezeBlendShapeProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/FreezeBlendShapeProcessor.cs @@ -11,7 +11,7 @@ public FreezeBlendShapeProcessor(FreezeBlendShape component) : base(component) { } - public override int ProcessOrder => -10000; + public override EditSkinnedMeshProcessorOrder ProcessOrder => EditSkinnedMeshProcessorOrder.AfterRemoveMesh; public override void Process(OptimizerSession session, MeshInfo2 target, MeshInfo2Holder meshInfo2Holder) { diff --git a/Editor/Processors/SkinnedMeshes/InternalAutoFreezeMeaninglessBlendShapeProcessor.cs b/Editor/Processors/SkinnedMeshes/InternalAutoFreezeMeaninglessBlendShapeProcessor.cs new file mode 100644 index 000000000..c87176d58 --- /dev/null +++ b/Editor/Processors/SkinnedMeshes/InternalAutoFreezeMeaninglessBlendShapeProcessor.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor; + +namespace Anatawa12.AvatarOptimizer.Processors.SkinnedMeshes +{ + internal class InternalAutoFreezeMeaninglessBlendShapeProcessor : EditSkinnedMeshProcessor + { + public InternalAutoFreezeMeaninglessBlendShapeProcessor(InternalAutoFreezeMeaninglessBlendShape component) : base(component) + { + } + + public override EditSkinnedMeshProcessorOrder ProcessOrder => EditSkinnedMeshProcessorOrder.AfterRemoveMesh; + + public override void Process(OptimizerSession session, MeshInfo2 target, MeshInfo2Holder meshInfo2Holder) + { + var meaningfulBlendShapes = new HashSet(); + + foreach (var vertex in target.Vertices) + foreach (var kvp in vertex.BlendShapes.Where(kvp => kvp.Value != default)) + meaningfulBlendShapes.Add(kvp.Key); + + var freezeBlendShape = Target.GetComponent(); + var serialized = new SerializedObject(freezeBlendShape); + var editorUtil = PrefabSafeSet.EditorUtil.Create( + serialized.FindProperty(nameof(FreezeBlendShape.shapeKeysSet)), + 0, p => p.stringValue, (p, v) => p.stringValue = v); + foreach (var (meaningLess, _) in target.BlendShapes.Where(x => !meaningfulBlendShapes.Contains(x.name))) + editorUtil.GetElementOf(meaningLess).EnsureAdded(); + serialized.ApplyModifiedPropertiesWithoutUndo(); + } + + // nothing to do + public override IMeshInfoComputer GetComputer(IMeshInfoComputer upstream) => upstream; + } +} diff --git a/Editor/Processors/SkinnedMeshes/InternalAutoFreezeMeaninglessBlendShapeProcessor.cs.meta b/Editor/Processors/SkinnedMeshes/InternalAutoFreezeMeaninglessBlendShapeProcessor.cs.meta new file mode 100644 index 000000000..2dca8a6df --- /dev/null +++ b/Editor/Processors/SkinnedMeshes/InternalAutoFreezeMeaninglessBlendShapeProcessor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7f44112e897747efaed7973f28534c4e +timeCreated: 1692455976 \ No newline at end of file diff --git a/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs b/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs index 3e353524e..a25f02f51 100644 --- a/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs @@ -15,7 +15,7 @@ public MergeSkinnedMeshProcessor(MergeSkinnedMesh component) : base(component) public override IEnumerable Dependencies => Component.renderersSet.GetAsList(); - public override int ProcessOrder => int.MinValue; + public override EditSkinnedMeshProcessorOrder ProcessOrder => EditSkinnedMeshProcessorOrder.Generation; public override void Process(OptimizerSession session, MeshInfo2 target, MeshInfo2Holder meshInfo2Holder) { diff --git a/Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs b/Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs index 73fe8367e..d56cbea29 100644 --- a/Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs @@ -24,7 +24,7 @@ public MergeToonLitMaterialProcessor(MergeToonLitMaterial component) : base(comp { } - public override int ProcessOrder => -10000; + public override EditSkinnedMeshProcessorOrder ProcessOrder => EditSkinnedMeshProcessorOrder.AfterRemoveMesh; public override void Process(OptimizerSession session, MeshInfo2 target, MeshInfo2Holder meshInfo2Holder) { diff --git a/Editor/Processors/SkinnedMeshes/RemoveMeshByBlendShapeProcessor.cs b/Editor/Processors/SkinnedMeshes/RemoveMeshByBlendShapeProcessor.cs index 7dc461ba2..02eafecc0 100644 --- a/Editor/Processors/SkinnedMeshes/RemoveMeshByBlendShapeProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/RemoveMeshByBlendShapeProcessor.cs @@ -10,7 +10,7 @@ public RemoveMeshByBlendShapeProcessor(RemoveMeshByBlendShape component) : base( } // This needs to be less than FreezeBlendshapeProcessor.ProcessOrder. - public override int ProcessOrder => -10001; + public override EditSkinnedMeshProcessorOrder ProcessOrder => EditSkinnedMeshProcessorOrder.RemovingMesh; public override void Process(OptimizerSession session, MeshInfo2 target, MeshInfo2Holder meshInfo2Holder) { diff --git a/Editor/Processors/SkinnedMeshes/RemoveMeshInBoxProcessor.cs b/Editor/Processors/SkinnedMeshes/RemoveMeshInBoxProcessor.cs index 6780969de..6cd465c90 100644 --- a/Editor/Processors/SkinnedMeshes/RemoveMeshInBoxProcessor.cs +++ b/Editor/Processors/SkinnedMeshes/RemoveMeshInBoxProcessor.cs @@ -10,7 +10,7 @@ public RemoveMeshInBoxProcessor(RemoveMeshInBox component) : base(component) { } - public override int ProcessOrder => -10000; + public override EditSkinnedMeshProcessorOrder ProcessOrder => EditSkinnedMeshProcessorOrder.RemovingMesh; public override void Process(OptimizerSession session, MeshInfo2 target, MeshInfo2Holder meshInfo2Holder) { diff --git a/Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs b/Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs index d678ae76e..314d9c5eb 100644 --- a/Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs +++ b/Editor/Processors/TraceAndOptimize/AutoFreezeBlendShape.cs @@ -17,14 +17,13 @@ public AutoFreezeBlendShape(AnimatorParser animator, OptimizerSession session) public void Process() { + // first optimization: unused blend shapes foreach (var skinnedMeshRenderer in _session.GetComponents()) { var mesh = skinnedMeshRenderer.sharedMesh; // skip SMR without mesh if (!mesh) continue; - // skip configured mesh - if (skinnedMeshRenderer.GetComponent()) continue; var modifies = _animator.GetModifiedProperties(skinnedMeshRenderer); var blendShapeValues = Enumerable.Range(0, mesh.blendShapeCount) @@ -53,7 +52,7 @@ public void Process() skinnedMeshRenderer.SetBlendShapeWeight(i, blendShapeValues[i]); EditorUtility.SetDirty(skinnedMeshRenderer); - var freeze = skinnedMeshRenderer.gameObject.AddComponent(); + var freeze = skinnedMeshRenderer.gameObject.GetOrAddComponent(); var serialized = new SerializedObject(freeze); var editorUtil = PrefabSafeSet.EditorUtil.Create( serialized.FindProperty(nameof(FreezeBlendShape.shapeKeysSet)), @@ -62,6 +61,13 @@ public void Process() editorUtil.GetElementOf(shape).EnsureAdded(); serialized.ApplyModifiedPropertiesWithoutUndo(); } + + // second optimization: remove meaningless blendShapes + foreach (var skinnedMeshRenderer in _session.GetComponents()) + { + skinnedMeshRenderer.gameObject.GetOrAddComponent(); + skinnedMeshRenderer.gameObject.GetOrAddComponent(); + } } } -} \ No newline at end of file +} diff --git a/Editor/Utils.cs b/Editor/Utils.cs index cc7082acd..9df8a85a6 100644 --- a/Editor/Utils.cs +++ b/Editor/Utils.cs @@ -160,6 +160,13 @@ public static void FillArray(T[] array, T value) array[i] = value; } + public static T GetOrAddComponent(this GameObject go) where T : Component + { + var component = go.GetComponent(); + if (!component) component = go.AddComponent(); + return component; + } + public static Transform GetTarget(this VRCPhysBoneBase physBoneBase) => physBoneBase.rootTransform ? physBoneBase.rootTransform : physBoneBase.transform; diff --git a/Runtime/InternalAutoFreezeMeaninglessBlendShape.cs b/Runtime/InternalAutoFreezeMeaninglessBlendShape.cs new file mode 100644 index 000000000..1026ab621 --- /dev/null +++ b/Runtime/InternalAutoFreezeMeaninglessBlendShape.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace Anatawa12.AvatarOptimizer +{ + // TODO move to somewhere else? e.g. in editor module if possible + [AddComponentMenu("")] + [DisallowMultipleComponent] + internal class InternalAutoFreezeMeaninglessBlendShape : EditSkinnedMeshComponent + { + } +} diff --git a/Runtime/InternalAutoFreezeMeaninglessBlendShape.cs.meta b/Runtime/InternalAutoFreezeMeaninglessBlendShape.cs.meta new file mode 100644 index 000000000..98f80314e --- /dev/null +++ b/Runtime/InternalAutoFreezeMeaninglessBlendShape.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e8083fdc1f4f422aa2ed7115cfbe5156 +timeCreated: 1692455932 \ No newline at end of file