From a122057ea878d844599e81f8b602d26a3cb9f5fe Mon Sep 17 00:00:00 2001 From: mob-sakai <12690315+mob-sakai@users.noreply.github.com> Date: Fri, 27 Dec 2024 19:42:31 +0900 Subject: [PATCH] feat: automatically display a dialog to import samples for TextMeshPro or Spine close #219 --- .../PreloadedProjectSettings.cs | 2 + .../Internal/Utilities/MaterialRepository.cs | 2 +- .../src/Runtime/Internal/Utilities/Misc.cs | 2 + .../Utilities/ShaderVariantRegistry.cs | 72 +++++++++++++++++-- .../UISoftMaskProjectSettings.cs | 56 +++++++++++++++ 5 files changed, 129 insertions(+), 5 deletions(-) diff --git a/Packages/src/Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs b/Packages/src/Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs index 077ffde..5ed9c22 100644 --- a/Packages/src/Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs +++ b/Packages/src/Runtime/Internal/ProjectSettings/PreloadedProjectSettings.cs @@ -133,6 +133,8 @@ public abstract class PreloadedProjectSettings : PreloadedProjectSettings #if UNITY_EDITOR private string _jsonText; + public static bool hasInstance => s_Instance; + public static T instance { get diff --git a/Packages/src/Runtime/Internal/Utilities/MaterialRepository.cs b/Packages/src/Runtime/Internal/Utilities/MaterialRepository.cs index 387bf5e..6b23b34 100644 --- a/Packages/src/Runtime/Internal/Utilities/MaterialRepository.cs +++ b/Packages/src/Runtime/Internal/Utilities/MaterialRepository.cs @@ -15,7 +15,7 @@ internal static class MaterialRepository #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] - private static void Clear() + public static void Clear() { s_Repository.Clear(); } diff --git a/Packages/src/Runtime/Internal/Utilities/Misc.cs b/Packages/src/Runtime/Internal/Utilities/Misc.cs index 42a8be4..9369b0d 100644 --- a/Packages/src/Runtime/Internal/Utilities/Misc.cs +++ b/Packages/src/Runtime/Internal/Utilities/Misc.cs @@ -69,6 +69,8 @@ public static T[] GetAllComponentsInPrefabStage() where T : Component return prefabStage.prefabContentsRoot.GetComponentsInChildren(true); } + + public static bool isBatchOrBuilding => Application.isBatchMode || BuildPipeline.isBuildingPlayer; #endif } } diff --git a/Packages/src/Runtime/Internal/Utilities/ShaderVariantRegistry.cs b/Packages/src/Runtime/Internal/Utilities/ShaderVariantRegistry.cs index c43e091..1f32912 100644 --- a/Packages/src/Runtime/Internal/Utilities/ShaderVariantRegistry.cs +++ b/Packages/src/Runtime/Internal/Utilities/ShaderVariantRegistry.cs @@ -1,14 +1,17 @@ +using System; using System.Collections.Generic; using UnityEngine; -using System; +using UnityEngine.Profiling; #if UNITY_EDITOR using System.IO; -using Object = UnityEngine.Object; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using UnityEditor; +using UnityEditor.PackageManager.UI; using UnityEditorInternal; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; +using Object = UnityEngine.Object; #endif namespace Coffee.UISoftMaskInternal @@ -75,7 +78,7 @@ public Shader FindOptionalShader(Shader shader, return Shader.Find(optionalShaderName); } - // Required shader. + // The shader has if (shader.name.Contains(requiredName)) { _cachedOptionalShaders[id] = shader.name; @@ -105,16 +108,70 @@ public Shader FindOptionalShader(Shader shader, return optionalShader; } +#if UNITY_EDITOR + ImportFromSample(optionalShaderName); +#endif + // Find default optional shader. _cachedOptionalShaders[id] = defaultOptionalShaderName; return Shader.Find(defaultOptionalShaderName); } #if UNITY_EDITOR - private HashSet _logVariants = new HashSet(); + private readonly HashSet _logVariants = new HashSet(); + private readonly Dictionary _sampleNames = new Dictionary(); + + /// + /// Import the sample containing the requested shader. + /// If choice 'Import' is selected, the sample is imported. + /// If choice 'Skip in this session' is selected, the sample is skipped in this session. + /// + public void ImportFromSample(string shaderName) + { + if (Misc.isBatchOrBuilding) return; + + // Find sample name. + if (_sampleNames.TryGetValue(shaderName, out var sampleName)) + { + // Find package info. + var pInfo = PackageInfo.FindForAssembly(typeof(ShaderVariantRegistry).Assembly); + if (pInfo == null) return; + + // Find sample. If not found (resolvedPath == null), skip. + var sample = Sample.FindByPackage(pInfo.name, pInfo.version) + .FirstOrDefault(x => x.displayName == sampleName); + if (sample.resolvedPath == null) return; + + // Import the sample if selected. + var importSelected = EditorUtility.DisplayDialog($"Import {sampleName}", + $"Import '{sampleName}' to use the shader '{shaderName}'", "Import", "Cancel"); + if (importSelected) + { + EditorApplication.delayCall += () => + { + sample.Import(); + }; + } + } + } + + public void ClearCache() + { + _cachedOptionalShaders.Clear(); + } + + public void RegisterSamples((string shaderName, string sampleName)[] samples) + { + foreach (var (shaderName, sampleName) in samples) + { + _sampleNames[shaderName] = sampleName; + } + } public void InitializeIfNeeded(Object owner, string optionalName) { + Profiler.BeginSample("(EDITOR/COF)[ShaderVariantRegistry] InitializeIfNeeded"); + // Register optional shader names by shader comment. if (!string.IsNullOrEmpty(optionalName)) { @@ -159,12 +216,16 @@ public void InitializeIfNeeded(Object owner, string optionalName) EditorUtility.SetDirty(owner); AssetDatabase.SaveAssets(); } + + ClearCache(); + Profiler.EndSample(); } internal void RegisterVariant(Material material, string path) { if (!material || !material.shader || !m_Asset) return; + Profiler.BeginSample("(EDITOR/COF)[ShaderVariantRegistry] RegisterVariant"); var shaderName = material.shader.name; var validKeywords = material.shaderKeywords .Where(x => !Regex.IsMatch(x, "(_EDITOR|EDITOR_)")) @@ -181,6 +242,7 @@ internal void RegisterVariant(Material material, string path) if (m_Asset.Contains(variant)) { m_UnregisteredVariants.Remove(pair); + Profiler.EndSample(); return; } @@ -199,11 +261,13 @@ internal void RegisterVariant(Material material, string path) $"Register it in 'ProjectSettings > {path}' to use it in player.", m_Asset); } + Profiler.EndSample(); return; } m_Asset.Add(variant); m_UnregisteredVariants.Remove(pair); + Profiler.EndSample(); } #endif } diff --git a/Packages/src/Runtime/ProjectSettings/UISoftMaskProjectSettings.cs b/Packages/src/Runtime/ProjectSettings/UISoftMaskProjectSettings.cs index e73570e..da0756a 100644 --- a/Packages/src/Runtime/ProjectSettings/UISoftMaskProjectSettings.cs +++ b/Packages/src/Runtime/ProjectSettings/UISoftMaskProjectSettings.cs @@ -1,4 +1,5 @@ #pragma warning disable CS0414 +using System.Linq; using Coffee.UISoftMaskInternal; using UnityEditor; using UnityEngine; @@ -76,6 +77,15 @@ public static bool useStereoMock } } + protected override void OnEnable() + { + base.OnEnable(); +#if UNITY_EDITOR + SetupSamplesForShaderVariantRegistry(); + m_ShaderVariantRegistry.ClearCache(); +#endif + } + private static void ResetAllSoftMasks() { var softMasks = InternalListPool.Rent(); @@ -115,6 +125,42 @@ private void OnValidate() ResetAllHideFlags(hideFlagsForTemp); } + private void SetupSamplesForShaderVariantRegistry() + { +#if UNITY_2023_2_OR_NEWER + const string tmpSupport = "TextMeshPro Support (Unity 6)"; +#else + const string tmpSupport = "TextMeshPro Support"; +#endif + m_ShaderVariantRegistry.RegisterSamples(new[] + { + // TextMeshPro Support + ("Hidden/TextMeshPro/Bitmap (SoftMaskable)", tmpSupport), + ("Hidden/TextMeshPro/Mobile/Bitmap (SoftMaskable)", tmpSupport), + ("Hidden/TextMeshPro/Distance Field (SoftMaskable)", tmpSupport), + ("Hidden/TextMeshPro/Mobile/Distance Field (SoftMaskable)", tmpSupport), + // Spine Support + ("Hidden/Spine/SkeletonGraphic (SoftMaskable)", "Spine Support"), + ("Hidden/Spine/SkeletonGraphic Fill (SoftMaskable)", "Spine Support"), + ("Hidden/Spine/SkeletonGraphic Grayscale (SoftMaskable)", "Spine Support"), + ("Hidden/Spine/SkeletonGraphic Multiply (SoftMaskable)", "Spine Support"), + ("Hidden/Spine/SkeletonGraphic Screen (SoftMaskable)", "Spine Support") + }); + } + + private void Refresh() + { + m_ShaderVariantRegistry.ClearCache(); + MaterialRepository.Clear(); + foreach (var c in Misc.FindObjectsOfType() + .Concat(Misc.GetAllComponentsInPrefabStage())) + { + c.SetMaterialDirty(); + } + + EditorApplication.QueuePlayerLoopUpdate(); + } + private void Reset() { m_ShaderVariantRegistry.InitializeIfNeeded(this, "(SoftMaskable)"); @@ -134,6 +180,16 @@ private static SettingsProvider CreateSettingsProvider() { return new PreloadedProjectSettingsProvider("Project/UI/Soft Mask"); } + + private class Postprocessor : AssetPostprocessor + { + private static void OnPostprocessAllAssets(string[] _, string[] __, string[] ___, string[] ____) + { + if (Misc.isBatchOrBuilding) return; + + instance.Refresh(); + } + } #endif } }