2024-03-14 21:33:44 +09:00
|
|
|
|
#region
|
|
|
|
|
|
|
|
|
|
using System;
|
2023-09-24 14:37:10 +09:00
|
|
|
|
using nadena.dev.modular_avatar.animation;
|
2024-03-14 21:33:44 +09:00
|
|
|
|
using nadena.dev.modular_avatar.core.editor.plugin;
|
2023-09-20 19:24:36 +09:00
|
|
|
|
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
|
|
|
|
using nadena.dev.ndmf;
|
2025-02-16 22:25:12 -08:00
|
|
|
|
using nadena.dev.ndmf.animator;
|
2023-09-10 17:14:19 +09:00
|
|
|
|
using nadena.dev.ndmf.fluent;
|
2025-02-16 22:25:12 -08:00
|
|
|
|
using nadena.dev.ndmf.util;
|
2023-08-05 15:47:03 +09:00
|
|
|
|
using UnityEngine;
|
2024-03-14 21:33:44 +09:00
|
|
|
|
using Object = UnityEngine.Object;
|
|
|
|
|
|
|
|
|
|
#endregion
|
2023-08-05 15:47:03 +09:00
|
|
|
|
|
|
|
|
|
[assembly: ExportsPlugin(
|
2024-03-14 21:33:44 +09:00
|
|
|
|
typeof(PluginDefinition)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
)]
|
|
|
|
|
|
|
|
|
|
namespace nadena.dev.modular_avatar.core.editor.plugin
|
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class PluginDefinition : Plugin<PluginDefinition>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
public override string QualifiedName => "nadena.dev.modular-avatar";
|
2023-09-10 17:14:19 +09:00
|
|
|
|
public override string DisplayName => "Modular Avatar";
|
2023-12-21 17:38:46 +09:00
|
|
|
|
public override Texture2D LogoTexture => LogoDisplay.LOGO_ASSET;
|
2023-09-17 18:16:27 +09:00
|
|
|
|
|
2024-03-14 21:33:44 +09:00
|
|
|
|
// 00a0e9
|
|
|
|
|
public override Color? ThemeColor => new Color(0x00 / 255f, 0xa0 / 255f, 0xe9 / 255f, 1);
|
|
|
|
|
|
2023-09-20 19:24:36 +09:00
|
|
|
|
protected override void OnUnhandledException(Exception e)
|
|
|
|
|
{
|
|
|
|
|
BuildReport.LogException(e);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Configure()
|
|
|
|
|
{
|
2023-09-17 18:16:27 +09:00
|
|
|
|
Sequence seq = InPhase(BuildPhase.Resolving);
|
|
|
|
|
seq.Run(ResolveObjectReferences.Instance);
|
2023-08-05 15:47:03 +09:00
|
|
|
|
|
2025-03-30 16:00:12 -07:00
|
|
|
|
// Protect against accidental destructive edits by cloning the input controllers ASAP
|
|
|
|
|
seq.WithRequiredExtension(typeof(AnimatorServicesContext), s =>
|
|
|
|
|
{
|
|
|
|
|
// Just activating the context is enough.
|
|
|
|
|
s.Run("Clone animators", _ => { });
|
|
|
|
|
});
|
|
|
|
|
|
2023-09-17 18:16:27 +09:00
|
|
|
|
seq = InPhase(BuildPhase.Transforming);
|
2023-12-26 16:10:45 +09:00
|
|
|
|
seq.Run("Validate configuration",
|
2023-12-21 17:38:46 +09:00
|
|
|
|
context => ComponentValidation.ValidateAll(context.AvatarRootObject));
|
2023-09-10 17:14:19 +09:00
|
|
|
|
seq.WithRequiredExtension(typeof(ModularAvatarContext), _s1 =>
|
|
|
|
|
{
|
|
|
|
|
seq.Run(ClearEditorOnlyTags.Instance);
|
2025-03-22 19:51:23 -07:00
|
|
|
|
seq.Run(VRChatSettingsPass.Instance);
|
2023-09-10 17:14:19 +09:00
|
|
|
|
seq.Run(MeshSettingsPluginPass.Instance);
|
2024-06-03 09:52:08 +09:00
|
|
|
|
seq.Run(ScaleAdjusterPass.Instance).PreviewingWith(new ScaleAdjusterPreview());
|
2025-02-16 22:25:12 -08:00
|
|
|
|
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2024-08-10 18:03:50 -07:00
|
|
|
|
seq.Run(ReactiveObjectPrepass.Instance);
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#endif
|
2025-02-16 22:25:12 -08:00
|
|
|
|
seq.WithRequiredExtension(typeof(AnimatorServicesContext), _s2 =>
|
2023-09-10 17:14:19 +09:00
|
|
|
|
{
|
2024-10-20 03:58:41 +02:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2025-03-14 20:44:50 -07:00
|
|
|
|
seq.Run(MMDRelayEarlyPass.Instance);
|
2025-02-16 22:25:12 -08:00
|
|
|
|
seq.Run(RenameParametersPluginPass.Instance);
|
|
|
|
|
seq.Run(ParameterAssignerPass.Instance);
|
|
|
|
|
seq.Run(MergeBlendTreePass.Instance);
|
|
|
|
|
seq.Run(MergeAnimatorPluginPass.Instance);
|
|
|
|
|
seq.Run(ApplyAnimatorDefaultValuesPass.Instance);
|
|
|
|
|
|
|
|
|
|
seq.WithRequiredExtension(typeof(ReadablePropertyExtension), _s3 =>
|
|
|
|
|
{
|
|
|
|
|
seq.Run("Shape Changer", ctx => new ReactiveObjectPass(ctx).Execute())
|
|
|
|
|
.PreviewingWith(new ShapeChangerPreview(), new ObjectSwitcherPreview(),
|
|
|
|
|
new MaterialSetterPreview());
|
|
|
|
|
});
|
|
|
|
|
seq.Run(GameObjectDelayDisablePass.Instance);
|
2024-10-20 03:58:41 +02:00
|
|
|
|
|
2024-10-01 19:52:25 -07:00
|
|
|
|
// TODO: We currently run this above MergeArmaturePlugin, because Merge Armature might destroy
|
|
|
|
|
// game objects which contain Menu Installers. It'd probably be better however to teach Merge Armature
|
|
|
|
|
// to retain those objects? maybe?
|
|
|
|
|
seq.Run(MenuInstallPluginPass.Instance);
|
|
|
|
|
#endif
|
2023-09-10 17:14:19 +09:00
|
|
|
|
seq.Run(MergeArmaturePluginPass.Instance);
|
2025-02-16 22:25:12 -08:00
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
seq.Run(BoneProxyPluginPass.Instance);
|
2025-02-16 22:25:12 -08:00
|
|
|
|
|
2024-10-20 03:58:41 +02:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2023-09-10 17:14:19 +09:00
|
|
|
|
seq.Run(VisibleHeadAccessoryPluginPass.Instance);
|
2024-10-20 03:58:41 +02:00
|
|
|
|
#endif
|
2025-02-16 22:25:12 -08:00
|
|
|
|
|
2023-09-24 16:59:02 +09:00
|
|
|
|
seq.Run("World Fixed Object",
|
2023-10-15 18:44:53 +09:00
|
|
|
|
ctx => new WorldFixedObjectProcessor().Process(ctx)
|
2023-09-24 16:59:02 +09:00
|
|
|
|
);
|
2025-03-10 19:11:45 -07:00
|
|
|
|
seq.Run(WorldScaleObjectPass.Instance);
|
2025-02-16 22:25:12 -08:00
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
seq.Run(ReplaceObjectPluginPass.Instance);
|
2025-02-16 22:25:12 -08:00
|
|
|
|
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2023-10-01 00:09:43 +09:00
|
|
|
|
seq.Run(BlendshapeSyncAnimationPluginPass.Instance);
|
2024-10-20 03:58:41 +02:00
|
|
|
|
#endif
|
2025-02-16 22:25:12 -08:00
|
|
|
|
|
2024-08-16 18:52:04 -07:00
|
|
|
|
seq.Run(ConstraintConverterPass.Instance);
|
2025-02-16 22:25:12 -08:00
|
|
|
|
|
|
|
|
|
seq.Run("Prune empty animator layers",
|
|
|
|
|
ctx => { ctx.Extension<AnimatorServicesContext>().RemoveEmptyLayers(); });
|
|
|
|
|
seq.Run("Harmonize animator parameter types",
|
|
|
|
|
ctx => { ctx.Extension<AnimatorServicesContext>().HarmonizeParameterTypes(); });
|
2025-03-14 20:44:50 -07:00
|
|
|
|
|
|
|
|
|
seq.Run(MMDRelayPass.Instance);
|
2023-09-10 17:14:19 +09:00
|
|
|
|
});
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2023-09-17 18:16:27 +09:00
|
|
|
|
seq.Run(PhysbonesBlockerPluginPass.Instance);
|
2023-09-17 22:21:04 +09:00
|
|
|
|
seq.Run("Fixup Expressions Menu", ctx =>
|
|
|
|
|
{
|
|
|
|
|
var maContext = ctx.Extension<ModularAvatarContext>().BuildContext;
|
|
|
|
|
FixupExpressionsMenuPass.FixupExpressionsMenu(maContext);
|
|
|
|
|
});
|
2024-12-01 12:30:49 -08:00
|
|
|
|
seq.Run(SyncParameterSequencePass.Instance);
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#endif
|
2024-12-01 12:30:49 -08:00
|
|
|
|
seq.Run(RemoveVertexColorPass.Instance).PreviewingWith(new RemoveVertexColorPreview());
|
2024-09-01 08:54:48 +09:00
|
|
|
|
seq.Run(RebindHumanoidAvatarPass.Instance);
|
2023-09-20 19:24:36 +09:00
|
|
|
|
seq.Run("Purge ModularAvatar components", ctx =>
|
|
|
|
|
{
|
|
|
|
|
foreach (var component in ctx.AvatarRootTransform.GetComponentsInChildren<AvatarTagComponent>(true))
|
|
|
|
|
{
|
2024-03-14 21:33:44 +09:00
|
|
|
|
Object.DestroyImmediate(component);
|
2024-03-09 17:57:15 +09:00
|
|
|
|
}
|
2024-03-14 21:33:44 +09:00
|
|
|
|
foreach (var component in ctx.AvatarRootTransform.GetComponentsInChildren<MAMoveIndependently>(true))
|
2024-03-09 17:57:15 +09:00
|
|
|
|
{
|
2024-03-14 21:33:44 +09:00
|
|
|
|
Object.DestroyImmediate(component);
|
2023-09-20 19:24:36 +09:00
|
|
|
|
}
|
|
|
|
|
});
|
2024-02-12 14:59:23 +09:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2023-12-26 16:10:45 +09:00
|
|
|
|
seq.Run(PruneParametersPass.Instance);
|
2024-02-12 14:59:23 +09:00
|
|
|
|
#endif
|
2023-09-10 17:14:19 +09:00
|
|
|
|
});
|
2023-09-17 18:16:27 +09:00
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
InPhase(BuildPhase.Optimizing)
|
|
|
|
|
.WithRequiredExtension(typeof(ModularAvatarContext),
|
|
|
|
|
s => s.Run(GCGameObjectsPluginPass.Instance));
|
|
|
|
|
}
|
2023-08-05 15:47:03 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This plugin runs very early in order to resolve all AvatarObjectReferences to their
|
|
|
|
|
/// referent before any other plugins perform heirarchy manipulations.
|
|
|
|
|
/// </summary>
|
2023-09-10 17:14:19 +09:00
|
|
|
|
internal class ResolveObjectReferences : Pass<ResolveObjectReferences>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
foreach (var obj in context.AvatarRootObject.GetComponentsInChildren<AvatarTagComponent>())
|
|
|
|
|
{
|
|
|
|
|
obj.ResolveReferences();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
abstract class MAPass<T> : Pass<T> where T : Pass<T>, new()
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
protected BuildContext MAContext(ndmf.BuildContext context)
|
|
|
|
|
{
|
|
|
|
|
return context.Extension<ModularAvatarContext>().BuildContext;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class ClearEditorOnlyTags : MAPass<ClearEditorOnlyTags>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
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<AvatarTagComponent>(true))
|
|
|
|
|
{
|
2024-03-14 21:33:44 +09:00
|
|
|
|
Object.DestroyImmediate(component);
|
2023-08-05 15:47:03 +09:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
foreach (Transform transform in obj)
|
|
|
|
|
{
|
|
|
|
|
Traverse(transform);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class MeshSettingsPluginPass : MAPass<MeshSettingsPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
new MeshSettingsPass(MAContext(context)).OnPreprocessAvatar();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class RenameParametersPluginPass : MAPass<RenameParametersPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
new RenameParametersHook().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class MergeAnimatorPluginPass : MAPass<MergeAnimatorPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
new MergeAnimatorProcessor().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class MenuInstallPluginPass : MAPass<MenuInstallPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
new MenuInstallHook().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context));
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#endif
|
2023-08-05 15:47:03 +09:00
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class MergeArmaturePluginPass : MAPass<MergeArmaturePluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
new MergeArmatureHook().OnPreprocessAvatar(context, context.AvatarRootObject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class BoneProxyPluginPass : MAPass<BoneProxyPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
new BoneProxyProcessor().OnPreprocessAvatar(context.AvatarRootObject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-20 03:58:41 +02:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class VisibleHeadAccessoryPluginPass : MAPass<VisibleHeadAccessoryPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-10-15 18:44:53 +09:00
|
|
|
|
new VisibleHeadAccessoryProcessor(MAContext(context)).Process();
|
2023-08-05 15:47:03 +09:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-20 03:58:41 +02:00
|
|
|
|
#endif
|
2023-08-05 15:47:03 +09:00
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class ReplaceObjectPluginPass : MAPass<ReplaceObjectPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
new ReplaceObjectPass(context).Process();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#if MA_VRCSDK3_AVATARS
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class BlendshapeSyncAnimationPluginPass : MAPass<BlendshapeSyncAnimationPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2025-02-16 22:25:12 -08:00
|
|
|
|
new BlendshapeSyncAnimationProcessor(context).OnPreprocessAvatar();
|
2023-08-05 15:47:03 +09:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class PhysbonesBlockerPluginPass : MAPass<PhysbonesBlockerPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
PhysboneBlockerPass.Process(context.AvatarRootObject);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-10 15:37:56 +09:00
|
|
|
|
#endif
|
2023-08-05 15:47:03 +09:00
|
|
|
|
|
2024-09-01 08:54:48 +09:00
|
|
|
|
class RebindHumanoidAvatarPass : MAPass<RebindHumanoidAvatarPass>
|
|
|
|
|
{
|
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
|
|
|
|
{
|
|
|
|
|
new RebindHumanoidAvatar(context).Process();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 17:14:19 +09:00
|
|
|
|
class GCGameObjectsPluginPass : MAPass<GCGameObjectsPluginPass>
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
2023-09-10 17:14:19 +09:00
|
|
|
|
protected override void Execute(ndmf.BuildContext context)
|
2023-08-05 15:47:03 +09:00
|
|
|
|
{
|
|
|
|
|
new GCGameObjectsPass(MAContext(context), context.AvatarRootObject).OnPreprocessAvatar();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|