From 8cbed0b7299b0d691dd8205d24f29b8c4258ad88 Mon Sep 17 00:00:00 2001 From: bd_ Date: Thu, 28 Nov 2024 19:06:05 -0800 Subject: [PATCH] feat: RemoveMeshByMaterial Closes: #1353 --- .../RemoveMeshByMaterialFilter.cs | 57 ++++++++++++++++ .../RemoveMeshByMaterialFilter.cs.meta | 3 + Editor/EditSkinnedMeshComponentUtil.cs | 1 + .../Inspector/AvatarTagComponentEditorBase.cs | 2 +- .../Inspector/RemoveMeshByMaterialEditor.cs | 13 ++++ .../RemoveMeshByMaterialEditor.cs.meta | 3 + Editor/OptimizerPlugin.cs | 1 + .../RemoveMeshByMaterialProcessor.cs | 35 ++++++++++ .../RemoveMeshByMaterialProcessor.cs.meta | 3 + Runtime/RemoveMeshByMaterial.cs | 68 +++++++++++++++++++ Runtime/RemoveMeshByMaterial.cs.meta | 3 + 11 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 Editor/EditModePreview/RemoveMeshByMaterialFilter.cs create mode 100644 Editor/EditModePreview/RemoveMeshByMaterialFilter.cs.meta create mode 100644 Editor/Inspector/RemoveMeshByMaterialEditor.cs create mode 100644 Editor/Inspector/RemoveMeshByMaterialEditor.cs.meta create mode 100644 Editor/Processors/SkinnedMeshes/RemoveMeshByMaterialProcessor.cs create mode 100644 Editor/Processors/SkinnedMeshes/RemoveMeshByMaterialProcessor.cs.meta create mode 100644 Runtime/RemoveMeshByMaterial.cs create mode 100644 Runtime/RemoveMeshByMaterial.cs.meta diff --git a/Editor/EditModePreview/RemoveMeshByMaterialFilter.cs b/Editor/EditModePreview/RemoveMeshByMaterialFilter.cs new file mode 100644 index 000000000..df562b1a4 --- /dev/null +++ b/Editor/EditModePreview/RemoveMeshByMaterialFilter.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using nadena.dev.ndmf; +using nadena.dev.ndmf.preview; +using UnityEngine; + +namespace Anatawa12.AvatarOptimizer.EditModePreview +{ + internal class RemoveMeshByMaterialRenderFilter : AAORenderFilterBase + { + private RemoveMeshByMaterialRenderFilter() : base("Remove Mesh by Material", "remove-mesh-by-material") + { + } + + public static RemoveMeshByMaterialRenderFilter Instance { get; } = new(); + protected override AAORenderFilterNodeBase CreateNode() => new RemoveMeshByMaterialRendererNode(); + protected override bool SupportsMultiple() => false; + } + + internal class RemoveMeshByMaterialRendererNode : AAORenderFilterNodeBase + { + protected override ValueTask Process( + SkinnedMeshRenderer original, + SkinnedMeshRenderer proxy, + RemoveMeshByMaterial[] components, + Mesh duplicated, + ComputeContext context + ) + { + HashSet toRemove = new(); + foreach (var component in components) + { + var materials = context.Observe(component, c => c.Materials.ToImmutableList()); + foreach (var material in materials) + { + toRemove.Add(ObjectRegistry.GetReference(material)); + } + } + + var actualMaterials = proxy.sharedMaterials ?? Array.Empty(); + for (int i = 0; i < actualMaterials.Length; i++) + { + var mat = actualMaterials[i]; + if (mat == null || !toRemove.Contains(ObjectRegistry.GetReference(mat))) continue; + + var submesh = duplicated.GetSubMesh(i); + submesh.indexCount = 0; + duplicated.SetSubMesh(i, submesh); + } + + return default; + } + } +} diff --git a/Editor/EditModePreview/RemoveMeshByMaterialFilter.cs.meta b/Editor/EditModePreview/RemoveMeshByMaterialFilter.cs.meta new file mode 100644 index 000000000..764a27d67 --- /dev/null +++ b/Editor/EditModePreview/RemoveMeshByMaterialFilter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4239ff757d6648d0ab02f945e8522e65 +timeCreated: 1732849021 \ No newline at end of file diff --git a/Editor/EditSkinnedMeshComponentUtil.cs b/Editor/EditSkinnedMeshComponentUtil.cs index 0cdd9684c..1eb32e807 100644 --- a/Editor/EditSkinnedMeshComponentUtil.cs +++ b/Editor/EditSkinnedMeshComponentUtil.cs @@ -203,6 +203,7 @@ private T CheckRecursive(Func compute) [typeof(MergeSkinnedMesh)] = x => new MergeSkinnedMeshProcessor((MergeSkinnedMesh)x), [typeof(FreezeBlendShape)] = x => new FreezeBlendShapeProcessor((FreezeBlendShape)x), [typeof(MergeToonLitMaterial)] = x => new MergeToonLitMaterialProcessor((MergeToonLitMaterial)x), + [typeof(RemoveMeshByMaterial)] = x => new RemoveMeshByMaterialProcessor((RemoveMeshByMaterial)x), [typeof(RemoveMeshInBox)] = x => new RemoveMeshInBoxProcessor((RemoveMeshInBox)x), [typeof(RemoveMeshByBlendShape)] = x => new RemoveMeshByBlendShapeProcessor((RemoveMeshByBlendShape)x), [typeof(RemoveMeshByMask)] = x => new RemoveMeshByMaskProcessor((RemoveMeshByMask)x), diff --git a/Editor/Inspector/AvatarTagComponentEditorBase.cs b/Editor/Inspector/AvatarTagComponentEditorBase.cs index 42ce52f82..d5c08910f 100644 --- a/Editor/Inspector/AvatarTagComponentEditorBase.cs +++ b/Editor/Inspector/AvatarTagComponentEditorBase.cs @@ -2,7 +2,7 @@ namespace Anatawa12.AvatarOptimizer { - abstract class AvatarTagComponentEditorBase : Editor + public abstract class AvatarTagComponentEditorBase : Editor { public sealed override void OnInspectorGUI() { diff --git a/Editor/Inspector/RemoveMeshByMaterialEditor.cs b/Editor/Inspector/RemoveMeshByMaterialEditor.cs new file mode 100644 index 000000000..9910ee561 --- /dev/null +++ b/Editor/Inspector/RemoveMeshByMaterialEditor.cs @@ -0,0 +1,13 @@ +using UnityEditor; + +namespace Anatawa12.AvatarOptimizer; + +[CustomEditor(typeof(RemoveMeshByMaterial))] +public class RemoveMeshByMaterialEditor : AvatarTagComponentEditorBase +{ + // TODO + protected override void OnInspectorGUIInner() + { + DrawDefaultInspector(); + } +} diff --git a/Editor/Inspector/RemoveMeshByMaterialEditor.cs.meta b/Editor/Inspector/RemoveMeshByMaterialEditor.cs.meta new file mode 100644 index 000000000..d0fd0777d --- /dev/null +++ b/Editor/Inspector/RemoveMeshByMaterialEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 980b753ac4794d05a72912693d989a9f +timeCreated: 1732849435 \ No newline at end of file diff --git a/Editor/OptimizerPlugin.cs b/Editor/OptimizerPlugin.cs index c449e8a2b..2dd3d6ccc 100644 --- a/Editor/OptimizerPlugin.cs +++ b/Editor/OptimizerPlugin.cs @@ -64,6 +64,7 @@ protected override void Configure() .Then.Run(Processors.MergePhysBoneProcessor.Instance) #endif .Then.Run(Processors.EditSkinnedMeshComponentProcessor.Instance) + .PreviewingWith(EditModePreview.RemoveMeshByMaterialRenderFilter.Instance) .PreviewingWith(EditModePreview.RemoveMeshByMaskRenderFilter.Instance) .PreviewingWith(EditModePreview.RemoveMeshByBlendShapeRenderFilter.Instance) .PreviewingWith(EditModePreview.RemoveMeshInBoxRenderFilter.Instance) diff --git a/Editor/Processors/SkinnedMeshes/RemoveMeshByMaterialProcessor.cs b/Editor/Processors/SkinnedMeshes/RemoveMeshByMaterialProcessor.cs new file mode 100644 index 000000000..3f18297c1 --- /dev/null +++ b/Editor/Processors/SkinnedMeshes/RemoveMeshByMaterialProcessor.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using nadena.dev.ndmf; +using UnityEngine; + +namespace Anatawa12.AvatarOptimizer.Processors.SkinnedMeshes; + +internal class RemoveMeshByMaterialProcessor : EditSkinnedMeshProcessor +{ + public RemoveMeshByMaterialProcessor(RemoveMeshByMaterial component) : base(component) + { + } + + public override EditSkinnedMeshProcessorOrder ProcessOrder => EditSkinnedMeshProcessorOrder.RemovingMesh; + public override void Process(BuildContext context, MeshInfo2 target) + { + var submeshes = target.SubMeshes; + var toDeleteMats = new HashSet(Component.Materials.Select(m => ObjectRegistry.GetReference(m))); + Renderer renderer = Component.GetComponent(); + var actualMats = renderer.sharedMaterials ?? Array.Empty(); + + for (int i = 0; i < submeshes.Count; i++) + { + if (i < actualMats.Length && toDeleteMats.Contains(ObjectRegistry.GetReference(actualMats[i]))) + { + submeshes[i].RemovePrimitives("RemoveMeshByMaterial", v => true); + } + } + + target.RemoveUnusedVertices(); + } + + public override IMeshInfoComputer GetComputer(IMeshInfoComputer upstream) => upstream; +} diff --git a/Editor/Processors/SkinnedMeshes/RemoveMeshByMaterialProcessor.cs.meta b/Editor/Processors/SkinnedMeshes/RemoveMeshByMaterialProcessor.cs.meta new file mode 100644 index 000000000..60f7758f3 --- /dev/null +++ b/Editor/Processors/SkinnedMeshes/RemoveMeshByMaterialProcessor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ef3233132b59467c9c08d830c2582bcc +timeCreated: 1732848070 \ No newline at end of file diff --git a/Runtime/RemoveMeshByMaterial.cs b/Runtime/RemoveMeshByMaterial.cs new file mode 100644 index 000000000..4aec07f1e --- /dev/null +++ b/Runtime/RemoveMeshByMaterial.cs @@ -0,0 +1,68 @@ +using System; +using JetBrains.Annotations; +using UnityEngine; + +namespace Anatawa12.AvatarOptimizer +{ + [AddComponentMenu("Avatar Optimizer/AAO Remove Mesh By Material")] + [RequireComponent(typeof(SkinnedMeshRenderer))] + [AllowMultipleComponent] + [HelpURL("https://vpm.anatawa12.com/avatar-optimizer/ja/docs/reference/remove-mesh-by-material/")] + [PublicAPI] + public class RemoveMeshByMaterial : EditSkinnedMeshComponent, ISerializationCallbackReceiver + { + [SerializeField] internal PrefabSafeSet.PrefabSafeSet materials; + + internal RemoveMeshByMaterial() + { + materials = new PrefabSafeSet.PrefabSafeSet(this); + } + + private void ValidatePSUC() + { + PrefabSafeSet.PrefabSafeSet.OnValidate(this, x => x.materials); + } + + private void OnValidate() => ValidatePSUC(); + void ISerializationCallbackReceiver.OnBeforeSerialize() => ValidatePSUC(); + + void ISerializationCallbackReceiver.OnAfterDeserialize() + { + } + + APIChecker _checker; + + /// + /// Initializes the RemoveMEshByBlendShape with the specified default behavior version. + /// + /// As Described in the documentation, you have to call this method after `AddComponent` to make sure + /// the default configuration is what you want. + /// Without calling this method, the default configuration might be changed in the future. + /// + /// + /// The default configuration version. + /// Since 1.7.0, version 1 is supported. + /// + /// Unsupported configuration version + [PublicAPI] + public void Initialize(int version) + { + switch (version) + { + case 1: + // nothing to do + break; + default: + throw new ArgumentOutOfRangeException(nameof(version), $"unsupported version: {version}"); + } + _checker.OnInitialize(version, this); + } + + /// + /// Gets the set of materials to delete from the target mesh. + /// + [PublicAPI] + public API.PrefabSafeSetAccessor Materials => + _checker.OnAPIUsage(this, new API.PrefabSafeSetAccessor(materials)); + } +} diff --git a/Runtime/RemoveMeshByMaterial.cs.meta b/Runtime/RemoveMeshByMaterial.cs.meta new file mode 100644 index 000000000..0d0efcb83 --- /dev/null +++ b/Runtime/RemoveMeshByMaterial.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 249f95f6928f4f41b68c2d401602a3ca +timeCreated: 1732847827 \ No newline at end of file