using System; using nadena.dev.modular_avatar.animation; using nadena.dev.modular_avatar.editor.ErrorReporting; using nadena.dev.ndmf; using nadena.dev.ndmf.fluent; using UnityEngine; [assembly: ExportsPlugin( typeof(nadena.dev.modular_avatar.core.editor.plugin.PluginDefinition) )] namespace nadena.dev.modular_avatar.core.editor.plugin { class PluginDefinition : Plugin { public override string QualifiedName => "nadena.dev.modular-avatar"; public override string DisplayName => "Modular Avatar"; protected override void OnUnhandledException(Exception e) { BuildReport.LogException(e); } protected override void Configure() { Sequence seq = InPhase(BuildPhase.Resolving); seq.Run(ResolveObjectReferences.Instance); // Protect against accidental destructive edits by cloning the input controllers ASAP seq.Run("Clone animators", AnimationUtil.CloneAllControllers); seq = InPhase(BuildPhase.Transforming); seq.WithRequiredExtension(typeof(ModularAvatarContext), _s1 => { seq.Run(ClearEditorOnlyTags.Instance); seq.Run(MeshSettingsPluginPass.Instance); seq.Run(RenameParametersPluginPass.Instance); seq.Run(MergeAnimatorPluginPass.Instance); seq.Run(MenuInstallPluginPass.Instance); seq.WithRequiredExtension(typeof(AnimationServicesContext), _s2 => { seq.Run(MergeArmaturePluginPass.Instance); seq.Run(BoneProxyPluginPass.Instance); seq.Run(VisibleHeadAccessoryPluginPass.Instance); seq.Run("World Fixed Object", ctx => new WorldFixedObjectProcessor(ctx.AvatarDescriptor).Process(ctx) ); seq.Run(ReplaceObjectPluginPass.Instance); seq.Run(BlendshapeSyncAnimationPluginPass.Instance); }); seq.Run(PhysbonesBlockerPluginPass.Instance); seq.Run("Fixup Expressions Menu", ctx => { var maContext = ctx.Extension().BuildContext; FixupExpressionsMenuPass.FixupExpressionsMenu(maContext); }); seq.Run("Rebind humanoid avatar", ctx => { // workaround problem with avatar matching // https://github.com/bdunderscore/modular-avatar/issues/430 var animator = ctx.AvatarRootObject.GetComponent(); if (animator) { var avatar = animator.avatar; animator.avatar = null; // ReSharper disable once Unity.InefficientPropertyAccess animator.avatar = avatar; } }); seq.Run("Purge ModularAvatar components", ctx => { foreach (var component in ctx.AvatarRootTransform.GetComponentsInChildren(true)) { UnityEngine.Object.DestroyImmediate(component); } }); }); InPhase(BuildPhase.Optimizing) .WithRequiredExtension(typeof(ModularAvatarContext), s => s.Run(GCGameObjectsPluginPass.Instance)); } } /// /// This plugin runs very early in order to resolve all AvatarObjectReferences to their /// referent before any other plugins perform heirarchy manipulations. /// internal class ResolveObjectReferences : Pass { protected override void Execute(ndmf.BuildContext context) { foreach (var obj in context.AvatarRootObject.GetComponentsInChildren()) { obj.ResolveReferences(); } } } abstract class MAPass : Pass where T : Pass, new() { protected BuildContext MAContext(ndmf.BuildContext context) { return context.Extension().BuildContext; } } class ClearEditorOnlyTags : MAPass { protected override void Execute(ndmf.BuildContext context) { Traverse(context.AvatarRootTransform); } void Traverse(Transform obj) { // EditorOnly objects can be used for multiple purposes - users might want a camera rig to be available in // play mode, for example. For now, we'll prune MA components from EditorOnly objects, but otherwise leave // them in place when in play mode. if (obj.CompareTag("EditorOnly")) { foreach (var component in obj.GetComponentsInChildren(true)) { UnityEngine.Object.DestroyImmediate(component); } } else { foreach (Transform transform in obj) { Traverse(transform); } } } } class MeshSettingsPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new MeshSettingsPass(MAContext(context)).OnPreprocessAvatar(); } } class RenameParametersPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new RenameParametersHook().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); } } class MergeAnimatorPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new MergeAnimatorProcessor().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); } } class MenuInstallPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new MenuInstallHook().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); } } class MergeArmaturePluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new MergeArmatureHook().OnPreprocessAvatar(context, context.AvatarRootObject); } } class BoneProxyPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new BoneProxyProcessor().OnPreprocessAvatar(context.AvatarRootObject); } } class VisibleHeadAccessoryPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new VisibleHeadAccessoryProcessor(context.AvatarDescriptor).Process(MAContext(context)); } } class ReplaceObjectPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new ReplaceObjectPass(context).Process(); } } class BlendshapeSyncAnimationPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); } } class PhysbonesBlockerPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { PhysboneBlockerPass.Process(context.AvatarRootObject); } } class GCGameObjectsPluginPass : MAPass { protected override void Execute(ndmf.BuildContext context) { new GCGameObjectsPass(MAContext(context), context.AvatarRootObject).OnPreprocessAvatar(); } } }