diff --git a/CHANGELOG.md b/CHANGELOG.md index ef1c75e..f48d285 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +# [1.0.0-preview.3](https://github.com/mob-sakai/SoftMaskForUGUI/compare/v1.0.0-preview.2...v1.0.0-preview.3) (2020-06-04) + + +### Bug Fixes + +* outside interaction doesn't work when the RaycastFilter option is off ([1935650](https://github.com/mob-sakai/SoftMaskForUGUI/commit/19356500c5b777aa5857fa5176fc09f0fd7951cb)) + + +### Features + +* Add ignore self graphic option. ([91c0099](https://github.com/mob-sakai/SoftMaskForUGUI/commit/91c00993b9afbdda0386b8e426e181f8f31618b9)) +* TextMeshPro support ([5b0906b](https://github.com/mob-sakai/SoftMaskForUGUI/commit/5b0906b6086193bc8f62fa174955c9df901ef3f0)) + + +### BREAKING CHANGES + +* TextMeshPro support is now an option. +If a shader or material has errors after a version upgrade, you will need to import the asset. +Please see the README for more information. + # [1.0.0-preview.2](https://github.com/mob-sakai/SoftMaskForUGUI/compare/v1.0.0-preview.1...v1.0.0-preview.2) (2020-05-13) diff --git a/README.md b/README.md index 6b2a934..8471a09 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ By using SoftMask instead of the default Mask component, you can beautifully rep +



## Demo @@ -73,6 +74,7 @@ By using SoftMask instead of the default Mask component, you can beautifully rep +



## Installation @@ -117,23 +119,19 @@ Or, use [UpmGitExtension](https://github.com/mob-sakai/UpmGitExtension) to insta +



## How to play demo -#### For Unity 2019.1 or later +- For Unity 2019.1 or later + - Open `Package Manager` window and select `UI Soft Mask` package in package list and click `Demo > Import in project` button +- For Unity 2018.4 or earlier + - Click `Assets/Samples/UISoftMask/Import Demo` from menu -1. Open `Package Manager` window and select `UI Soft Mask` package in package list -2. Click `Import in project` button -![](https://user-images.githubusercontent.com/12690315/79000480-a8bd4880-7b87-11ea-89c7-a17033a486ba.png) -3. The demo will be imported into `Assets/Samples/UI Soft Mask/{version}/Demo`。 -Open `UISoftMask_Demo` scene and play it +The assets will be imported into `Assets/Samples/UI Soft Mask/{version}/Demo`. +Open `UISoftMask_Demo` scene and play it. -#### For Unity 2018.4 or earlier -1. Select `Assets/Samples/UI Soft Mask Demo` from menu -![Assets_と_Menubar](https://user-images.githubusercontent.com/12690315/79001101-dd7dcf80-7b88-11ea-9cf2-b44c6c5c9b17.png) -2. The demo will be imported into `Assets/Samples/UI Soft Mask/{version}/Demo`. -Open `UISoftMask_Demo` scene and play it



@@ -152,6 +150,20 @@ Or, add a SoftMaskable components from the inspector of the SoftMask component. + +



+## Support TextMeshPro + +To use SoftMask with TextMeshPro, import a sample asset. + +- For Unity 2019.1 or later + - Open `Package Manager` window and select `UI Soft Mask` package in package list and click `TextMeshPro Support > Import in project` button +- For Unity 2018.4 or earlier + - Click `Assets/Samples/UISoftMask/Import TextMeshPro Support` from menu + +The assets will be imported into `Assets/Samples/UI Soft Mask/{version}/TextMeshPro Support`. + +



