From 280724313c1b115c0ab2d99be27f7719a6205ee4 Mon Sep 17 00:00:00 2001 From: bd_ Date: Sun, 28 Jul 2024 18:25:26 -0700 Subject: [PATCH] feat: add support for disabling preview on a pass or plugin basis Closes: #296 --- CHANGELOG.md | 1 + Editor/API/Solver/PluginResolver.cs | 37 +++- Editor/GlobalInit.cs | 10 + Editor/PreviewSystem/PreviewSession.cs | 24 +-- Editor/PreviewSystem/UI.meta | 3 + Editor/PreviewSystem/UI/PreviewPrefs.cs | 63 +++++++ Editor/PreviewSystem/UI/PreviewPrefs.cs.meta | 3 + Editor/PreviewSystem/UI/PreviewPrefsUI.cs | 175 ++++++++++++++++++ .../PreviewSystem/UI/PreviewPrefsUI.cs.meta | 3 + Editor/PreviewSystem/UI/Resources.meta | 3 + .../UI/Resources/PreviewPrefsWindow.uss | 55 ++++++ .../UI/Resources/PreviewPrefsWindow.uss.meta | 3 + .../UI/Resources/PreviewPrefsWindow.uxml | 15 ++ .../UI/Resources/PreviewPrefsWindow.uxml.meta | 3 + Editor/UI/Localization/en-US.po | 3 + 15 files changed, 380 insertions(+), 21 deletions(-) create mode 100644 Editor/PreviewSystem/UI.meta create mode 100644 Editor/PreviewSystem/UI/PreviewPrefs.cs create mode 100644 Editor/PreviewSystem/UI/PreviewPrefs.cs.meta create mode 100644 Editor/PreviewSystem/UI/PreviewPrefsUI.cs create mode 100644 Editor/PreviewSystem/UI/PreviewPrefsUI.cs.meta create mode 100644 Editor/PreviewSystem/UI/Resources.meta create mode 100644 Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss create mode 100644 Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss.meta create mode 100644 Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml create mode 100644 Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml.meta diff --git a/CHANGELOG.md b/CHANGELOG.md index 76a2f4d..9224363 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- [#297] Added UI for turning preview on/off at a plugin or pass level ### Fixed diff --git a/Editor/API/Solver/PluginResolver.cs b/Editor/API/Solver/PluginResolver.cs index d7c907a..d3ed45e 100644 --- a/Editor/API/Solver/PluginResolver.cs +++ b/Editor/API/Solver/PluginResolver.cs @@ -6,6 +6,7 @@ using System.Linq; using nadena.dev.ndmf.model; using nadena.dev.ndmf.preview; +using nadena.dev.ndmf.preview.UI; #endregion @@ -30,6 +31,7 @@ internal class ConcretePass internal IPass InstantiatedPass { get; } internal ImmutableList DeactivatePlugins { get; } internal ImmutableList ActivatePlugins { get; } + internal bool HasPreviews { get; set; } public void Execute(BuildContext context) { @@ -49,9 +51,10 @@ internal ConcretePass(IPluginInternal plugin, IPass pass, ImmutableList de internal class PluginResolver { - internal PreviewSession PreviewSession { get; private set; } internal ImmutableList<(BuildPhase, IList)> Passes { get; } + private readonly List _allPasses = new(); + public PluginResolver() : this( AppDomain.CurrentDomain.GetAssemblies().SelectMany( assembly => assembly.GetCustomAttributes(typeof(ExportsPlugin), false)) @@ -71,8 +74,6 @@ public PluginResolver(IEnumerable plugins) : this( public PluginResolver(IEnumerable pluginTemplates) { - PreviewSession = new PreviewSession(); - var solverContext = new SolverContext(); foreach (var plugin in pluginTemplates) @@ -156,6 +157,7 @@ public PluginResolver(IEnumerable pluginTemplates) #endif var sorted = TopoSort.DoSort(passes, constraints); + _allPasses.AddRange(sorted); var concrete = ToConcretePasses(phase, sorted); @@ -196,13 +198,11 @@ ImmutableList ToConcretePasses(BuildPhase phase, IEnumerable 0; - foreach (var filter in pass.RenderFilters) - { - PreviewSession.AddMutator(new SequencePoint(), filter); - } + concrete.Add(concretePass); } if (activeExtensions.Count > 0) @@ -219,5 +219,24 @@ ImmutableList ToConcretePasses(BuildPhase phase, IEnumerable + { + var oldSession = PreviewSession.Current; + PreviewSession.Current = resolver.PreviewSession; + oldSession.Dispose(); + + SceneView.RepaintAll(); + }; }; } } diff --git a/Editor/PreviewSystem/PreviewSession.cs b/Editor/PreviewSystem/PreviewSession.cs index 5038fed..7331776 100644 --- a/Editor/PreviewSystem/PreviewSession.cs +++ b/Editor/PreviewSystem/PreviewSession.cs @@ -15,14 +15,14 @@ namespace nadena.dev.ndmf.preview /// /// (For now, this isn't very useful; use `DeclaringPass.PreviewingWith` instead) /// - public class PreviewSession // : IDisposable + internal class PreviewSession // : IDisposable { #region Static State /// /// The PreviewSession used for any cameras not overriden using `OverrideCamera`. /// - public static PreviewSession Current { get; set; } = null; + public static PreviewSession Current { get; set; } #if FUTURE_API /// @@ -48,27 +48,27 @@ public static void ClearCameraOverride(Camera target) internal IEnumerable<(Renderer, Renderer)> GetReplacements() { - return _session?.OnPreCull() ?? Enumerable.Empty<(Renderer, Renderer)>(); + return _proxySession?.OnPreCull() ?? Enumerable.Empty<(Renderer, Renderer)>(); } internal ImmutableDictionary OriginalToProxyRenderer => - _session?.OriginalToProxyRenderer ?? ImmutableDictionary.Empty; + _proxySession?.OriginalToProxyRenderer ?? ImmutableDictionary.Empty; internal ImmutableDictionary OriginalToProxyObject => - _session?.OriginalToProxyObject ?? ImmutableDictionary.Empty; + _proxySession?.OriginalToProxyObject ?? ImmutableDictionary.Empty; internal ImmutableDictionary ProxyToOriginalObject => - _session?.ProxyToOriginalObject ?? ImmutableDictionary.Empty; + _proxySession?.ProxyToOriginalObject ?? ImmutableDictionary.Empty; private readonly Sequencer _sequence = new Sequencer(); private Dictionary _filters = new(); - private ProxySession _session; + private readonly ProxySession _proxySession; public PreviewSession() { - _session = new ProxySession(ImmutableList.Empty); + _proxySession = new ProxySession(ImmutableList.Empty); } /// @@ -117,7 +117,7 @@ void RebuildSequence() var sequence = _sequence.Sequence; var filters = sequence.Select(p => _filters.GetValueOrDefault(p)).Where(f => f != null).ToImmutableList(); - _session.Filters = filters; + _proxySession.Filters = filters; } #if FUTURE_API @@ -131,11 +131,11 @@ public PreviewSession Fork() { throw new NotImplementedException(); } - +#endif + public void Dispose() { - throw new NotImplementedException(); + _proxySession.Dispose(); } -#endif } } \ No newline at end of file diff --git a/Editor/PreviewSystem/UI.meta b/Editor/PreviewSystem/UI.meta new file mode 100644 index 0000000..2f41d96 --- /dev/null +++ b/Editor/PreviewSystem/UI.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 91cc693d08284eb1a9e046cc10d938d9 +timeCreated: 1722211570 \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/PreviewPrefs.cs b/Editor/PreviewSystem/UI/PreviewPrefs.cs new file mode 100644 index 0000000..a91856f --- /dev/null +++ b/Editor/PreviewSystem/UI/PreviewPrefs.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using UnityEditor; +using UnityEngine; + +namespace nadena.dev.ndmf.preview.UI +{ + internal class PreviewPrefs : ScriptableSingleton + { + [SerializeField] private List _disabledPreviewPasses = new(); + private ImmutableHashSet _disabledPreviewPassesSet; + + [SerializeField] private List _disabledPreviewPlugins = new(); + private ImmutableHashSet _disabledPreviewPluginsSet; + + public event Action OnPreviewConfigChanged; + + private void OnValidate() + { + _disabledPreviewPassesSet = _disabledPreviewPasses.ToImmutableHashSet(); + _disabledPreviewPluginsSet = _disabledPreviewPlugins.ToImmutableHashSet(); + } + + public bool IsPreviewPassEnabled(string qualifiedName) + { + if (_disabledPreviewPassesSet == null) OnValidate(); + + return !_disabledPreviewPassesSet.Contains(qualifiedName); + } + + public bool IsPreviewPluginEnabled(string qualifiedName) + { + if (_disabledPreviewPluginsSet == null) OnValidate(); + + return !_disabledPreviewPluginsSet.Contains(qualifiedName); + } + + public void SetPreviewPassEnabled(string qualifiedName, bool enabled) + { + if (enabled) + _disabledPreviewPasses.Remove(qualifiedName); + else + _disabledPreviewPasses.Add(qualifiedName); + + OnValidate(); + + OnPreviewConfigChanged?.Invoke(); + } + + public void SetPreviewPluginEnabled(string qualifiedName, bool enabled) + { + if (enabled) + _disabledPreviewPlugins.Remove(qualifiedName); + else + _disabledPreviewPlugins.Add(qualifiedName); + + OnValidate(); + + OnPreviewConfigChanged?.Invoke(); + } + } +} \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/PreviewPrefs.cs.meta b/Editor/PreviewSystem/UI/PreviewPrefs.cs.meta new file mode 100644 index 0000000..14ad598 --- /dev/null +++ b/Editor/PreviewSystem/UI/PreviewPrefs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aa895f0293044debbf434a5b8448dbfd +timeCreated: 1722211584 \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/PreviewPrefsUI.cs b/Editor/PreviewSystem/UI/PreviewPrefsUI.cs new file mode 100644 index 0000000..a5248cf --- /dev/null +++ b/Editor/PreviewSystem/UI/PreviewPrefsUI.cs @@ -0,0 +1,175 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using nadena.dev.ndmf.localization; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace nadena.dev.ndmf.preview.UI +{ + public class PreviewPrefsUI : EditorWindow + { + internal static bool DISABLE_WINDOW = false; + + private ImmutableList<(BuildPhase, IList)> _passes = new PluginResolver().Passes; + private readonly List> _treeViewData = new(); + private readonly List _flatItems = new(); + private TreeView _treeView; + + private class ItemData + { + public bool isPass; + public string QualifiedName; + public string DisplayName; + public string PluginQualifiedName; + } + + [MenuItem("Tools/NDM Framework/Configure Previews")] + public static void ShowPreviewConfigWindow() + { + if (Application.isBatchMode || DISABLE_WINDOW) return; // headless unit tests + + GetWindow(); + } + + private PreviewPrefsUI() + { + BuildTreeViewData(); + } + + private void BuildTreeViewData() + { + var passesByPlugin = + new PluginResolver().Passes + .SelectMany(kv => kv.Item2) + .Where(pass => pass.HasPreviews) + .OrderBy(pass => pass.Description) + .GroupBy(cp => cp.Plugin) + .OrderBy(g => g.Key.DisplayName) + .Select(g => (g.Key, g.ToList())) + .ToList(); + + _treeViewData.Clear(); + _flatItems.Clear(); + + var id = 0; + foreach (var (plugin, passes) in passesByPlugin) + { + var pluginItemData = new ItemData + { + isPass = false, + QualifiedName = plugin.QualifiedName, + DisplayName = plugin.DisplayName + }; + _flatItems.Add(pluginItemData); + + var items = new List>(); + + foreach (var pass in passes) + { + var passItemData = new ItemData + { + isPass = true, + QualifiedName = pass.InstantiatedPass.QualifiedName, + DisplayName = pass.Description, + PluginQualifiedName = plugin.QualifiedName + }; + + items.Add(new TreeViewItemData(id++, passItemData)); + _flatItems.Add(passItemData); + } + + _treeViewData.Add(new TreeViewItemData(id++, pluginItemData, items)); + } + } + + public void CreateGUI() + { + titleContent = new GUIContent(NDMFLocales.L.GetLocalizedString("PreviewEnable:Title")); + minSize = new Vector2(300, 400); + + // Each editor window contains a root VisualElement object + var root = rootVisualElement; + root.AddToClassList("WindowRoot"); + + // Import UXML + var visualTree = + AssetDatabase.LoadAssetAtPath( + "Packages/nadena.dev.ndmf/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml"); + VisualElement labelFromUXML = visualTree.CloneTree(); + root.Add(labelFromUXML); + + var styleSheet = + AssetDatabase.LoadAssetAtPath( + "Packages/nadena.dev.ndmf/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss"); + root.styleSheets.Add(styleSheet); + + NDMFLocales.L.LocalizeUIElements(root); + + _treeView = root.Q("PreviewEnableTree"); + _treeView.SetRootItems(_treeViewData); + _treeView.makeItem = MakeItem; + _treeView.bindItem = BindItem; + _treeView.selectionType = SelectionType.None; + _treeView.Rebuild(); + } + + private void BindItem(VisualElement elem, int id) + { + var itemData = _treeView.GetItemDataForIndex(id); + var itemElem = elem as ItemElement; + + itemElem?.BindItem(itemData); + } + + private VisualElement MakeItem() + { + return new ItemElement(); + } + + private class ItemElement : VisualElement + { + private readonly Toggle _toggle = new(); + private bool _isPass; + private string _qualifiedName = ""; + + internal ItemElement() + { + Add(_toggle); + + _toggle.RegisterValueChangedCallback(OnValueChanged); + } + + private void OnValueChanged(ChangeEvent evt) + { + if (_isPass) + PreviewPrefs.instance.SetPreviewPassEnabled(_qualifiedName, evt.newValue); + else + PreviewPrefs.instance.SetPreviewPluginEnabled(_qualifiedName, evt.newValue); + } + + internal void BindItem(ItemData itemData) + { + _toggle.text = itemData.DisplayName; + + var isChecked = itemData.isPass + ? PreviewPrefs.instance.IsPreviewPassEnabled(itemData.QualifiedName) + : PreviewPrefs.instance.IsPreviewPluginEnabled(itemData.QualifiedName); + + _toggle.SetValueWithoutNotify(isChecked); + _isPass = itemData.isPass; + _qualifiedName = itemData.QualifiedName; + + var isEnabled = !itemData.isPass || + PreviewPrefs.instance.IsPreviewPluginEnabled(itemData.PluginQualifiedName); + _toggle.SetEnabled(isEnabled); + + if (itemData.isPass) + _toggle.AddToClassList("ndmf-pass-item"); + else + _toggle.RemoveFromClassList("ndmf-pass-item"); + } + } + } +} \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/PreviewPrefsUI.cs.meta b/Editor/PreviewSystem/UI/PreviewPrefsUI.cs.meta new file mode 100644 index 0000000..bcf8bf7 --- /dev/null +++ b/Editor/PreviewSystem/UI/PreviewPrefsUI.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 53a089cd0faa4a24851629ce74109b12 +timeCreated: 1722211640 \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/Resources.meta b/Editor/PreviewSystem/UI/Resources.meta new file mode 100644 index 0000000..27ec16d --- /dev/null +++ b/Editor/PreviewSystem/UI/Resources.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f9f1c46c324543d5af16abe4aed03abd +timeCreated: 1722212073 \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss b/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss new file mode 100644 index 0000000..01f47f3 --- /dev/null +++ b/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss @@ -0,0 +1,55 @@ +VisualElement { + +} + +.WindowRoot { + padding: 5px; +} + +.WindowRoot > TemplateContainer { + height: 100%; +} + +#root { + height: 100%; +} + +#topHeader { + flex-direction: row; + padding-left: 4px; + padding-right: 4px; +} + +#topHeader > Label { + border-color: black; + border-left-width: 1px; + border-right-width: 1px; + border-top-width: 1px; + border-bottom-width: 0px; + padding: 4px; + background-color: rgba(255, 255, 255, 0.1); +} + +#topHeader > .filler { + flex-grow: 1; + border-bottom-width: 1px; + border-color: black; +} + +#PreviewEnableTree { + border-color: black; + border-left-width: 1px; + border-right-width: 1px; + border-top-width: 0px; + border-bottom-width: 1px; + margin: 4px; + margin-top: 0px; + padding: 4px; + background-color: rgba(255, 255, 255, 0.1); + flex-grow: 1; + height: auto; +} + +#PreviewEnableTree Toggle { + margin: 3px; +} \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss.meta b/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss.meta new file mode 100644 index 0000000..488c277 --- /dev/null +++ b/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uss.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d47e928fa985420bb35e9d669321d191 +timeCreated: 1722212080 \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml b/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml new file mode 100644 index 0000000..1cf49cf --- /dev/null +++ b/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml.meta b/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml.meta new file mode 100644 index 0000000..ea65111 --- /dev/null +++ b/Editor/PreviewSystem/UI/Resources/PreviewPrefsWindow.uxml.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 03e4aa8a478f4379922410d9e12f25d1 +timeCreated: 1722212080 \ No newline at end of file diff --git a/Editor/UI/Localization/en-US.po b/Editor/UI/Localization/en-US.po index 4c8175f..70f2557 100644 --- a/Editor/UI/Localization/en-US.po +++ b/Editor/UI/Localization/en-US.po @@ -34,3 +34,6 @@ msgstr "Show Stack Trace (please include with bug reports!)" msgid "AvatarProcessor:ProcessingFailed" msgstr "There were errors processing the avatar. Check the NDMF Console for details." + +msgid "PreviewEnable:Title" +msgstr "Disable/Enable plugin preview" \ No newline at end of file