refactor: refactor

vr
mob-sakai 2020-05-11 11:47:44 +09:00
parent 0347b04fb7
commit e65bbb1cef
7 changed files with 1178 additions and 1410 deletions

View File

@ -1,22 +1,57 @@
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
using UnityEditor.Experimental.SceneManagement; using UnityEditor.Experimental.SceneManagement;
#endif #endif
using UnityEditor;
using UnityEditor.SceneManagement; using UnityEditor.SceneManagement;
using UnityEngine;
namespace Coffee.UISoftMask namespace Coffee.UISoftMask
{ {
public static class EditorUtils internal static class EditorUtils
{ {
public static void MarkPrefabDirty () internal static void MarkPrefabDirty()
{ {
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
if (prefabStage != null) if (prefabStage == null) return;
{
EditorSceneManager.MarkSceneDirty(prefabStage.scene); EditorSceneManager.MarkSceneDirty(prefabStage.scene);
}
#endif #endif
} }
/// <summary>
/// Verify whether it can be converted to the specified component.
/// </summary>
internal static bool CanConvertTo<T>(Object context) where T : MonoBehaviour
{
return context && context.GetType() != typeof(T);
}
/// <summary>
/// Convert to the specified component.
/// </summary>
internal static void ConvertTo<T>(Object context) where T : MonoBehaviour
{
var target = context as MonoBehaviour;
var so = new SerializedObject(target);
so.Update();
bool oldEnable = target.enabled;
target.enabled = false;
// Find MonoScript of the specified component.
foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
{
if (script.GetClass() != typeof(T))
continue;
// Set 'm_Script' to convert.
so.FindProperty("m_Script").objectReferenceValue = script;
so.ApplyModifiedProperties();
break;
}
(so.targetObject as MonoBehaviour).enabled = oldEnable;
}
} }
} }

View File

@ -29,11 +29,15 @@ namespace Coffee.UISoftMask
var current = target as SoftMask; var current = target as SoftMask;
current.GetComponentsInChildren<Graphic>(true, s_Graphics); current.GetComponentsInChildren<Graphic>(true, s_Graphics);
var fixTargets = s_Graphics.Where (x => x.gameObject != current.gameObject && !x.GetComponent<SoftMaskable> () && (!x.GetComponent<Mask> () || x.GetComponent<Mask> ().showMaskGraphic)).ToList (); var fixTargets = s_Graphics.Where(x =>
x.gameObject != current.gameObject && !x.GetComponent<SoftMaskable>() &&
(!x.GetComponent<Mask>() || x.GetComponent<Mask>().showMaskGraphic)).ToList();
if (0 < fixTargets.Count) if (0 < fixTargets.Count)
{ {
GUILayout.BeginHorizontal(); 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(); GUILayout.BeginVertical();
if (GUILayout.Button("Fix")) if (GUILayout.Button("Fix"))
{ {
@ -44,20 +48,24 @@ namespace Coffee.UISoftMask
EditorUtils.MarkPrefabDirty(); EditorUtils.MarkPrefabDirty();
} }
if (GUILayout.Button("Ping")) if (GUILayout.Button("Ping"))
{ {
EditorGUIUtility.PingObject(fixTargets[0]); EditorGUIUtility.PingObject(fixTargets[0]);
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
// Preview buffer. // Preview buffer.
GUILayout.BeginHorizontal(EditorStyles.helpBox); GUILayout.BeginHorizontal(EditorStyles.helpBox);
if (s_Preview != (s_Preview = EditorGUILayout.ToggleLeft ("Preview Buffer", s_Preview, GUILayout.MaxWidth (EditorGUIUtility.labelWidth)))) if (s_Preview != (s_Preview = EditorGUILayout.ToggleLeft("Preview Buffer", s_Preview,
GUILayout.MaxWidth(EditorGUIUtility.labelWidth))))
{ {
EditorPrefs.SetBool(k_PrefsPreview, s_Preview); EditorPrefs.SetBool(k_PrefsPreview, s_Preview);
} }
if (s_Preview) if (s_Preview)
{ {
var tex = current.softMaskBuffer; var tex = current.softMaskBuffer;
@ -65,6 +73,7 @@ namespace Coffee.UISoftMask
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(width, 64), tex, null, ScaleMode.ScaleToFit); EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(width, 64), tex, null, ScaleMode.ScaleToFit);
Repaint(); Repaint();
} }
GUILayout.FlexibleSpace(); GUILayout.FlexibleSpace();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
@ -74,61 +83,25 @@ namespace Coffee.UISoftMask
[MenuItem("CONTEXT/Mask/Convert To SoftMask", true)] [MenuItem("CONTEXT/Mask/Convert To SoftMask", true)]
static bool _ConvertToSoftMask(MenuCommand command) static bool _ConvertToSoftMask(MenuCommand command)
{ {
return CanConvertTo<SoftMask>(command.context); return EditorUtils.CanConvertTo<SoftMask>(command.context);
} }
[MenuItem("CONTEXT/Mask/Convert To SoftMask", false)] [MenuItem("CONTEXT/Mask/Convert To SoftMask", false)]
static void ConvertToSoftMask(MenuCommand command) static void ConvertToSoftMask(MenuCommand command)
{ {
ConvertTo<SoftMask>(command.context); EditorUtils.ConvertTo<SoftMask>(command.context);
} }
[MenuItem("CONTEXT/Mask/Convert To Mask", true)] [MenuItem("CONTEXT/Mask/Convert To Mask", true)]
static bool _ConvertToMask(MenuCommand command) static bool _ConvertToMask(MenuCommand command)
{ {
return CanConvertTo<Mask>(command.context); return EditorUtils.CanConvertTo<Mask>(command.context);
} }
[MenuItem("CONTEXT/Mask/Convert To Mask", false)] [MenuItem("CONTEXT/Mask/Convert To Mask", false)]
static void ConvertToMask(MenuCommand command) static void ConvertToMask(MenuCommand command)
{ {
ConvertTo<Mask>(command.context); EditorUtils.ConvertTo<Mask>(command.context);
}
/// <summary>
/// Verify whether it can be converted to the specified component.
/// </summary>
protected static bool CanConvertTo<T>(Object context)
where T : MonoBehaviour
{
return context && context.GetType() != typeof(T);
}
/// <summary>
/// Convert to the specified component.
/// </summary>
protected static void ConvertTo<T>(Object context) where T : MonoBehaviour
{
var target = context as MonoBehaviour;
var so = new SerializedObject(target);
so.Update();
bool oldEnable = target.enabled;
target.enabled = false;
// Find MonoScript of the specified component.
foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
{
if (script.GetClass() != typeof(T))
continue;
// Set 'm_Script' to convert.
so.FindProperty("m_Script").objectReferenceValue = script;
so.ApplyModifiedProperties();
break;
}
(so.targetObject as MonoBehaviour).enabled = oldEnable;
} }
} }
} }

View File