## Support soft masks with your custom shaders @@ -183,7 +195,7 @@ This operation determines the final alpha according to the soft mask buffer. color.a *= SoftMask(IN.vertex, IN.worldPosition); ``` -As an example of implementation, please see [UI-Default-SoftMask.shader](https://raw.githubusercontent.com/mob-sakai/SoftMaskForUGUI/upm/Shaders/Resources/UI-Default-SoftMask.shader). +As an example of implementation, please see [UI-Default-SoftMask.shader](https://github.com/mob-sakai/SoftMaskForUGUI/blob/upm/Shaders/Resources/UI-Default-SoftMaskable.shader). diff --git a/Shaders/TextMeshProSupport.meta b/Samples~/TextMeshPro Support.meta similarity index 100% rename from Shaders/TextMeshProSupport.meta rename to Samples~/TextMeshPro Support.meta diff --git a/Shaders/TextMeshProSupport/TMP_SDF (SoftMaskable).shader b/Samples~/TextMeshPro Support/TMP_SDF (SoftMaskable).shader similarity index 100% rename from Shaders/TextMeshProSupport/TMP_SDF (SoftMaskable).shader rename to Samples~/TextMeshPro Support/TMP_SDF (SoftMaskable).shader diff --git a/Shaders/TextMeshProSupport/TMP_SDF (SoftMaskable).shader.meta b/Samples~/TextMeshPro Support/TMP_SDF (SoftMaskable).shader.meta old mode 100755 new mode 100644 similarity index 75% rename from Shaders/TextMeshProSupport/TMP_SDF (SoftMaskable).shader.meta rename to Samples~/TextMeshPro Support/TMP_SDF (SoftMaskable).shader.meta index 85d6a3d..a1a301f --- a/Shaders/TextMeshProSupport/TMP_SDF (SoftMaskable).shader.meta +++ b/Samples~/TextMeshPro Support/TMP_SDF (SoftMaskable).shader.meta @@ -1,7 +1,9 @@ fileFormatVersion: 2 guid: 935b7be1c88464d2eb87204fdfab5a38 ShaderImporter: + externalObjects: {} defaultTextures: [] + nonModifiableTextures: [] userData: assetBundleName: assetBundleVariant: diff --git a/Shaders/TextMeshProSupport/TMP_SDF-Mobile (SoftMaskable).shader b/Samples~/TextMeshPro Support/TMP_SDF-Mobile (SoftMaskable).shader similarity index 100% rename from Shaders/TextMeshProSupport/TMP_SDF-Mobile (SoftMaskable).shader rename to Samples~/TextMeshPro Support/TMP_SDF-Mobile (SoftMaskable).shader diff --git a/Shaders/TextMeshProSupport/TMP_SDF-Mobile (SoftMaskable).shader.meta b/Samples~/TextMeshPro Support/TMP_SDF-Mobile (SoftMaskable).shader.meta old mode 100755 new mode 100644 similarity index 75% rename from Shaders/TextMeshProSupport/TMP_SDF-Mobile (SoftMaskable).shader.meta rename to Samples~/TextMeshPro Support/TMP_SDF-Mobile (SoftMaskable).shader.meta index bbb1dda..4eca24f --- a/Shaders/TextMeshProSupport/TMP_SDF-Mobile (SoftMaskable).shader.meta +++ b/Samples~/TextMeshPro Support/TMP_SDF-Mobile (SoftMaskable).shader.meta @@ -1,7 +1,9 @@ fileFormatVersion: 2 guid: e65241fa80a374114b3f55ed746c04d9 ShaderImporter: + externalObjects: {} defaultTextures: [] + nonModifiableTextures: [] userData: assetBundleName: assetBundleVariant: diff --git a/Shaders/TextMeshProSupport/TMP_Sprite (SoftMaskable).shader b/Samples~/TextMeshPro Support/TMP_Sprite (SoftMaskable).shader similarity index 100% rename from Shaders/TextMeshProSupport/TMP_Sprite (SoftMaskable).shader rename to Samples~/TextMeshPro Support/TMP_Sprite (SoftMaskable).shader diff --git a/Shaders/TextMeshProSupport/TMP_Sprite (SoftMaskable).shader.meta b/Samples~/TextMeshPro Support/TMP_Sprite (SoftMaskable).shader.meta old mode 100755 new mode 100644 similarity index 75% rename from Shaders/TextMeshProSupport/TMP_Sprite (SoftMaskable).shader.meta rename to Samples~/TextMeshPro Support/TMP_Sprite (SoftMaskable).shader.meta index 73aa212..f8797a5 --- a/Shaders/TextMeshProSupport/TMP_Sprite (SoftMaskable).shader.meta +++ b/Samples~/TextMeshPro Support/TMP_Sprite (SoftMaskable).shader.meta @@ -1,9 +1,9 @@ fileFormatVersion: 2 guid: 94593ebff37d04a64936ebe46ce7e769 -timeCreated: 1450517184 -licenseType: Pro ShaderImporter: + externalObjects: {} defaultTextures: [] + nonModifiableTextures: [] userData: assetBundleName: assetBundleVariant: diff --git a/Scripts/Editor/EditorUtils.cs b/Scripts/Editor/EditorUtils.cs index d41f9f7..6a8c097 100644 --- a/Scripts/Editor/EditorUtils.cs +++ b/Scripts/Editor/EditorUtils.cs @@ -5,7 +5,6 @@ using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; - namespace Coffee.UISoftMask { internal static class EditorUtils @@ -36,7 +35,7 @@ namespace Coffee.UISoftMask var so = new SerializedObject(target); so.Update(); - bool oldEnable = target.enabled; + var oldEnable = target.enabled; target.enabled = false; // Find MonoScript of the specified component. diff --git a/Scripts/Editor/ImportSampleMenu.cs b/Scripts/Editor/ImportSampleMenu.cs index c61a9d1..56d73c5 100644 --- a/Scripts/Editor/ImportSampleMenu.cs +++ b/Scripts/Editor/ImportSampleMenu.cs @@ -8,13 +8,18 @@ namespace Coffee.UISoftMask { public static class ImportSampleMenu { - [MenuItem("Assets/Samples/Import UISoftMask Sample")] - private static void ImportSample() - { - const string jsonGuid = "c43fd233e88b347cdabc530c23ffe30a"; - const string dirName = "Demo"; + private const string jsonGuid = "c43fd233e88b347cdabc530c23ffe30a"; - ImportSample(jsonGuid, dirName); + [MenuItem("Assets/Samples/UISoftMask/Import Demo")] + private static void ImportDemo() + { + ImportSample(jsonGuid, "Demo"); + } + + [MenuItem("Assets/Samples/UISoftMask/Import TextMeshPro Support")] + private static void ImportTextMeshProSupport() + { + ImportSample(jsonGuid, "TextMeshProSupport"); } private static void ImportSample(string jsonGuid, string sampleName) diff --git a/Scripts/Editor/SoftMaskEditor.cs b/Scripts/Editor/SoftMaskEditor.cs index 8116ea0..ce32fd7 100644 --- a/Scripts/Editor/SoftMaskEditor.cs +++ b/Scripts/Editor/SoftMaskEditor.cs @@ -14,9 +14,9 @@ namespace Coffee.UISoftMask [CanEditMultipleObjects] public class SoftMaskEditor : Editor { - const string k_PrefsPreview = "SoftMaskEditor_Preview"; - static readonly List s_Graphics = new List(); - static bool s_Preview; + private const string k_PrefsPreview = "SoftMaskEditor_Preview"; + private static readonly List s_Graphics = new List(); + private static bool s_Preview; private void OnEnable() { @@ -35,9 +35,7 @@ namespace Coffee.UISoftMask if (0 < fixTargets.Count) { GUILayout.BeginHorizontal(); - EditorGUILayout.HelpBox( - "There are child Graphics that does not have a SoftMaskable component.\nAdd SoftMaskable component to them.", - MessageType.Warning); + EditorGUILayout.HelpBox("There are child Graphics that does not have a SoftMaskable component.\nAdd SoftMaskable component to them.", MessageType.Warning); GUILayout.BeginVertical(); if (GUILayout.Button("Fix")) { @@ -59,9 +57,8 @@ namespace Coffee.UISoftMask } // Preview buffer. - GUILayout.BeginHorizontal(EditorStyles.helpBox); - if (s_Preview != (s_Preview = EditorGUILayout.ToggleLeft("Preview Buffer", s_Preview, - GUILayout.MaxWidth(EditorGUIUtility.labelWidth)))) + GUILayout.BeginVertical(EditorStyles.helpBox); + if (s_Preview != (s_Preview = EditorGUILayout.ToggleLeft("Preview Buffer", s_Preview))) { EditorPrefs.SetBool(k_PrefsPreview, s_Preview); } @@ -69,37 +66,35 @@ namespace Coffee.UISoftMask if (s_Preview) { var tex = current.softMaskBuffer; - var width = tex.width * 64 / tex.height; - EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(width, 64), tex, null, ScaleMode.ScaleToFit); + var width = tex.width * 128 / tex.height; + EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(width, 128), tex, null, ScaleMode.ScaleToFit); Repaint(); } - - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); + GUILayout.EndVertical(); } //%%%% Context menu for editor %%%% [MenuItem("CONTEXT/Mask/Convert To SoftMask", true)] - static bool _ConvertToSoftMask(MenuCommand command) + private static bool _ConvertToSoftMask(MenuCommand command) { return EditorUtils.CanConvertTo(command.context); } [MenuItem("CONTEXT/Mask/Convert To SoftMask", false)] - static void ConvertToSoftMask(MenuCommand command) + private static void ConvertToSoftMask(MenuCommand command) { EditorUtils.ConvertTo(command.context); } [MenuItem("CONTEXT/Mask/Convert To Mask", true)] - static bool _ConvertToMask(MenuCommand command) + private static bool _ConvertToMask(MenuCommand command) { return EditorUtils.CanConvertTo(command.context); } [MenuItem("CONTEXT/Mask/Convert To Mask", false)] - static void ConvertToMask(MenuCommand command) + private static void ConvertToMask(MenuCommand command) { EditorUtils.ConvertTo(command.context); } diff --git a/Scripts/Editor/SoftMaskableEditor.cs b/Scripts/Editor/SoftMaskableEditor.cs index 54272f9..d1814fe 100644 --- a/Scripts/Editor/SoftMaskableEditor.cs +++ b/Scripts/Editor/SoftMaskableEditor.cs @@ -2,15 +2,17 @@ using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEditor; -using System.Linq; -using System; -using System.Reflection; -using Object = UnityEngine.Object; using MaskIntr = UnityEngine.SpriteMaskInteraction; -using System.IO; namespace Coffee.UISoftMask { + internal enum MaskInteraction : int + { + VisibleInsideMask = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6), + VisibleOutsideMask = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6), + Custom = -1, + } + /// /// SoftMaskable editor. /// @@ -18,18 +20,16 @@ namespace Coffee.UISoftMask [CanEditMultipleObjects] public class SoftMaskableEditor : Editor { - public enum MaskInteraction : int - { - VisibleInsideMask = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6), - VisibleOutsideMask = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6), - Custom = -1, - } + private static readonly List s_TmpMasks = new List(); + private static GUIContent s_MaskWarning; + private SerializedProperty _spMaskInteraction; + private bool _custom; - MaskInteraction maskInteraction + private MaskInteraction maskInteraction { get { - int value = _spMaskInteraction.intValue; + var value = _spMaskInteraction.intValue; return _custom ? MaskInteraction.Custom : System.Enum.IsDefined(typeof(MaskInteraction), value) @@ -46,31 +46,26 @@ namespace Coffee.UISoftMask } } - bool _custom = false; - - static readonly List s_Graphics = new List(); - SerializedProperty _spMaskInteraction; - List tmpMasks = new List(); - static GUIContent s_MaskWarning; - private void OnEnable() { _spMaskInteraction = serializedObject.FindProperty("m_MaskInteraction"); _custom = (maskInteraction == MaskInteraction.Custom); - s_MaskWarning = new GUIContent(EditorGUIUtility.FindTexture("console.warnicon.sml"), - "This is not a SoftMask component."); - } + if (s_MaskWarning == null) + { + s_MaskWarning = new GUIContent(EditorGUIUtility.FindTexture("console.warnicon.sml"), "This is not a SoftMask component."); + } + } private void DrawMaskInteractions() { var softMaskable = target as SoftMaskable; if (softMaskable == null) return; - softMaskable.GetComponentsInParent(true, tmpMasks); - tmpMasks.RemoveAll(x => !x.enabled); - tmpMasks.Reverse(); + softMaskable.GetComponentsInParent(true, s_TmpMasks); + s_TmpMasks.RemoveAll(x => !x.enabled); + s_TmpMasks.Reverse(); maskInteraction = (MaskInteraction) EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction); if (!_custom) return; @@ -80,10 +75,10 @@ namespace Coffee.UISoftMask using (var ccs = new EditorGUI.ChangeCheckScope()) { - int intr0 = DrawMaskInteraction(0); - int intr1 = DrawMaskInteraction(1); - int intr2 = DrawMaskInteraction(2); - int intr3 = DrawMaskInteraction(3); + var intr0 = DrawMaskInteraction(0); + var intr1 = DrawMaskInteraction(1); + var intr2 = DrawMaskInteraction(2); + var intr3 = DrawMaskInteraction(3); if (ccs.changed) { @@ -94,24 +89,24 @@ namespace Coffee.UISoftMask EditorGUIUtility.labelWidth = l; } - private int DrawMaskInteraction(int layer) { - Mask mask = layer < tmpMasks.Count ? tmpMasks[layer] : null; - MaskIntr intr = (MaskIntr) ((_spMaskInteraction.intValue >> layer * 2) & 0x3); + var mask = layer < s_TmpMasks.Count ? s_TmpMasks[layer] : null; + var intr = (MaskIntr) ((_spMaskInteraction.intValue >> layer * 2) & 0x3); if (!mask) { return (int) intr; } - using (new EditorGUILayout.HorizontalScope()) - { - EditorGUILayout.LabelField(mask is SoftMask ? GUIContent.none : s_MaskWarning, GUILayout.Width(16)); - GUILayout.Space(-5); - EditorGUILayout.ObjectField("Mask " + layer, mask, typeof(Mask), false); - GUILayout.Space(-15); - return (int) (MaskIntr) EditorGUILayout.EnumPopup(intr); - } + GUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(mask is SoftMask ? GUIContent.none : s_MaskWarning, GUILayout.Width(16)); + GUILayout.Space(-5); + EditorGUILayout.ObjectField("Mask " + layer, mask, typeof(Mask), false); + GUILayout.Space(-15); + intr = (MaskIntr) EditorGUILayout.EnumPopup(intr); + GUILayout.EndHorizontal(); + + return (int) intr; } public override void OnInspectorGUI() @@ -124,61 +119,20 @@ namespace Coffee.UISoftMask serializedObject.ApplyModifiedProperties(); var current = target as SoftMaskable; + if (current == null) return; - current.GetComponentsInChildren(true, s_Graphics); - var fixTargets = s_Graphics.Where(x => - x.gameObject != current.gameObject && !x.GetComponent() && - (!x.GetComponent() || x.GetComponent().showMaskGraphic)).ToList(); - if (0 < fixTargets.Count) + var mask = current.softMask; + if (mask) return; + + GUILayout.BeginHorizontal(); + EditorGUILayout.HelpBox("This is unnecessary SoftMaskable.\nCan't find any SoftMask components above.", MessageType.Warning); + if (GUILayout.Button("Remove", GUILayout.Height(40))) { - GUILayout.BeginHorizontal(); - EditorGUILayout.HelpBox( - "There are child Graphics that does not have a SoftMaskable component.\nAdd SoftMaskable component to them.", - MessageType.Warning); - GUILayout.BeginVertical(); - if (GUILayout.Button("Fix")) - { - foreach (var p in fixTargets) - { - p.gameObject.AddComponent(); - } - } - - if (GUILayout.Button("Ping")) - { - EditorGUIUtility.PingObject(fixTargets[0]); - } - - GUILayout.EndVertical(); - GUILayout.EndHorizontal(); + DestroyImmediate(current); + EditorUtils.MarkPrefabDirty(); } - if (!DetectMask(current.transform.parent)) - { - GUILayout.BeginHorizontal(); - EditorGUILayout.HelpBox("This is unnecessary SoftMaskable.\nCan't find any SoftMask components above.", - MessageType.Warning); - if (GUILayout.Button("Remove", GUILayout.Height(40))) - { - DestroyImmediate(current); - - EditorUtils.MarkPrefabDirty(); - } - - GUILayout.EndHorizontal(); - } - } - - static bool DetectMask(Transform transform) - { - while (transform) - { - if (transform.GetComponent()) return true; - - transform = transform.parent; - } - - return false; + GUILayout.EndHorizontal(); } } } diff --git a/Scripts/GraphicConnector.cs b/Scripts/GraphicConnector.cs index a437335..1627f5f 100644 --- a/Scripts/GraphicConnector.cs +++ b/Scripts/GraphicConnector.cs @@ -17,9 +17,20 @@ namespace Coffee.UISoftMask GraphicConnector.FindConnector(graphic).SetMaterialDirty(graphic); } - public static Shader FindEffectShader(this Graphic graphic) + public static T GetComponentInParentEx(this Component component, bool includeInactive = false) where T : MonoBehaviour { - return GraphicConnector.FindConnector(graphic).FindEffectShader(graphic); + if (!component) return null; + var trans = component.transform; + + while (trans) + { + var c = trans.GetComponent(); + if (c && (includeInactive || c.isActiveAndEnabled)) return c; + + trans = trans.parent; + } + + return null; } } @@ -27,10 +38,7 @@ namespace Coffee.UISoftMask public class GraphicConnector { private static readonly List s_Connectors = new List(); - - private static readonly Dictionary s_ConnectorMap = - new Dictionary(); - + private static readonly Dictionary s_ConnectorMap = new Dictionary(); private static readonly GraphicConnector s_EmptyConnector = new GraphicConnector(); #if UNITY_EDITOR @@ -75,15 +83,6 @@ namespace Coffee.UISoftMask get { return -1; } } - - /// - /// Find effect shader. - /// - public virtual Shader FindEffectShader(Graphic graphic) - { - return Shader.Find("Hidden/UI/SoftMaskable"); - } - /// /// The connector is valid for the component. /// diff --git a/Scripts/MaterialCache.cs b/Scripts/MaterialCache.cs index 055abb2..47b80e4 100644 --- a/Scripts/MaterialCache.cs +++ b/Scripts/MaterialCache.cs @@ -1,42 +1,44 @@ using System.Collections.Generic; using System; using UnityEngine; -using UnityEngine.UI; namespace Coffee.UISoftMask { - internal class MaterialCache + internal class MaterialEntry { - public delegate void ModifyAction(Material material, Graphic graphic); + public Material material; + public int referenceCount; - static Dictionary materialMap = new Dictionary(); - - private class MaterialEntry + public void Release() { - public Material material; - public int referenceCount; - - public void Release() + if (material) { - if (material) - { +#if UNITY_EDITOR + if (!Application.isPlaying) UnityEngine.Object.DestroyImmediate(material, false); - } - - material = null; + else +#endif + UnityEngine.Object.Destroy(material); } + + material = null; } + } + + internal static class MaterialCache + { + static readonly Dictionary s_MaterialMap = new Dictionary(); #if UNITY_EDITOR [UnityEditor.InitializeOnLoadMethod] private static void ClearCache() { - foreach (var entry in materialMap.Values) + foreach (var entry in s_MaterialMap.Values) { entry.Release(); } - materialMap.Clear(); + s_MaterialMap.Clear(); } #endif @@ -45,7 +47,7 @@ namespace Coffee.UISoftMask if (!hash.isValid) return null; MaterialEntry entry; - if (!materialMap.TryGetValue(hash, out entry)) + if (!s_MaterialMap.TryGetValue(hash, out entry)) { entry = new MaterialEntry() { @@ -56,7 +58,7 @@ namespace Coffee.UISoftMask }; onModify(entry.material); - materialMap.Add(hash, entry); + s_MaterialMap.Add(hash, entry); } entry.referenceCount++; @@ -67,13 +69,13 @@ namespace Coffee.UISoftMask public static void Unregister(Hash128 hash) { MaterialEntry entry; - if (!hash.isValid || !materialMap.TryGetValue(hash, out entry)) return; + if (!hash.isValid || !s_MaterialMap.TryGetValue(hash, out entry)) return; //Debug.LogFormat("Unregister: {0}, {1}", hash, entry.referenceCount -1); if (--entry.referenceCount > 0) return; entry.Release(); - materialMap.Remove(hash); + s_MaterialMap.Remove(hash); //Debug.LogFormat("Unregister: Release Emtry: {0}, {1} (Total: {2})", hash, entry.referenceCount, materialMap.Count); } } diff --git a/Scripts/SoftMask.cs b/Scripts/SoftMask.cs index 4c38ebb..3fc612c 100644 --- a/Scripts/SoftMask.cs +++ b/Scripts/SoftMask.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; @@ -25,7 +24,7 @@ namespace Coffee.UISoftMask x8 = 8, } - static readonly List[] s_TmpSoftMasks = new List[] + private static readonly List[] s_TmpSoftMasks = new List[] { new List(), new List(), @@ -33,7 +32,7 @@ namespace Coffee.UISoftMask new List(), }; - static readonly Color[] s_ClearColors = new Color[] + private static readonly Color[] s_ClearColors = new Color[] { new Color(0, 0, 0, 0), new Color(1, 0, 0, 0), @@ -41,50 +40,49 @@ namespace Coffee.UISoftMask new Color(1, 1, 1, 0), }; - static bool s_UVStartsAtTop; - - static Shader s_SoftMaskShader; - static Texture2D s_ReadTexture; - static readonly List s_ActiveSoftMasks = new List(); - static readonly List s_TempRelatables = new List(); - static readonly Dictionary s_previousViewProjectionMatrices = new Dictionary(); - static readonly Dictionary s_nowViewProjectionMatrices = new Dictionary(); - static int s_StencilCompId; - static int s_ColorMaskId; - static int s_MainTexId; - static int s_SoftnessId; - static int s_GameVPId; - static int s_GameTVPId; - static int s_Alpha; - MaterialPropertyBlock _mpb; - CommandBuffer _cb; - Material _material; - RenderTexture _softMaskBuffer; - int _stencilDepth; - Mesh _mesh; - SoftMask _parent; - readonly List _children = new List(); - bool _hasChanged = false; - bool _hasStencilStateChanged = false; + private static bool s_UVStartsAtTop; + private static Shader s_SoftMaskShader; + private static Texture2D s_ReadTexture; + private static readonly List s_ActiveSoftMasks = new List(); + private static readonly List s_TempRelatables = new List(); + private static readonly Dictionary s_PreviousViewProjectionMatrices = new Dictionary(); + private static readonly Dictionary s_NowViewProjectionMatrices = new Dictionary(); + private static int s_StencilCompId; + private static int s_ColorMaskId; + private static int s_MainTexId; + private static int s_SoftnessId; + private static int s_GameVPId; + private static int s_GameTVPId; + private static int s_Alpha; + private MaterialPropertyBlock _mpb; + private CommandBuffer _cb; + private Material _material; + private RenderTexture _softMaskBuffer; + private int _stencilDepth; + private Mesh _mesh; + private SoftMask _parent; + internal readonly List _children = new List(); + private bool _hasChanged = false; + private bool _hasStencilStateChanged = false; - [Tooltip("The desampling rate for soft mask buffer.")] [SerializeField] - DesamplingRate m_DesamplingRate = DesamplingRate.None; + [SerializeField, Tooltip("The desampling rate for soft mask buffer.")] + private DesamplingRate m_DesamplingRate = DesamplingRate.None; - [Tooltip( - "The value used by the soft mask to select the area of influence defined over the soft mask's graphic.")] - [SerializeField] - [Range(0.01f, 1)] - float m_Softness = 1; + [SerializeField, Range(0.01f, 1), Tooltip("The value used by the soft mask to select the area of influence defined over the soft mask's graphic.")] + private float m_Softness = 1; - [Tooltip("The transparency of the whole masked graphic.")] [SerializeField] [Range(0f, 1f)] - float m_Alpha = 1; + [SerializeField, Range(0f, 1f), Tooltip("The transparency of the whole masked graphic.")] + private float m_Alpha = 1; - [Tooltip("Should the soft mask ignore parent soft masks?")] [SerializeField] - bool m_IgnoreParent = false; + [SerializeField, Tooltip("Should the soft mask ignore parent soft masks?")] + private bool m_IgnoreParent = false; - [Tooltip("Is the soft mask a part of parent soft mask?")] [SerializeField] - bool m_PartOfParent = false; + [SerializeField, Tooltip("Is the soft mask a part of parent soft mask?")] + private bool m_PartOfParent = false; + + [SerializeField, Tooltip("Self Graphic will not be drawn to soft mask buffer.")] + private bool m_IgnoreSelfGraphic; /// @@ -214,6 +212,19 @@ namespace Coffee.UISoftMask get { return _parent; } } + public bool ignoreSelfGraphic + { + get { return m_IgnoreSelfGraphic; } + set + { + if (m_IgnoreSelfGraphic == value) return; + m_IgnoreSelfGraphic = value; + hasChanged = true; + graphic.SetVerticesDirtyEx(); + } + } + + Material material { get @@ -267,7 +278,15 @@ namespace Coffee.UISoftMask void IMeshModifier.ModifyMesh(VertexHelper verts) { if (isActiveAndEnabled) + { + if (ignoreSelfGraphic) + { + verts.Clear(); + } + verts.FillMesh(mesh); + } + hasChanged = true; } @@ -336,7 +355,7 @@ namespace Coffee.UISoftMask _mpb = new MaterialPropertyBlock(); _cb = new CommandBuffer(); - graphic.SetVerticesDirty(); + graphic.SetVerticesDirtyEx(); base.OnEnable(); _hasStencilStateChanged = false; @@ -411,7 +430,8 @@ namespace Coffee.UISoftMask /// protected override void OnValidate() { - graphic.SetMaterialDirty(); + graphic.SetVerticesDirtyEx(); + graphic.SetMaterialDirtyEx(); OnTransformParentChanged(); base.OnValidate(); _hasStencilStateChanged = false; @@ -441,8 +461,8 @@ namespace Coffee.UISoftMask var nowVP = cam.projectionMatrix * cam.worldToCameraMatrix; var previousVP = default(Matrix4x4); var id = cam.GetInstanceID(); - s_previousViewProjectionMatrices.TryGetValue(id, out previousVP); - s_nowViewProjectionMatrices[id] = nowVP; + s_PreviousViewProjectionMatrices.TryGetValue(id, out previousVP); + s_NowViewProjectionMatrices[id] = nowVP; if (previousVP != nowVP) { @@ -478,13 +498,13 @@ namespace Coffee.UISoftMask MaskUtilities.NotifyStencilStateChanged(sm); } - s_previousViewProjectionMatrices.Clear(); - foreach (var id in s_nowViewProjectionMatrices.Keys) + s_PreviousViewProjectionMatrices.Clear(); + foreach (var id in s_NowViewProjectionMatrices.Keys) { - s_previousViewProjectionMatrices[id] = s_nowViewProjectionMatrices[id]; + s_PreviousViewProjectionMatrices[id] = s_NowViewProjectionMatrices[id]; } - s_nowViewProjectionMatrices.Clear(); + s_NowViewProjectionMatrices.Clear(); } /// diff --git a/Scripts/SoftMaskable.cs b/Scripts/SoftMaskable.cs index 2be2cbc..4cb0f7b 100755 --- a/Scripts/SoftMaskable.cs +++ b/Scripts/SoftMaskable.cs @@ -15,40 +15,37 @@ namespace Coffee.UISoftMask #else [ExecuteInEditMode] # endif + [RequireComponent(typeof(Graphic))] public class SoftMaskable : MonoBehaviour, IMaterialModifier, ICanvasRaycastFilter #if UNITY_EDITOR , ISerializationCallbackReceiver # endif { - const int kVisibleInside = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6); - const int kVisibleOutside = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6); - static readonly Hash128 k_InvalidHash = new Hash128(); + private const int kVisibleInside = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6); + private const int kVisibleOutside = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6); + private static readonly Hash128 k_InvalidHash = new Hash128(); - static int s_SoftMaskTexId; - static int s_StencilCompId; - static int s_MaskInteractionId; - static List s_ActiveSoftMaskables; - static int[] s_Interactions = new int[4]; + private static int s_SoftMaskTexId; + private static int s_StencilCompId; + private static int s_MaskInteractionId; + private static List s_ActiveSoftMaskables; + private static int[] s_Interactions = new int[4]; - [Tooltip("The graphic will be visible only in areas where no mask is present.")] - [System.Obsolete] - [HideInInspector] - [SerializeField] - bool m_Inverse = false; + [SerializeField, HideInInspector, System.Obsolete] + private bool m_Inverse; - [Tooltip("The interaction for each masks.")] [HideInInspector] [SerializeField] - int m_MaskInteraction = kVisibleInside; + [SerializeField, Tooltip("The interaction for each masks."), HideInInspector] + private int m_MaskInteraction = kVisibleInside; - [Tooltip("Use stencil to mask.")] [SerializeField] - bool m_UseStencil = false; + [SerializeField, Tooltip("Use stencil to mask.")] + private bool m_UseStencil; - [Tooltip("Use soft-masked raycast target.\n\nNote: This option is expensive.")] [SerializeField] - bool m_RaycastFilter = false; + [SerializeField, Tooltip("Use soft-masked raycast target.\n\nNote: This option is expensive.")] + private bool m_RaycastFilter; - Graphic _graphic = null; - SoftMask _softMask = null; - Material _maskMaterial = null; - Hash128 _effectMaterialHash; + private Graphic _graphic; + private SoftMask _softMask; + private Hash128 _effectMaterialHash; /// /// The graphic will be visible only in areas where no mask is present. @@ -82,6 +79,11 @@ namespace Coffee.UISoftMask get { return _graphic ? _graphic : _graphic = GetComponent(); } } + public SoftMask softMask + { + get { return _softMask ? _softMask : _softMask = this.GetComponentInParentEx(); } + } + /// /// Perform material modification in this function. /// @@ -98,27 +100,13 @@ namespace Coffee.UISoftMask // If this component is disabled, the material is returned as is. if (!isActiveAndEnabled) return baseMaterial; - // Find the nearest parent softmask. - var parentTransform = transform.parent; - while (parentTransform) - { - var sm = parentTransform.GetComponent(); - if (sm && sm.enabled) - { - _softMask = sm; - break; - } - - parentTransform = parentTransform.parent; - } - // If the parents do not have a soft mask component, the material is returned as is. - if (!_softMask) return baseMaterial; + if (!softMask) return baseMaterial; // Generate soft maskable material. _effectMaterialHash = new Hash128( (uint) baseMaterial.GetInstanceID(), - (uint) _softMask.GetInstanceID(), + (uint) softMask.GetInstanceID(), (uint) m_MaskInteraction, (uint) (m_UseStencil ? 1 : 0) ); @@ -130,7 +118,7 @@ namespace Coffee.UISoftMask #if UNITY_EDITOR mat.EnableKeyword("SOFTMASK_EDITOR"); #endif - mat.SetTexture(s_SoftMaskTexId, _softMask.softMaskBuffer); + mat.SetTexture(s_SoftMaskTexId, softMask.softMaskBuffer); mat.SetInt(s_StencilCompId, m_UseStencil ? (int) CompareFunction.Equal : (int) CompareFunction.Always); mat.SetVector(s_MaskInteractionId, new Vector4( @@ -140,7 +128,6 @@ namespace Coffee.UISoftMask ((m_MaskInteraction >> 6) & 0x3) )); }); - _maskMaterial = modifiedMaterial; return modifiedMaterial; } @@ -153,21 +140,45 @@ namespace Coffee.UISoftMask /// Raycast camera. bool ICanvasRaycastFilter.IsRaycastLocationValid(Vector2 sp, Camera eventCamera) { - if (!isActiveAndEnabled || !_softMask) + if (!isActiveAndEnabled || !softMask) return true; if (!RectTransformUtility.RectangleContainsScreenPoint(transform as RectTransform, sp, eventCamera)) return false; - if (!m_RaycastFilter) - return true; - var sm = _softMask; - for (var i = 0; i < 4; i++) + if (m_RaycastFilter) { - s_Interactions[i] = sm ? ((m_MaskInteraction >> i * 2) & 0x3) : 0; - sm = sm ? sm.parent : null; - } + var sm = _softMask; + for (var i = 0; i < 4; i++) + { + s_Interactions[i] = sm ? ((m_MaskInteraction >> i * 2) & 0x3) : 0; + sm = sm ? sm.parent : null; + } - return _softMask.IsRaycastLocationValid(sp, eventCamera, graphic, s_Interactions); + return _softMask.IsRaycastLocationValid(sp, eventCamera, graphic, s_Interactions); + } + else + { + var sm = _softMask; + for (var i = 0; i < 4; i++) + { + if (!sm) break; + s_Interactions[i] = sm ? ((m_MaskInteraction >> i * 2) & 0x3) : 0; + var interaction = s_Interactions[i] == 1; + var inRect = RectTransformUtility.RectangleContainsScreenPoint(sm.transform as RectTransform, sp, eventCamera); + if (!sm.ignoreSelfGraphic && interaction != inRect) return false; + + foreach (var child in sm._children) + { + if (!child) break; + var inRectChild = RectTransformUtility.RectangleContainsScreenPoint(child.transform as RectTransform, sp, eventCamera); + if (!child.ignoreSelfGraphic && interaction != inRectChild) return false; + } + + sm = sm ? sm.parent : null; + } + + return true; + } } /// @@ -243,7 +254,7 @@ namespace Coffee.UISoftMask if (m_Inverse) { m_Inverse = false; - m_MaskInteraction = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6); + m_MaskInteraction = kVisibleOutside; } #pragma warning restore 0612 diff --git a/package.json b/package.json index 0dde190..4e4cbca 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,34 @@ { - "name": "com.coffee.softmask-for-ugui", - "displayName": "UI Soft Mask", - "description": "UI Soft Mask is a smooth masking component for Unity UI (uGUI) elements.\nBy using SoftMask instead of the default Mask component, you can beautifully represent the rounded edges of UI elements.", - "version": "1.0.0-preview.2", - "unity": "2017.1", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/mob-sakai/SoftMaskForUGUI.git" - }, - "author": { - "name": "mob-sakai", - "email": "sakai861104@gmail.com", - "url": "https://github.com/mob-sakai" - }, - "dependencies": {}, - "keywords": [ - "ui", - "softmask" - ], - "samples": [ - { - "displayName": "Demo", - "description": "UI Soft Mask Demo", - "path": "Samples~/Demo" - } - ] + "name": "com.coffee.softmask-for-ugui", + "displayName": "UI Soft Mask", + "description": "UI Soft Mask is a smooth masking component for Unity UI (uGUI) elements.\nBy using SoftMask instead of the default Mask component, you can beautifully represent the rounded edges of UI elements.", + "version": "1.0.0-preview.3", + "unity": "2017.1", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/mob-sakai/SoftMaskForUGUI.git" + }, + "author": { + "name": "mob-sakai", + "email": "sakai861104@gmail.com", + "url": "https://github.com/mob-sakai" + }, + "dependencies": {}, + "keywords": [ + "ui", + "softmask" + ], + "samples": [ + { + "displayName": "Demo", + "description": "UI Soft Mask Demo", + "path": "Samples~/Demo" + }, + { + "displayName": "TextMeshPro Support", + "description": "TextMeshPro Support", + "path": "Samples~/TextMeshPro Support" + } + ] }