From 3a5e6ed5bceb14388b0eda177d6ee91c36f8241b Mon Sep 17 00:00:00 2001 From: bd_ Date: Sun, 1 Dec 2024 12:30:49 -0800 Subject: [PATCH] sync param sequence --- Editor/PluginDefinition/PluginDefinition.cs | 1 + Editor/SyncParameterSequencePass.cs | 110 ++++++++++++++++++ Editor/SyncParameterSequencePass.cs.meta | 3 + Runtime/ModularAvatarSyncParameterSequence.cs | 23 ++++ ...ModularAvatarSyncParameterSequence.cs.meta | 3 + 5 files changed, 140 insertions(+) create mode 100644 Editor/SyncParameterSequencePass.cs create mode 100644 Editor/SyncParameterSequencePass.cs.meta create mode 100644 Runtime/ModularAvatarSyncParameterSequence.cs create mode 100644 Runtime/ModularAvatarSyncParameterSequence.cs.meta diff --git a/Editor/PluginDefinition/PluginDefinition.cs b/Editor/PluginDefinition/PluginDefinition.cs index d6c58b19..f855b8b3 100644 --- a/Editor/PluginDefinition/PluginDefinition.cs +++ b/Editor/PluginDefinition/PluginDefinition.cs @@ -89,6 +89,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin FixupExpressionsMenuPass.FixupExpressionsMenu(maContext); }); seq.Run(RemoveVertexColorPass.Instance); + seq.Run(SyncParameterSequencePass.Instance); #endif seq.Run(RebindHumanoidAvatarPass.Instance); seq.Run("Purge ModularAvatar components", ctx => diff --git a/Editor/SyncParameterSequencePass.cs b/Editor/SyncParameterSequencePass.cs new file mode 100644 index 00000000..d5973092 --- /dev/null +++ b/Editor/SyncParameterSequencePass.cs @@ -0,0 +1,110 @@ +#nullable enable + +using System; +using System.Collections.Specialized; +using System.Linq; +using nadena.dev.modular_avatar.editor.ErrorReporting; +using nadena.dev.ndmf; +using UnityEditor; +using VRC.SDK3.Avatars.ScriptableObjects; + +namespace nadena.dev.modular_avatar.core.editor +{ + public class SyncParameterSequencePass : Pass + { + private static Platform? CurrentPlatform + { + get + { + switch (EditorUserBuildSettings.activeBuildTarget) + { + case BuildTarget.Android: return Platform.Android; + case BuildTarget.iOS: return Platform.iOS; + case BuildTarget.StandaloneWindows64: return Platform.PC; + default: return null; + } + } + } + + protected override void Execute(ndmf.BuildContext context) + { + var avDesc = context.AvatarDescriptor; + + var components = context.AvatarRootObject.GetComponentsInChildren(true); + if (components.Length == 0) return; + if (components.Length > 1) + { + BuildReport.LogFatal("ma.error.TODO.sps.multiple_components", components.Cast().ToArray()); + return; + } + + var syncComponent = components[0]; + if (syncComponent.Parameters == null) return; + + if (avDesc.expressionParameters == null) return; + var avatarParams = avDesc.expressionParameters; + + if (!context.IsTemporaryAsset(avatarParams)) + { + avatarParams = UnityEngine.Object.Instantiate(avatarParams); + avDesc.expressionParameters = avatarParams; + } + + if (syncComponent.Parameters.parameters == null) + { + syncComponent.Parameters.parameters = Array.Empty(); + EditorUtility.SetDirty(syncComponent.Parameters); + } + + // If we're on the primary platform, add in any unknown parameters, and prune if we exceed the limit. + if (CurrentPlatform != null && CurrentPlatform == syncComponent.PrimaryPlatform) + { + var registered = new OrderedDictionary(); + + foreach (var param in syncComponent.Parameters.parameters) + { + if (param == null) continue; + if (!param.networkSynced) continue; + registered[param.name] = param; + } + + foreach (var param in avatarParams.parameters) + { + if (param == null) continue; + if (!param.networkSynced) continue; + registered[param.name] = param; + } + + syncComponent.Parameters.parameters = registered.Values.Cast().ToArray(); + if (!syncComponent.Parameters.IsWithinBudget()) + { + var knownParams = avatarParams.parameters.Where(p => p != null).Select(p => p.name).ToHashSet(); + syncComponent.Parameters.parameters = syncComponent.Parameters.parameters.Where( + p => p != null && knownParams.Contains(p.name) + ).ToArray(); + } + + EditorUtility.SetDirty(syncComponent.Parameters); + } + + // Now copy back... + OrderedDictionary finalParams = new(); + foreach (var param in syncComponent.Parameters.parameters) + { + if (param == null) continue; + if (!param.networkSynced) continue; + finalParams[param.name] = param; + } + + foreach (var param in avatarParams.parameters) + { + if (param == null) continue; + finalParams[param.name] = param; + } + + avatarParams.parameters = finalParams.Values.Cast().ToArray(); + + EditorUtility.SetDirty(avatarParams); + } + } +} \ No newline at end of file diff --git a/Editor/SyncParameterSequencePass.cs.meta b/Editor/SyncParameterSequencePass.cs.meta new file mode 100644 index 00000000..cd61ea01 --- /dev/null +++ b/Editor/SyncParameterSequencePass.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 756425df8aeb4926afceda71bedffa40 +timeCreated: 1733011801 \ No newline at end of file diff --git a/Runtime/ModularAvatarSyncParameterSequence.cs b/Runtime/ModularAvatarSyncParameterSequence.cs new file mode 100644 index 00000000..9284a83c --- /dev/null +++ b/Runtime/ModularAvatarSyncParameterSequence.cs @@ -0,0 +1,23 @@ +using System; +using UnityEngine; +using VRC.SDK3.Avatars.ScriptableObjects; + +namespace nadena.dev.modular_avatar.core +{ + [Serializable] + public enum Platform + { + PC, + Android, + iOS + } + + [AddComponentMenu("Modular Avatar/MA Sync Parameter Sequence")] + [DisallowMultipleComponent] + [HelpURL("https://modular-avatar.nadena.dev/docs/reference/sync-parameter-sequence?lang=auto")] + public class ModularAvatarSyncParameterSequence : AvatarTagComponent + { + public Platform PrimaryPlatform; + public VRCExpressionParameters Parameters; + } +} \ No newline at end of file diff --git a/Runtime/ModularAvatarSyncParameterSequence.cs.meta b/Runtime/ModularAvatarSyncParameterSequence.cs.meta new file mode 100644 index 00000000..cc1de8fe --- /dev/null +++ b/Runtime/ModularAvatarSyncParameterSequence.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 934543afe4744213b5621aa13a67e3b4 +timeCreated: 1733011730 \ No newline at end of file