@ -18,9 +18,6 @@ namespace Coffee.UISoftMask
[CanEditMultipleObjects] [CanEditMultipleObjects]
public class SoftMaskableEditor : Editor public class SoftMaskableEditor : Editor
{ {
//################################
// Constant or Static Members.
//################################
public enum MaskInteraction : int public enum MaskInteraction : int
{ {
VisibleInsideMask = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6), VisibleInsideMask = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6),
@ -48,73 +45,36 @@ namespace Coffee.UISoftMask
} }
} }
} }
bool _custom = false; bool _custom = false;
static readonly Type s_TypeTMPro = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_Text");
static readonly Type s_TypeTMP_SpriteAsset = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_SpriteAsset");
static readonly Type s_TypeTMProSettings = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_Settings");
static readonly Type s_TypeTMP_SubMesh = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_SubMesh");
static readonly Type s_TypeTMP_SubMeshUI = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_SubMeshUI");
static PropertyInfo s_PiFontSharedMaterial;
static PropertyInfo s_PiFontSharedMaterials;
static PropertyInfo s_PiSpriteAsset;
static PropertyInfo s_PiRichText;
static PropertyInfo s_PiText;
static PropertyInfo s_PiDefaultFontAssetPath;
static PropertyInfo s_PiDefaultSpriteAssetPath;
static FieldInfo s_FiMaterial;
static MethodInfo s_miGetSpriteAsset;
static readonly List<Graphic> s_Graphics = new List<Graphic>(); static readonly List<Graphic> s_Graphics = new List<Graphic>();
Shader _shader;
Shader _mobileShader;
Shader _spriteShader;
List<MaterialEditor> _materialEditors = new List<MaterialEditor> ();
SerializedProperty _spMaskInteraction; SerializedProperty _spMaskInteraction;
List<Mask> tmpMasks = new List<Mask>();
static GUIContent s_MaskWarning;
void OnEnable ()
private void OnEnable()
{ {
_spMaskInteraction = serializedObject.FindProperty("m_MaskInteraction"); _spMaskInteraction = serializedObject.FindProperty("m_MaskInteraction");
_custom = (maskInteraction == MaskInteraction.Custom); _custom = (maskInteraction == MaskInteraction.Custom);
s_MaskWarning = new GUIContent(EditorGUIUtility.FindTexture("console.warnicon.sml"),
ClearMaterialEditors (); "This is not a SoftMask component.");
_shader = Shader.Find ("TextMeshPro/Distance Field (SoftMaskable)");
_mobileShader = Shader.Find ("TextMeshPro/Mobile/Distance Field (SoftMaskable)");
_spriteShader = Shader.Find ("TextMeshPro/Sprite (SoftMaskable)");
if(s_TypeTMPro != null)
{
s_PiFontSharedMaterial = s_TypeTMPro.GetProperty ("fontSharedMaterial");
s_PiSpriteAsset = s_TypeTMPro.GetProperty ("spriteAsset");
s_PiRichText = s_TypeTMPro.GetProperty ("richText");
s_PiText = s_TypeTMPro.GetProperty ("text");
s_FiMaterial = s_TypeTMP_SpriteAsset.GetField ("material");
s_PiFontSharedMaterials = s_TypeTMPro.GetProperty ("fontSharedMaterials");
s_miGetSpriteAsset = s_TypeTMProSettings.GetMethod ("GetSpriteAsset", BindingFlags.Static | BindingFlags.Public);
s_PiDefaultFontAssetPath = s_TypeTMProSettings.GetProperty ("defaultFontAssetPath", BindingFlags.Static | BindingFlags.Public);
s_PiDefaultSpriteAssetPath = s_TypeTMProSettings.GetProperty ("defaultSpriteAssetPath", BindingFlags.Static | BindingFlags.Public);
} }
s_MaskWarning = new GUIContent(EditorGUIUtility.FindTexture("console.warnicon.sml"), "This component is not SoftMask. Use SoftMask instead of Mask.");
}
void OnDisable () private void DrawMaskInteractions()
{ {
ClearMaterialEditors (); var softMaskable = target as SoftMaskable;
} if (softMaskable == null) return;
List<Mask> tmpMasks = new List<Mask>(); softMaskable.GetComponentsInParent<Mask>(true, tmpMasks);
void DrawMaskInteractions()
{
(target as SoftMaskable).GetComponentsInParent<Mask>(true, tmpMasks);
tmpMasks.RemoveAll(x => !x.enabled); tmpMasks.RemoveAll(x => !x.enabled);
tmpMasks.Reverse(); tmpMasks.Reverse();
maskInteraction = (MaskInteraction) EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction); maskInteraction = (MaskInteraction) EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction);
if (_custom) if (!_custom) return;
{
var l = EditorGUIUtility.labelWidth; var l = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 45; EditorGUIUtility.labelWidth = 45;
@ -125,18 +85,17 @@ namespace Coffee.UISoftMask
int intr2 = DrawMaskInteraction(2); int intr2 = DrawMaskInteraction(2);
int intr3 = DrawMaskInteraction(3); int intr3 = DrawMaskInteraction(3);
if (ccs.changed) { if (ccs.changed)
{
_spMaskInteraction.intValue = (intr0 << 0) + (intr1 << 2) + (intr2 << 4) + (intr3 << 6); _spMaskInteraction.intValue = (intr0 << 0) + (intr1 << 2) + (intr2 << 4) + (intr3 << 6);
} }
} }
EditorGUIUtility.labelWidth = l; EditorGUIUtility.labelWidth = l;
} }
}
static GUIContent s_MaskWarning = new GUIContent();
int DrawMaskInteraction(int layer) private int DrawMaskInteraction(int layer)
{ {
Mask mask = layer < tmpMasks.Count ? tmpMasks[layer] : null; Mask mask = layer < tmpMasks.Count ? tmpMasks[layer] : null;
MaskIntr intr = (MaskIntr) ((_spMaskInteraction.intValue >> layer * 2) & 0x3); MaskIntr intr = (MaskIntr) ((_spMaskInteraction.intValue >> layer * 2) & 0x3);
@ -162,53 +121,20 @@ namespace Coffee.UISoftMask
serializedObject.Update(); serializedObject.Update();
DrawMaskInteractions(); DrawMaskInteractions();
// maskInteraction = (MaskInteraction)EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction);
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
/*
EditorGUI.indentLevel++;
var l = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 60;
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.ObjectField("Mask 0", null, typeof(Mask), false);
EditorGUILayout.EnumPopup (MaskIntr.None);
}
EditorGUIUtility.labelWidth = l;
EditorGUI.indentLevel--;
var spMaskInteraction = serializedObject.FindProperty ("m_MaskInteraction");
MaskIntr intr0 = (MaskIntr)((spMaskInteraction.intValue >> 0) & 0x3);
MaskIntr intr1 = (MaskIntr)((spMaskInteraction.intValue >> 2) & 0x3);
MaskIntr intr2 = (MaskIntr)((spMaskInteraction.intValue >> 4) & 0x3);
MaskIntr intr3 = (MaskIntr)((spMaskInteraction.intValue >> 6) & 0x3);
using (var ccs = new EditorGUI.ChangeCheckScope ()) {
intr0 = (MaskIntr)EditorGUILayout.EnumPopup ("Layer 0", intr0);
intr1 = (MaskIntr)EditorGUILayout.EnumPopup ("Layer 1", intr1);
intr2 = (MaskIntr)EditorGUILayout.EnumPopup ("Layer 2", intr2);
intr3 = (MaskIntr)EditorGUILayout.EnumPopup ("Layer 3", intr3);
if (ccs.changed) {
current.SetMaskInteractions (intr0,intr1,intr2,intr3);
}
}
*/
// spMaskInteraction.intValue = (intr0 << 0) | (intr1 << 2) | (intr2 << 4) | (intr3 << 6);
//
// serializedObject.ApplyModifiedProperties ();
var current = target as SoftMaskable; var current = target as SoftMaskable;
current.GetComponentsInChildren<Graphic>(true, s_Graphics); current.GetComponentsInChildren<Graphic>(true, s_Graphics);
var fixTargets = s_Graphics.Where (x => x.gameObject != current.gameObject && !x.GetComponent<SoftMaskable> () && (!x.GetComponent<Mask> () || x.GetComponent<Mask> ().showMaskGraphic)).ToList (); var fixTargets = s_Graphics.Where(x =>
x.gameObject != current.gameObject && !x.GetComponent<SoftMaskable>() &&
(!x.GetComponent<Mask>() || x.GetComponent<Mask>().showMaskGraphic)).ToList();
if (0 < fixTargets.Count) if (0 < fixTargets.Count)
{ {
GUILayout.BeginHorizontal(); 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(); GUILayout.BeginVertical();
if (GUILayout.Button("Fix")) if (GUILayout.Button("Fix"))
{ {
@ -217,197 +143,42 @@ namespace Coffee.UISoftMask
p.gameObject.AddComponent<SoftMaskable>(); p.gameObject.AddComponent<SoftMaskable>();
} }
} }
if (GUILayout.Button("Ping")) if (GUILayout.Button("Ping"))
{ {
EditorGUIUtility.PingObject(fixTargets[0]); EditorGUIUtility.PingObject(fixTargets[0]);
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
if(s_TypeTMPro != null)
{
ShowTMProWarning (_shader, _mobileShader, _spriteShader, m => { });
var textMeshPro = current.GetComponent (s_TypeTMPro);
if (textMeshPro != null)
{
Material [] fontSharedMaterials = s_PiFontSharedMaterials.GetValue (textMeshPro, new object [0]) as Material [];
ShowMaterialEditors (fontSharedMaterials, 1, fontSharedMaterials.Length - 1);
}
}
if (!DetectMask(current.transform.parent)) if (!DetectMask(current.transform.parent))
{ {
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
EditorGUILayout.HelpBox ("This is unnecessary SoftMaskable.\nCan't find any SoftMask components above.", MessageType.Warning); EditorGUILayout.HelpBox("This is unnecessary SoftMaskable.\nCan't find any SoftMask components above.",
MessageType.Warning);
if (GUILayout.Button("Remove", GUILayout.Height(40))) if (GUILayout.Button("Remove", GUILayout.Height(40)))
{ {
DestroyImmediate(current); DestroyImmediate(current);
EditorUtils.MarkPrefabDirty(); EditorUtils.MarkPrefabDirty();
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
} }
static bool DetectMask(Transform transform) static bool DetectMask(Transform transform)
{ {
if (transform == null) while (transform)
{ {
if (transform.GetComponent<SoftMask>()) return true;
transform = transform.parent;
}
return false; return false;
} }
if (transform.GetComponent<SoftMask> () != null)
{
return true;
}
return DetectMask (transform.parent);
}
void ClearMaterialEditors ()
{
foreach (var e in _materialEditors)
{
if (e)
{
DestroyImmediate (e);
}
}
_materialEditors.Clear ();
}
protected void ShowMaterialEditors (Material [] materials, int startIndex, int count)
{
for (int i = 0; i < count; i++)
{
if (_materialEditors.Count == i)
{
_materialEditors.Add (null);
}
var mat = materials [startIndex + i];
var editor = _materialEditors [i];
if (editor && editor.target != mat)
{
DestroyImmediate (editor);
editor = null;
}
if (!editor)
{
editor = _materialEditors [i] = Editor.CreateEditor (mat) as MaterialEditor;
}
editor.DrawHeader ();
editor.OnInspectorGUI ();
}
}
public void ShowTMProWarning (Shader shader, Shader mobileShader, Shader spriteShader, System.Action<Material> onCreatedMaterial)
{
var current = target as SoftMaskable;
var textMeshPro = current.GetComponent (s_TypeTMPro);
if (textMeshPro == null)
{
return;
}
Material fontSharedMaterial = s_PiFontSharedMaterial.GetValue (textMeshPro, new object [0]) as Material;
if (fontSharedMaterial == null)
{
return;
}
// Is the material preset for dissolve?
Material m = fontSharedMaterial;
if (m.shader != shader && m.shader != mobileShader)
{
EditorGUILayout.BeginHorizontal ();
EditorGUILayout.HelpBox (string.Format ("{0} requires '{1}' or '{2}' as a shader for material preset.", current.GetType ().Name, shader.name, mobileShader.name), MessageType.Warning);
if (GUILayout.Button ("Fix"))
{
var correctShader = m.shader.name.Contains ("Mobile") ? mobileShader : shader;
m = ModifyTMProMaterialPreset (m, correctShader, onCreatedMaterial);
s_PiFontSharedMaterial.SetValue (textMeshPro, m, new object [0]);
}
EditorGUILayout.EndHorizontal ();
return;
}
// Is the sprite asset for dissolve?
object spriteAsset = s_PiSpriteAsset.GetValue (textMeshPro, new object [0]) ?? s_miGetSpriteAsset.Invoke (null, new object [0]);
//TMP_SpriteAsset spriteAsset = textMeshPro.spriteAsset ?? TMP_Settings.GetSpriteAsset ();
m = s_FiMaterial.GetValue (spriteAsset) as Material;
bool hasSprite = (bool)s_PiRichText.GetValue (textMeshPro, new object [0]) && (s_PiText.GetValue (textMeshPro, new object [0]) as string).Contains ("<sprite=");
if (m && m.shader != spriteShader && hasSprite)
{
EditorGUILayout.BeginHorizontal ();
EditorGUILayout.HelpBox (string.Format ("{0} requires '{1}' as a shader for sprite asset.", GetType ().Name, spriteShader.name), MessageType.Warning);
if (GUILayout.Button ("Fix"))
{
current.GetComponentsInChildren (s_TypeTMP_SubMesh).Select (x => x.gameObject).ToList ().ForEach (DestroyImmediate);
current.GetComponentsInChildren (s_TypeTMP_SubMeshUI).Select (x => x.gameObject).ToList ().ForEach (DestroyImmediate);
spriteAsset = ModifyTMProSpriteAsset (m, _spriteShader, mat => { });
s_PiSpriteAsset.SetValue (textMeshPro, spriteAsset, new object [0]);
}
EditorGUILayout.EndHorizontal ();
return;
}
}
Material ModifyTMProMaterialPreset (Material baseMaterial, Shader shader, System.Action<Material> onCreatedMaterial)
{
string path = AssetDatabase.GetAssetPath (baseMaterial);
string filename = Path.GetFileNameWithoutExtension (path) + " (" + typeof (SoftMaskable).Name + ")";
string defaultAssetPath = s_PiDefaultFontAssetPath.GetValue (null, new object [0]) as string;
Material mat = Resources.Load<Material> (defaultAssetPath + filename);
if (!mat)
{
mat = new Material (baseMaterial)
{
shaderKeywords = baseMaterial.shaderKeywords,
shader = shader,
};
onCreatedMaterial (mat);
AssetDatabase.CreateAsset (mat, Path.GetDirectoryName (path) + "/" + filename + ".mat");
EditorUtility.FocusProjectWindow ();
EditorGUIUtility.PingObject (mat);
}
else
{
mat.shader = shader;
}
EditorUtility.SetDirty (mat);
return mat;
}
object ModifyTMProSpriteAsset (Material baseMaterial, Shader shader, System.Action<Material> onCreatedMaterial)
{
string path = AssetDatabase.GetAssetPath (baseMaterial);
string filename = Path.GetFileNameWithoutExtension (path) + " (" + typeof (SoftMaskable).Name + ")";
string defaultAssetPath = s_PiDefaultSpriteAssetPath.GetValue (null, new object [0]) as string;
Object spriteAsset = Resources.Load (defaultAssetPath + filename, s_TypeTMP_SpriteAsset);
if (spriteAsset == null)
{
AssetDatabase.CopyAsset (path, Path.GetDirectoryName (path) + "/" + filename + ".mat");
spriteAsset = Resources.Load (defaultAssetPath + filename, s_TypeTMP_SpriteAsset);
Material m = s_FiMaterial.GetValue (spriteAsset) as Material;
m.shader = shader;
m.name = shader.name;
onCreatedMaterial (m);
EditorUtility.FocusProjectWindow ();
EditorGUIUtility.PingObject (spriteAsset);
}
else
{
Material m = s_FiMaterial.GetValue (spriteAsset) as Material;
m.shader = shader;
}
EditorUtility.SetDirty (spriteAsset);
return spriteAsset;
}
} }
} }

View File

@ -26,9 +26,11 @@ namespace Coffee.UISoftMask
public class GraphicConnector public class GraphicConnector
{ {
private static readonly List<GraphicConnector> s_Connectors = new List<GraphicConnector>(); private static readonly List<GraphicConnector> s_Connectors = new List<GraphicConnector>();
private static readonly Dictionary<Type, GraphicConnector> s_ConnectorMap = new Dictionary<Type, GraphicConnector>();
private static readonly Dictionary<Type, GraphicConnector> s_ConnectorMap =
new Dictionary<Type, GraphicConnector>();
private static readonly GraphicConnector s_EmptyConnector = new GraphicConnector(); private static readonly GraphicConnector s_EmptyConnector = new GraphicConnector();
#if UNITY_EDITOR #if UNITY_EDITOR
@ -74,7 +76,6 @@ namespace Coffee.UISoftMask
} }
/// <summary> /// <summary>
/// Find effect shader. /// Find effect shader.
/// </summary> /// </summary>

View File

@ -1,7 +1,9 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Rendering; using UnityEngine.Rendering;
using UnityEngine.UI; using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace Coffee.UISoftMask namespace Coffee.UISoftMask
{ {
@ -11,9 +13,6 @@ namespace Coffee.UISoftMask
/// </summary> /// </summary>
public class SoftMask : Mask, IMeshModifier public class SoftMask : Mask, IMeshModifier
{ {
//################################
// Constant or Static Members.
//################################
/// <summary> /// <summary>
/// Desampling rate. /// Desampling rate.
/// </summary> /// </summary>
@ -44,25 +43,50 @@ namespace Coffee.UISoftMask
static bool s_UVStartsAtTop; static bool s_UVStartsAtTop;
static Shader s_SoftMaskShader;
//################################ static Texture2D s_ReadTexture;
// Serialize Members. static readonly List<SoftMask> s_ActiveSoftMasks = new List<SoftMask>();
//################################ static readonly List<SoftMask> s_TempRelatables = new List<SoftMask>();
[Tooltip("The desampling rate for soft mask buffer.")] static readonly Dictionary<int, Matrix4x4> s_previousViewProjectionMatrices = new Dictionary<int, Matrix4x4>();
[SerializeField] DesamplingRate m_DesamplingRate = DesamplingRate.None; static readonly Dictionary<int, Matrix4x4> s_nowViewProjectionMatrices = new Dictionary<int, Matrix4x4>();
[Tooltip("The value used by the soft mask to select the area of influence defined over the soft mask's graphic.")] static int s_StencilCompId;
[SerializeField][Range(0.01f, 1)] float m_Softness = 1; static int s_ColorMaskId;
[Tooltip("The transparency of the whole masked graphic.")] static int s_MainTexId;
[SerializeField][Range(0f, 1f)] float m_Alpha = 1; static int s_SoftnessId;
[Tooltip("Should the soft mask ignore parent soft masks?")] static int s_GameVPId;
[SerializeField] bool m_IgnoreParent = false; static int s_GameTVPId;
[Tooltip("Is the soft mask a part of parent soft mask?")] static int s_Alpha;
[SerializeField] bool m_PartOfParent = false; MaterialPropertyBlock _mpb;
CommandBuffer _cb;
Material _material;
RenderTexture _softMaskBuffer;
int _stencilDepth;
Mesh _mesh;
SoftMask _parent;
readonly List<SoftMask> _children = new List<SoftMask>();
bool _hasChanged = false;
bool _hasStencilStateChanged = false;
[Tooltip("The desampling rate for soft mask buffer.")] [SerializeField]
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;
[Tooltip("The transparency of the whole masked graphic.")] [SerializeField] [Range(0f, 1f)]
float m_Alpha = 1;
[Tooltip("Should the soft mask ignore parent soft masks?")] [SerializeField]
bool m_IgnoreParent = false;
[Tooltip("Is the soft mask a part of parent soft mask?")] [SerializeField]
bool m_PartOfParent = false;
//################################
// Public Members.
//################################
/// <summary> /// <summary>
/// The desampling rate for soft mask buffer. /// The desampling rate for soft mask buffer.
/// </summary> /// </summary>
@ -71,13 +95,11 @@ namespace Coffee.UISoftMask
get { return m_DesamplingRate; } get { return m_DesamplingRate; }
set set
{ {
if (m_DesamplingRate != value) if (m_DesamplingRate == value) return;
{
m_DesamplingRate = value; m_DesamplingRate = value;
hasChanged = true; hasChanged = true;
} }
} }
}
/// <summary> /// <summary>
/// The value used by the soft mask to select the area of influence defined over the soft mask's graphic. /// The value used by the soft mask to select the area of influence defined over the soft mask's graphic.
@ -88,13 +110,11 @@ namespace Coffee.UISoftMask
set set
{ {
value = Mathf.Clamp01(value); value = Mathf.Clamp01(value);
if (m_Softness != value) if (Mathf.Approximately(m_Softness, value)) return;
{
m_Softness = value; m_Softness = value;
hasChanged = true; hasChanged = true;
} }
} }
}
/// <summary> /// <summary>
/// The transparency of the whole masked graphic. /// The transparency of the whole masked graphic.
@ -105,13 +125,11 @@ namespace Coffee.UISoftMask
set set
{ {
value = Mathf.Clamp01(value); value = Mathf.Clamp01(value);
if (m_Alpha != value) if (Mathf.Approximately(m_Alpha, value)) return;
{
m_Alpha = value; m_Alpha = value;
hasChanged = true; hasChanged = true;
} }
} }
}
/// <summary> /// <summary>
/// Should the soft mask ignore parent soft masks? /// Should the soft mask ignore parent soft masks?
@ -122,14 +140,12 @@ namespace Coffee.UISoftMask
get { return m_IgnoreParent; } get { return m_IgnoreParent; }
set set
{ {
if (m_IgnoreParent != value) if (m_IgnoreParent == value) return;
{
m_IgnoreParent = value; m_IgnoreParent = value;
hasChanged = true; hasChanged = true;
OnTransformParentChanged(); OnTransformParentChanged();
} }
} }
}
/// <summary> /// <summary>
/// Is the soft mask a part of parent soft mask? /// Is the soft mask a part of parent soft mask?
@ -139,14 +155,12 @@ namespace Coffee.UISoftMask
get { return m_PartOfParent; } get { return m_PartOfParent; }
set set
{ {
if (m_PartOfParent != value) if (m_PartOfParent == value) return;
{
m_PartOfParent = value; m_PartOfParent = value;
hasChanged = true; hasChanged = true;
OnTransformParentChanged(); OnTransformParentChanged();
} }
} }
}
/// <summary> /// <summary>
/// The soft mask buffer. /// The soft mask buffer.
@ -157,7 +171,7 @@ namespace Coffee.UISoftMask
{ {
if (_parent) if (_parent)
{ {
ReleaseRT(ref _softMaskBuffer); ReleaseRt(ref _softMaskBuffer);
return _parent.softMaskBuffer; return _parent.softMaskBuffer;
} }
@ -166,12 +180,13 @@ namespace Coffee.UISoftMask
GetDesamplingSize(m_DesamplingRate, out w, out h); GetDesamplingSize(m_DesamplingRate, out w, out h);
if (_softMaskBuffer && (_softMaskBuffer.width != w || _softMaskBuffer.height != h)) if (_softMaskBuffer && (_softMaskBuffer.width != w || _softMaskBuffer.height != h))
{ {
ReleaseRT(ref _softMaskBuffer); ReleaseRt(ref _softMaskBuffer);
} }
if (!_softMaskBuffer) if (!_softMaskBuffer)
{ {
_softMaskBuffer = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default); _softMaskBuffer = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGB32,
RenderTextureReadWrite.Default);
hasChanged = true; hasChanged = true;
_hasStencilStateChanged = true; _hasStencilStateChanged = true;
} }
@ -182,28 +197,42 @@ namespace Coffee.UISoftMask
public bool hasChanged public bool hasChanged
{ {
get get { return _parent ? _parent.hasChanged : _hasChanged; }
{
return _parent ? _parent.hasChanged : _hasChanged;
}
private set private set
{ {
if (_parent) if (_parent)
{ {
_parent.hasChanged = value; _parent.hasChanged = value;
} }
_hasChanged = value; _hasChanged = value;
} }
} }
public SoftMask parent public SoftMask parent
{
get { return _parent; }
}
Material material
{ {
get get
{ {
return _parent; return _material
? _material
: _material =
new Material(s_SoftMaskShader
? s_SoftMaskShader
: s_SoftMaskShader = Resources.Load<Shader>("SoftMask"))
{hideFlags = HideFlags.HideAndDontSave};
} }
} }
Mesh mesh
{
get { return _mesh ? _mesh : _mesh = new Mesh() {hideFlags = HideFlags.HideAndDontSave}; }
}
/// <summary> /// <summary>
/// Perform material modification in this function. /// Perform material modification in this function.
@ -218,6 +247,7 @@ namespace Coffee.UISoftMask
{ {
result.SetInt(s_StencilCompId, (int) CompareFunction.Always); result.SetInt(s_StencilCompId, (int) CompareFunction.Always);
} }
return result; return result;
} }
@ -237,9 +267,7 @@ namespace Coffee.UISoftMask
void IMeshModifier.ModifyMesh(VertexHelper verts) void IMeshModifier.ModifyMesh(VertexHelper verts)
{ {
if (isActiveAndEnabled) if (isActiveAndEnabled)
{
verts.FillMesh(mesh); verts.FillMesh(mesh);
}
hasChanged = true; hasChanged = true;
} }
@ -252,10 +280,7 @@ namespace Coffee.UISoftMask
/// <param name="g">Target graphic.</param> /// <param name="g">Target graphic.</param>
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera, Graphic g, int[] interactions) public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera, Graphic g, int[] interactions)
{ {
if (!isActiveAndEnabled || (g == graphic && !g.raycastTarget)) if (!isActiveAndEnabled || (g == graphic && !g.raycastTarget)) return true;
{
return true;
}
int x = (int) ((softMaskBuffer.width - 1) * Mathf.Clamp01(sp.x / Screen.width)); int x = (int) ((softMaskBuffer.width - 1) * Mathf.Clamp01(sp.x / Screen.width));
int y = s_UVStartsAtTop int y = s_UVStartsAtTop
@ -269,10 +294,6 @@ namespace Coffee.UISoftMask
return true; return true;
} }
//################################
// Protected Members.
//################################
/// <summary> /// <summary>
/// This function is called when the object becomes enabled and active. /// This function is called when the object becomes enabled and active.
/// </summary> /// </summary>
@ -299,6 +320,7 @@ namespace Coffee.UISoftMask
#endif #endif
} }
} }
s_ActiveSoftMasks.Add(this); s_ActiveSoftMasks.Add(this);
// Reset the parent-child relation. // Reset the parent-child relation.
@ -307,6 +329,7 @@ namespace Coffee.UISoftMask
{ {
s_TempRelatables[i].OnTransformParentChanged(); s_TempRelatables[i].OnTransformParentChanged();
} }
s_TempRelatables.Clear(); s_TempRelatables.Clear();
// Create objects. // Create objects.
@ -336,6 +359,7 @@ namespace Coffee.UISoftMask
{ {
_children[i].SetParent(_parent); _children[i].SetParent(_parent);
} }
_children.Clear(); _children.Clear();
SetParent(null); SetParent(null);
@ -349,7 +373,7 @@ namespace Coffee.UISoftMask
_mesh = null; _mesh = null;
ReleaseObject(_material); ReleaseObject(_material);
_material = null; _material = null;
ReleaseRT(ref _softMaskBuffer); ReleaseRt(ref _softMaskBuffer);
base.OnDisable(); base.OnDisable();
_hasStencilStateChanged = false; _hasStencilStateChanged = false;
@ -371,6 +395,7 @@ namespace Coffee.UISoftMask
parentTransform = parentTransform.parent; parentTransform = parentTransform.parent;
} }
} }
SetParent(newParent); SetParent(newParent);
hasChanged = true; hasChanged = true;
} }
@ -381,10 +406,6 @@ namespace Coffee.UISoftMask
} }
#if UNITY_EDITOR #if UNITY_EDITOR
/// <summary>
/// Update the scene view matrix for shader.
/// </summary>
/// <summary> /// <summary>
/// This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only). /// This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).
/// </summary> /// </summary>
@ -397,37 +418,6 @@ namespace Coffee.UISoftMask
} }
#endif #endif
//################################
// Private Members.
//################################
static Shader s_SoftMaskShader;
static Texture2D s_ReadTexture;
static List<SoftMask> s_ActiveSoftMasks = new List<SoftMask>();
static List<SoftMask> s_TempRelatables = new List<SoftMask>();
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;
List<SoftMask> _children = new List<SoftMask>();
bool _hasChanged = false;
bool _hasStencilStateChanged = false;
static readonly Dictionary<int, Matrix4x4> s_previousViewProjectionMatrices = new Dictionary<int, Matrix4x4> ();
static readonly Dictionary<int, Matrix4x4> s_nowViewProjectionMatrices = new Dictionary<int, Matrix4x4> ();
Material material { get { return _material ? _material : _material = new Material(s_SoftMaskShader ? s_SoftMaskShader : s_SoftMaskShader = Resources.Load<Shader>("SoftMask")){ hideFlags = HideFlags.HideAndDontSave }; } }
Mesh mesh { get { return _mesh ? _mesh : _mesh = new Mesh(){ hideFlags = HideFlags.HideAndDontSave }; } }
/// <summary> /// <summary>
/// Update all soft mask textures. /// Update all soft mask textures.
/// </summary> /// </summary>
@ -448,10 +438,9 @@ namespace Coffee.UISoftMask
if (!cam) if (!cam)
continue; continue;
Matrix4x4 nowVP = cam.projectionMatrix * cam.worldToCameraMatrix; var nowVP = cam.projectionMatrix * cam.worldToCameraMatrix;
var previousVP = default(Matrix4x4);
Matrix4x4 previousVP = default(Matrix4x4); var id = cam.GetInstanceID();
int id = cam.GetInstanceID ();
s_previousViewProjectionMatrices.TryGetValue(id, out previousVP); s_previousViewProjectionMatrices.TryGetValue(id, out previousVP);
s_nowViewProjectionMatrices[id] = nowVP; s_nowViewProjectionMatrices[id] = nowVP;
@ -481,55 +470,51 @@ namespace Coffee.UISoftMask
continue; continue;
sm._hasChanged = false; sm._hasChanged = false;
if (!sm._parent) if (sm._parent) continue;
{
sm.UpdateMaskTexture(); sm.UpdateMaskTexture();
if (sm._hasStencilStateChanged)
{ if (!sm._hasStencilStateChanged) continue;
sm._hasStencilStateChanged = false; sm._hasStencilStateChanged = false;
MaskUtilities.NotifyStencilStateChanged(sm); MaskUtilities.NotifyStencilStateChanged(sm);
} }
}
}
s_previousViewProjectionMatrices.Clear(); s_previousViewProjectionMatrices.Clear();
foreach (int id in s_nowViewProjectionMatrices.Keys) 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();
} }
/// <summary> /// <summary>
/// Update the mask texture. /// Update the mask texture.
/// </summary> /// </summary>
void UpdateMaskTexture() private void UpdateMaskTexture()
{ {
if (!graphic || !graphic.canvas) if (!graphic || !graphic.canvas) return;
{
return;
}
_stencilDepth = MaskUtilities.GetStencilDepth(transform, MaskUtilities.FindRootSortOverrideCanvas(transform)); _stencilDepth =
MaskUtilities.GetStencilDepth(transform, MaskUtilities.FindRootSortOverrideCanvas(transform));
// Collect children soft masks. // Collect children soft masks.
int depth = 0; var depth = 0;
s_TmpSoftMasks[0].Add(this); s_TmpSoftMasks[0].Add(this);
while (_stencilDepth + depth < 3) while (_stencilDepth + depth < 3)
{ {
int count = s_TmpSoftMasks[depth].Count; var count = s_TmpSoftMasks[depth].Count;
for (int i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
List<SoftMask> children = s_TmpSoftMasks[depth][i]._children; List<SoftMask> children = s_TmpSoftMasks[depth][i]._children;
int childCount = children.Count; var childCount = children.Count;
for (int j = 0; j < childCount; j++) for (var j = 0; j < childCount; j++)
{ {
var child = children[j]; var child = children[j];
var childDepth = child.m_PartOfParent ? depth : depth + 1; var childDepth = child.m_PartOfParent ? depth : depth + 1;
s_TmpSoftMasks[childDepth].Add(child); s_TmpSoftMasks[childDepth].Add(child);
} }
} }
depth++; depth++;
} }
@ -543,7 +528,8 @@ namespace Coffee.UISoftMask
var cam = c.worldCamera ?? Camera.main; var cam = c.worldCamera ?? Camera.main;
if (c && c.renderMode != RenderMode.ScreenSpaceOverlay && cam) if (c && c.renderMode != RenderMode.ScreenSpaceOverlay && cam)
{ {
_cb.SetViewProjectionMatrices(cam.worldToCameraMatrix, GL.GetGPUProjectionMatrix(cam.projectionMatrix, false)); _cb.SetViewProjectionMatrices(cam.worldToCameraMatrix,
GL.GetGPUProjectionMatrix(cam.projectionMatrix, false));
#if UNITY_EDITOR #if UNITY_EDITOR
var pv = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false) * cam.worldToCameraMatrix; var pv = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false) * cam.worldToCameraMatrix;
@ -555,28 +541,34 @@ namespace Coffee.UISoftMask
{ {
var pos = c.transform.position; var pos = c.transform.position;
var vm = Matrix4x4.TRS(new Vector3(-pos.x, -pos.y, -1000), Quaternion.identity, new Vector3(1, 1, -1f)); var vm = Matrix4x4.TRS(new Vector3(-pos.x, -pos.y, -1000), Quaternion.identity, new Vector3(1, 1, -1f));
var pm = Matrix4x4.TRS(new Vector3(0, 0, -1), Quaternion.identity, new Vector3(1 / pos.x, 1 / pos.y, -2 / 10000f)); var pm = Matrix4x4.TRS(new Vector3(0, 0, -1), Quaternion.identity,
new Vector3(1 / pos.x, 1 / pos.y, -2 / 10000f));
_cb.SetViewProjectionMatrices(vm, pm); _cb.SetViewProjectionMatrices(vm, pm);
#if UNITY_EDITOR #if UNITY_EDITOR
var scale = c.transform.localScale.x; var scale = c.transform.localScale.x;
var size = (c.transform as RectTransform).sizeDelta; var size = (c.transform as RectTransform).sizeDelta;
_cb.SetGlobalMatrix(s_GameVPId, Matrix4x4.TRS(new Vector3(0, 0, 0.5f), Quaternion.identity, new Vector3(2 / size.x, 2 / size.y, 0.0005f * scale))); _cb.SetGlobalMatrix(s_GameVPId,
_cb.SetGlobalMatrix(s_GameTVPId, Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, new Vector3(1 / pos.x, 1 / pos.y, -2 / 2000f)) * Matrix4x4.Translate(-pos)); Matrix4x4.TRS(new Vector3(0, 0, 0.5f), Quaternion.identity,
new Vector3(2 / size.x, 2 / size.y, 0.0005f * scale)));
_cb.SetGlobalMatrix(s_GameTVPId,
Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity,
new Vector3(1 / pos.x, 1 / pos.y, -2 / 2000f)) * Matrix4x4.Translate(-pos));
#endif #endif
} }
// Draw soft masks. // Draw soft masks.
for (int i = 0; i < s_TmpSoftMasks.Length; i++) for (var i = 0; i < s_TmpSoftMasks.Length; i++)
{ {
int count = s_TmpSoftMasks[i].Count; var count = s_TmpSoftMasks[i].Count;
for (int j = 0; j < count; j++) for (var j = 0; j < count; j++)
{ {
var sm = s_TmpSoftMasks[i][j]; var sm = s_TmpSoftMasks[i][j];
if (i != 0) if (i != 0)
{ {
sm._stencilDepth = MaskUtilities.GetStencilDepth(sm.transform, MaskUtilities.FindRootSortOverrideCanvas(sm.transform)); sm._stencilDepth = MaskUtilities.GetStencilDepth(sm.transform,
MaskUtilities.FindRootSortOverrideCanvas(sm.transform));
} }
// Set material property. // Set material property.
@ -588,6 +580,7 @@ namespace Coffee.UISoftMask
// Draw mesh. // Draw mesh.
_cb.DrawMesh(sm.mesh, sm.transform.localToWorldMatrix, sm.material, 0, 0, sm._mpb); _cb.DrawMesh(sm.mesh, sm.transform.localToWorldMatrix, sm.material, 0, 0, sm._mpb);
} }
s_TmpSoftMasks[i].Clear(); s_TmpSoftMasks[i].Clear();
} }
@ -597,7 +590,7 @@ namespace Coffee.UISoftMask
/// <summary> /// <summary>
/// Gets the size of the desampling. /// Gets the size of the desampling.
/// </summary> /// </summary>
void GetDesamplingSize(DesamplingRate rate, out int w, out int h) private static void GetDesamplingSize(DesamplingRate rate, out int w, out int h)
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
var res = UnityEditor.UnityStats.screenRes.Split('x'); var res = UnityEditor.UnityStats.screenRes.Split('x');
@ -611,7 +604,7 @@ namespace Coffee.UISoftMask
if (rate == DesamplingRate.None) if (rate == DesamplingRate.None)
return; return;
float aspect = (float)w / h; var aspect = (float) w / h;
if (w < h) if (w < h)
{ {
h = Mathf.ClosestPowerOfTwo(h / (int) rate); h = Mathf.ClosestPowerOfTwo(h / (int) rate);
@ -628,24 +621,21 @@ namespace Coffee.UISoftMask
/// Release the specified obj. /// Release the specified obj.
/// </summary> /// </summary>
/// <param name="obj">Object.</param> /// <param name="obj">Object.</param>
void ReleaseRT(ref RenderTexture tmpRT) private static void ReleaseRt(ref RenderTexture tmpRT)
{
if (tmpRT)
{ {
if (!tmpRT) return;
tmpRT.Release(); tmpRT.Release();
RenderTexture.ReleaseTemporary(tmpRT); RenderTexture.ReleaseTemporary(tmpRT);
tmpRT = null; tmpRT = null;
} }
}
/// <summary> /// <summary>
/// Release the specified obj. /// Release the specified obj.
/// </summary> /// </summary>
/// <param name="obj">Object.</param> /// <param name="obj">Object.</param>
void ReleaseObject(Object obj) private static void ReleaseObject(Object obj)
{
if (obj)
{ {
if (!obj) return;
#if UNITY_EDITOR #if UNITY_EDITOR
if (!Application.isPlaying) if (!Application.isPlaying)
DestroyImmediate(obj); DestroyImmediate(obj);
@ -654,14 +644,13 @@ namespace Coffee.UISoftMask
Destroy(obj); Destroy(obj);
obj = null; obj = null;
} }
}
/// <summary> /// <summary>
/// Set the parent of the soft mask. /// Set the parent of the soft mask.
/// </summary> /// </summary>
/// <param name="newParent">The parent soft mask to use.</param> /// <param name="newParent">The parent soft mask to use.</param>
void SetParent(SoftMask newParent) private void SetParent(SoftMask newParent)
{ {
if (_parent != newParent && this != newParent) if (_parent != newParent && this != newParent)
{ {
@ -670,6 +659,7 @@ namespace Coffee.UISoftMask
_parent._children.Remove(this); _parent._children.Remove(this);
_parent._children.RemoveAll(x => x == null); _parent._children.RemoveAll(x => x == null);
} }
_parent = newParent; _parent = newParent;
} }
@ -682,18 +672,19 @@ namespace Coffee.UISoftMask
/// <summary> /// <summary>
/// Gets the pixel value. /// Gets the pixel value.
/// </summary> /// </summary>
float GetPixelValue(int x, int y, int[] interactions) private float GetPixelValue(int x, int y, int[] interactions)
{ {
if (!s_ReadTexture) if (!s_ReadTexture)
{ {
s_ReadTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false); s_ReadTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
} }
var currentRT = RenderTexture.active;
var currentRt = RenderTexture.active;
RenderTexture.active = softMaskBuffer; RenderTexture.active = softMaskBuffer;
s_ReadTexture.ReadPixels(new Rect(x, y, 1, 1), 0, 0); s_ReadTexture.ReadPixels(new Rect(x, y, 1, 1), 0, 0);
s_ReadTexture.Apply(false, false); s_ReadTexture.Apply(false, false);
RenderTexture.active = currentRT; RenderTexture.active = currentRt;
var colors = s_ReadTexture.GetRawTextureData(); var colors = s_ReadTexture.GetRawTextureData();
@ -701,8 +692,12 @@ namespace Coffee.UISoftMask
{ {
switch (interactions[(i + 3) % 4]) switch (interactions[(i + 3) % 4])
{ {
case 0: colors[i] = 255; break; case 0:
case 2: colors[i] = (byte)(255 - colors[i]); break; colors[i] = 255;
break;
case 2:
colors[i] = (byte) (255 - colors[i]);
break;
} }
} }

View File

@ -24,34 +24,70 @@ namespace Coffee.UISoftMask
const int kVisibleOutside = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6); const int kVisibleOutside = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6);
static readonly Hash128 k_InvalidHash = new Hash128(); static readonly Hash128 k_InvalidHash = new Hash128();
[Tooltip("The graphic will be visible only in areas where no mask is present.")]
[System.Obsolete]
[HideInInspector]
[SerializeField] bool m_Inverse = false;
[Tooltip("The interaction for each masks.")]
[HideInInspector]
[SerializeField] int m_MaskInteraction = kVisibleInside;
[Tooltip("Use stencil to mask.")]
[SerializeField] bool m_UseStencil = false;
[Tooltip("Use soft-masked raycast target.\n\nNote: This option is expensive.")]
[SerializeField] bool m_RaycastFilter = false;
Graphic _graphic = null;
SoftMask _softMask = null;
Material _maskMaterial = null;
static int s_SoftMaskTexId; static int s_SoftMaskTexId;
static int s_StencilCompId; static int s_StencilCompId;
static int s_MaskInteractionId; static int s_MaskInteractionId;
static List<SoftMaskable> s_ActiveSoftMaskables; static List<SoftMaskable> s_ActiveSoftMaskables;
static int[] s_Interactions = new int[4]; 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;
[Tooltip("The interaction for each masks.")] [HideInInspector] [SerializeField]
int m_MaskInteraction = kVisibleInside;
[Tooltip("Use stencil to mask.")] [SerializeField]
bool m_UseStencil = false;
[Tooltip("Use soft-masked raycast target.\n\nNote: This option is expensive.")] [SerializeField]
bool m_RaycastFilter = false;
Graphic _graphic = null;
SoftMask _softMask = null;
Material _maskMaterial = null;
Hash128 _effectMaterialHash; Hash128 _effectMaterialHash;
/// <summary>
/// The graphic will be visible only in areas where no mask is present.
/// </summary>
public bool inverse
{
get { return m_MaskInteraction == kVisibleOutside; }
set
{
var intValue = value ? kVisibleOutside : kVisibleInside;
if (m_MaskInteraction == intValue) return;
m_MaskInteraction = intValue;
graphic.SetMaterialDirtyEx();
}
}
/// <summary>
/// Use soft-masked raycast target. This option is expensive.
/// </summary>
public bool raycastFilter
{
get { return m_RaycastFilter; }
set { m_RaycastFilter = value; }
}
/// <summary>
/// The graphic associated with the soft mask.
/// </summary>
public Graphic graphic
{
get { return _graphic ? _graphic : _graphic = GetComponent<Graphic>(); }
}
/// <summary> /// <summary>
/// Perform material modification in this function. /// Perform material modification in this function.
/// </summary> /// </summary>
/// <returns>Modified material.</returns> /// <returns>Modified material.</returns>
/// <param name="baseMaterial">Configured Material.</param> /// <param name="baseMaterial">Configured Material.</param>
public Material GetModifiedMaterial(Material baseMaterial) Material IMaterialModifier.GetModifiedMaterial(Material baseMaterial)
{ {
_softMask = null; _softMask = null;
if (!isActiveAndEnabled) if (!isActiveAndEnabled)
@ -69,6 +105,7 @@ namespace Coffee.UISoftMask
_softMask = sm; _softMask = sm;
break; break;
} }
parentTransform = parentTransform.parent; parentTransform = parentTransform.parent;
} }
@ -79,13 +116,13 @@ namespace Coffee.UISoftMask
_effectMaterialHash = GetMaterialHash(baseMaterial); _effectMaterialHash = GetMaterialHash(baseMaterial);
modifiedMaterial = MaterialCache.Register(baseMaterial, _effectMaterialHash, mat => modifiedMaterial = MaterialCache.Register(baseMaterial, _effectMaterialHash, mat =>
{ {
Debug.Log(mat.shader.name);
mat.shader = Shader.Find(string.Format("Hidden/{0} (SoftMaskable)", mat.shader.name)); mat.shader = Shader.Find(string.Format("Hidden/{0} (SoftMaskable)", mat.shader.name));
#if UNITY_EDITOR #if UNITY_EDITOR
mat.EnableKeyword("SOFTMASK_EDITOR"); mat.EnableKeyword("SOFTMASK_EDITOR");
#endif #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.SetInt(s_StencilCompId,
m_UseStencil ? (int) CompareFunction.Equal : (int) CompareFunction.Always);
mat.SetVector(s_MaskInteractionId, new Vector4( mat.SetVector(s_MaskInteractionId, new Vector4(
(m_MaskInteraction & 0x3), (m_MaskInteraction & 0x3),
((m_MaskInteraction >> 2) & 0x3), ((m_MaskInteraction >> 2) & 0x3),
@ -120,7 +157,7 @@ namespace Coffee.UISoftMask
/// <returns>Valid.</returns> /// <returns>Valid.</returns>
/// <param name="sp">Screen position.</param> /// <param name="sp">Screen position.</param>
/// <param name="eventCamera">Raycast camera.</param> /// <param name="eventCamera">Raycast camera.</param>
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera) bool ICanvasRaycastFilter.IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{ {
if (!isActiveAndEnabled || !_softMask) if (!isActiveAndEnabled || !_softMask)
return true; return true;
@ -130,7 +167,7 @@ namespace Coffee.UISoftMask
return true; return true;
var sm = _softMask; var sm = _softMask;
for (int i = 0; i < 4; i++) for (var i = 0; i < 4; i++)
{ {
s_Interactions[i] = sm ? ((m_MaskInteraction >> i * 2) & 0x3) : 0; s_Interactions[i] = sm ? ((m_MaskInteraction >> i * 2) & 0x3) : 0;
sm = sm ? sm.parent : null; sm = sm ? sm.parent : null;
@ -139,38 +176,6 @@ namespace Coffee.UISoftMask
return _softMask.IsRaycastLocationValid(sp, eventCamera, graphic, s_Interactions); return _softMask.IsRaycastLocationValid(sp, eventCamera, graphic, s_Interactions);
} }
/// <summary>
/// The graphic will be visible only in areas where no mask is present.
/// </summary>
public bool inverse
{
get { return m_MaskInteraction == kVisibleOutside; }
set
{
int intValue = value ? kVisibleOutside : kVisibleInside;
if (m_MaskInteraction != intValue)
{
m_MaskInteraction = intValue;
graphic.SetMaterialDirtyEx();
}
}
}
/// <summary>
/// Use soft-masked raycast target. This option is expensive.
/// </summary>
public bool raycastFilter
{
get { return m_RaycastFilter; }
set { m_RaycastFilter = value; }
}
/// <summary>
/// The graphic associated with the soft mask.
/// </summary>
public Graphic graphic{ get { return _graphic ? _graphic : _graphic = GetComponent<Graphic>(); } }
/// <summary> /// <summary>
/// Set the interaction for each mask. /// Set the interaction for each mask.
/// </summary> /// </summary>
@ -182,7 +187,8 @@ namespace Coffee.UISoftMask
/// <summary> /// <summary>
/// Set the interaction for each mask. /// Set the interaction for each mask.
/// </summary> /// </summary>
public void SetMaskInteraction(SpriteMaskInteraction layer0, SpriteMaskInteraction layer1, SpriteMaskInteraction layer2, SpriteMaskInteraction layer3) public void SetMaskInteraction(SpriteMaskInteraction layer0, SpriteMaskInteraction layer1,
SpriteMaskInteraction layer2, SpriteMaskInteraction layer3)
{ {
m_MaskInteraction = (int) layer0 + ((int) layer1 << 2) + ((int) layer2 << 4) + ((int) layer3 << 6); m_MaskInteraction = (int) layer0 + ((int) layer1 << 2) + ((int) layer2 << 4) + ((int) layer3 << 6);
graphic.SetMaterialDirtyEx(); graphic.SetMaterialDirtyEx();
@ -192,7 +198,7 @@ namespace Coffee.UISoftMask
/// <summary> /// <summary>
/// This function is called when the object becomes enabled and active. /// This function is called when the object becomes enabled and active.
/// </summary> /// </summary>
void OnEnable() private void OnEnable()
{ {
// Register. // Register.
if (s_ActiveSoftMaskables == null) if (s_ActiveSoftMaskables == null)
@ -203,32 +209,23 @@ namespace Coffee.UISoftMask
s_StencilCompId = Shader.PropertyToID("_StencilComp"); s_StencilCompId = Shader.PropertyToID("_StencilComp");
s_MaskInteractionId = Shader.PropertyToID("_MaskInteraction"); s_MaskInteractionId = Shader.PropertyToID("_MaskInteraction");
} }
s_ActiveSoftMaskables.Add(this); s_ActiveSoftMaskables.Add(this);
graphic.SetMaterialDirtyEx();
var g = graphic;
if (g)
{
g.SetMaterialDirty();
}
_softMask = null; _softMask = null;
} }
/// <summary> /// <summary>
/// This function is called when the behaviour becomes disabled. /// This function is called when the behaviour becomes disabled.
/// </summary> /// </summary>
void OnDisable() private void OnDisable()
{ {
s_ActiveSoftMaskables.Remove(this); s_ActiveSoftMaskables.Remove(this);
var g = graphic; graphic.SetMaterialDirtyEx();
if (g)
{
g.SetMaterialDirty();
}
ReleaseMaterial(ref _maskMaterial);
_softMask = null; _softMask = null;
ReleaseMaterial(ref _maskMaterial);
MaterialCache.Unregister(_effectMaterialHash); MaterialCache.Unregister(_effectMaterialHash);
_effectMaterialHash = k_InvalidHash; _effectMaterialHash = k_InvalidHash;
@ -237,23 +234,19 @@ namespace Coffee.UISoftMask
/// <summary> /// <summary>
/// Release the material. /// Release the material.
/// </summary> /// </summary>
void ReleaseMaterial(ref Material mat) static void ReleaseMaterial(ref Material mat)
{
if (mat)
{ {
if (!mat) return;
#if UNITY_EDITOR #if UNITY_EDITOR
if (!Application.isPlaying) if (!Application.isPlaying)
{
DestroyImmediate(mat); DestroyImmediate(mat);
}
else else
#endif #endif
{
Destroy(mat); Destroy(mat);
}
mat = null; mat = null;
} }
}
#if UNITY_EDITOR #if UNITY_EDITOR
@ -282,9 +275,9 @@ namespace Coffee.UISoftMask
var current = this; var current = this;
UnityEditor.EditorApplication.delayCall += () => UnityEditor.EditorApplication.delayCall += () =>
{ {
if (current && graphic && graphic.material && graphic.material.shader && graphic.material.shader.name == "Hidden/UI/Default (SoftMaskable)") if (current && graphic && graphic.material && graphic.material.shader &&
graphic.material.shader.name == "Hidden/UI/Default (SoftMaskable)")
{ {
Debug.LogFormat("OnAfterDeserialize: reset material {0}",current);
graphic.material = null; graphic.material = null;
graphic.SetMaterialDirtyEx(); graphic.SetMaterialDirtyEx();
} }