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

@ -5,55 +5,55 @@ using UnityEngine.UI;
namespace Coffee.UISoftMask.Demos namespace Coffee.UISoftMask.Demos
{ {
public class SoftMask_Demo : MonoBehaviour public class SoftMask_Demo : MonoBehaviour
{ {
[SerializeField] RawImage[] softMaskBufferViewer; [SerializeField] RawImage[] softMaskBufferViewer;
[SerializeField] SoftMask[] softMask; [SerializeField] SoftMask[] softMask;
[SerializeField] Text text; [SerializeField] Text text;
[SerializeField] GameObject title; [SerializeField] GameObject title;
// Use this for initialization // Use this for initialization
void OnEnable() void OnEnable()
{ {
title.SetActive(true); title.SetActive(true);
text.text = string.Format("GPU: {0}\nDeviceType: {1}\nShaderLevel: {2}\nUVStartsAtTop: {3}", text.text = string.Format("GPU: {0}\nDeviceType: {1}\nShaderLevel: {2}\nUVStartsAtTop: {3}",
SystemInfo.graphicsDeviceName, SystemInfo.graphicsDeviceName,
SystemInfo.graphicsDeviceType, SystemInfo.graphicsDeviceType,
SystemInfo.graphicsShaderLevel, SystemInfo.graphicsShaderLevel,
SystemInfo.graphicsUVStartsAtTop); SystemInfo.graphicsUVStartsAtTop);
for (int i = 0; i < softMask.Length; i++) for (int i = 0; i < softMask.Length; i++)
{ {
softMaskBufferViewer[i].texture = softMask[i].softMaskBuffer; softMaskBufferViewer[i].texture = softMask[i].softMaskBuffer;
} }
} }
public void SetWorldSpase(bool flag) public void SetWorldSpase(bool flag)
{ {
if(flag) if (flag)
{ {
GetComponent<Canvas> ().renderMode = RenderMode.ScreenSpaceCamera; GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceCamera;
GetComponent<Canvas> ().renderMode = RenderMode.WorldSpace; GetComponent<Canvas>().renderMode = RenderMode.WorldSpace;
transform.rotation = Quaternion.Euler (new Vector3 (0, 6, 0)); transform.rotation = Quaternion.Euler(new Vector3(0, 6, 0));
} }
} }
public void SetScreenSpase (bool flag) public void SetScreenSpase(bool flag)
{ {
if (flag) if (flag)
{ {
GetComponent<Canvas> ().renderMode = RenderMode.ScreenSpaceCamera; GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceCamera;
} }
} }
public void SetOverlay (bool flag) public void SetOverlay(bool flag)
{ {
if (flag) if (flag)
{ {
GetComponent<Canvas> ().renderMode = RenderMode.ScreenSpaceOverlay; GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
} }
} }
} }
} }

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

@ -7,128 +7,101 @@ using System.Linq;
namespace Coffee.UISoftMask namespace Coffee.UISoftMask
{ {
/// <summary> /// <summary>
/// SoftMask editor. /// SoftMask editor.
/// </summary> /// </summary>
[CustomEditor(typeof(SoftMask))] [CustomEditor(typeof(SoftMask))]
[CanEditMultipleObjects] [CanEditMultipleObjects]
public class SoftMaskEditor : Editor public class SoftMaskEditor : Editor
{ {
const string k_PrefsPreview = "SoftMaskEditor_Preview"; const string k_PrefsPreview = "SoftMaskEditor_Preview";
static readonly List<Graphic> s_Graphics = new List<Graphic> (); static readonly List<Graphic> s_Graphics = new List<Graphic>();
static bool s_Preview; static bool s_Preview;
private void OnEnable () private void OnEnable()
{ {
s_Preview = EditorPrefs.GetBool (k_PrefsPreview, false); s_Preview = EditorPrefs.GetBool(k_PrefsPreview, false);
} }
public override void OnInspectorGUI () public override void OnInspectorGUI()
{ {
base.OnInspectorGUI (); base.OnInspectorGUI();
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 =>
if (0 < fixTargets.Count) x.gameObject != current.gameObject && !x.GetComponent<SoftMaskable>() &&
{ (!x.GetComponent<Mask>() || x.GetComponent<Mask>().showMaskGraphic)).ToList();
GUILayout.BeginHorizontal (); if (0 < fixTargets.Count)
EditorGUILayout.HelpBox ("There are child Graphics that does not have a SoftMaskable component.\nAdd SoftMaskable component to them.", MessageType.Warning); {
GUILayout.BeginVertical (); GUILayout.BeginHorizontal();
if (GUILayout.Button ("Fix")) EditorGUILayout.HelpBox(
{ "There are child Graphics that does not have a SoftMaskable component.\nAdd SoftMaskable component to them.",
foreach (var p in fixTargets) MessageType.Warning);
{ GUILayout.BeginVertical();
p.gameObject.AddComponent<SoftMaskable> (); if (GUILayout.Button("Fix"))
} {
foreach (var p in fixTargets)
{
p.gameObject.AddComponent<SoftMaskable>();
}
EditorUtils.MarkPrefabDirty (); EditorUtils.MarkPrefabDirty();
} }
if (GUILayout.Button ("Ping"))
{
EditorGUIUtility.PingObject (fixTargets[0]);
}
GUILayout.EndVertical ();
GUILayout.EndHorizontal ();
}
// Preview buffer. if (GUILayout.Button("Ping"))
GUILayout.BeginHorizontal (EditorStyles.helpBox); {
if (s_Preview != (s_Preview = EditorGUILayout.ToggleLeft ("Preview Buffer", s_Preview, GUILayout.MaxWidth (EditorGUIUtility.labelWidth)))) EditorGUIUtility.PingObject(fixTargets[0]);
{ }
EditorPrefs.SetBool (k_PrefsPreview, s_Preview);
} GUILayout.EndVertical();
if (s_Preview) GUILayout.EndHorizontal();
{ }
var tex = current.softMaskBuffer;
var width = tex.width * 64 / tex.height; // Preview buffer.
EditorGUI.DrawPreviewTexture (GUILayoutUtility.GetRect (width, 64), tex, null, ScaleMode.ScaleToFit); GUILayout.BeginHorizontal(EditorStyles.helpBox);
Repaint (); if (s_Preview != (s_Preview = EditorGUILayout.ToggleLeft("Preview Buffer", s_Preview,
} GUILayout.MaxWidth(EditorGUIUtility.labelWidth))))
GUILayout.FlexibleSpace (); {
GUILayout.EndHorizontal (); EditorPrefs.SetBool(k_PrefsPreview, s_Preview);
} }
if (s_Preview)
{
var tex = current.softMaskBuffer;
var width = tex.width * 64 / tex.height;
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(width, 64), tex, null, ScaleMode.ScaleToFit);
Repaint();
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
//%%%% Context menu for editor %%%% //%%%% Context menu for editor %%%%
[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

@ -11,403 +11,174 @@ using System.IO;
namespace Coffee.UISoftMask namespace Coffee.UISoftMask
{ {
/// <summary> /// <summary>
/// SoftMaskable editor. /// SoftMaskable editor.
/// </summary> /// </summary>
[CustomEditor (typeof (SoftMaskable))] [CustomEditor(typeof(SoftMaskable))]
[CanEditMultipleObjects] [CanEditMultipleObjects]
public class SoftMaskableEditor : Editor public class SoftMaskableEditor : Editor
{ {
//################################ public enum MaskInteraction : int
// Constant or Static Members. {
//################################ VisibleInsideMask = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6),
public enum MaskInteraction : int VisibleOutsideMask = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6),
{ Custom = -1,
VisibleInsideMask = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6), }
VisibleOutsideMask = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6),
Custom = -1,
}
MaskInteraction maskInteraction MaskInteraction maskInteraction
{ {
get get
{ {
int value = _spMaskInteraction.intValue; int value = _spMaskInteraction.intValue;
return _custom return _custom
? MaskInteraction.Custom ? MaskInteraction.Custom
: System.Enum.IsDefined(typeof(MaskInteraction), value) : System.Enum.IsDefined(typeof(MaskInteraction), value)
? (MaskInteraction)value ? (MaskInteraction) value
: MaskInteraction.Custom; : MaskInteraction.Custom;
} }
set set
{ {
_custom = (value == MaskInteraction.Custom); _custom = (value == MaskInteraction.Custom);
if (!_custom) if (!_custom)
{ {
_spMaskInteraction.intValue = (int)value; _spMaskInteraction.intValue = (int) value;
} }
} }
} }
bool _custom = false;
static readonly Type s_TypeTMPro = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_Text"); bool _custom = false;
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> ();
Shader _shader;
Shader _mobileShader;
Shader _spriteShader;
List<MaterialEditor> _materialEditors = new List<MaterialEditor> ();
SerializedProperty _spMaskInteraction;
void OnEnable () static readonly List<Graphic> s_Graphics = new List<Graphic>();
{ SerializedProperty _spMaskInteraction;
_spMaskInteraction = serializedObject.FindProperty("m_MaskInteraction"); List<Mask> tmpMasks = new List<Mask>();
_custom = (maskInteraction == MaskInteraction.Custom); static GUIContent s_MaskWarning;
ClearMaterialEditors ();
_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 ()
{
ClearMaterialEditors ();
}
List<Mask> tmpMasks = new List<Mask>();
void DrawMaskInteractions()
{
(target as SoftMaskable).GetComponentsInParent<Mask>(true, tmpMasks);
tmpMasks.RemoveAll(x => !x.enabled);
tmpMasks.Reverse();
maskInteraction = (MaskInteraction)EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction);
if (_custom)
{
var l = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 45;
using (var ccs = new EditorGUI.ChangeCheckScope())
{
int intr0 = DrawMaskInteraction(0);
int intr1 = DrawMaskInteraction(1);
int intr2 = DrawMaskInteraction(2);
int intr3 = DrawMaskInteraction(3);
if (ccs.changed) {
_spMaskInteraction.intValue = (intr0 << 0) + (intr1 << 2) + (intr2 << 4) + (intr3 << 6);
}
}
EditorGUIUtility.labelWidth = l;
}
}
static GUIContent s_MaskWarning = new GUIContent();
int DrawMaskInteraction(int layer)
{
Mask mask = layer < tmpMasks.Count ? tmpMasks[layer] : null;
MaskIntr 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);
}
}
public override void OnInspectorGUI ()
{
base.OnInspectorGUI ();
serializedObject.Update();
DrawMaskInteractions();
// maskInteraction = (MaskInteraction)EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction);
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 ();
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.");
}
var current = target as SoftMaskable;
current.GetComponentsInChildren<Graphic> (true, s_Graphics); private void DrawMaskInteractions()
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) var softMaskable = target as SoftMaskable;
{ if (softMaskable == null) return;
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<SoftMaskable> ();
}
}
if (GUILayout.Button ("Ping"))
{
EditorGUIUtility.PingObject (fixTargets [0]);
}
GUILayout.EndVertical ();
GUILayout.EndHorizontal ();
}
if(s_TypeTMPro != null) softMaskable.GetComponentsInParent<Mask>(true, tmpMasks);
{ tmpMasks.RemoveAll(x => !x.enabled);
ShowTMProWarning (_shader, _mobileShader, _spriteShader, m => { }); tmpMasks.Reverse();
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)) maskInteraction = (MaskInteraction) EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction);
{ if (!_custom) 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)))
{
DestroyImmediate (current);
EditorUtils.MarkPrefabDirty (); var l = EditorGUIUtility.labelWidth;
} EditorGUIUtility.labelWidth = 45;
GUILayout.EndHorizontal ();
}
}
static bool DetectMask (Transform transform) using (var ccs = new EditorGUI.ChangeCheckScope())
{ {
if (transform == null) int intr0 = DrawMaskInteraction(0);
{ int intr1 = DrawMaskInteraction(1);
return false; int intr2 = DrawMaskInteraction(2);
} int intr3 = DrawMaskInteraction(3);
if (transform.GetComponent<SoftMask> () != null) if (ccs.changed)
{ {
return true; _spMaskInteraction.intValue = (intr0 << 0) + (intr1 << 2) + (intr2 << 4) + (intr3 << 6);
} }
}
return DetectMask (transform.parent); EditorGUIUtility.labelWidth = l;
} }
void ClearMaterialEditors ()
{
foreach (var e in _materialEditors)
{
if (e)
{
DestroyImmediate (e);
}
}
_materialEditors.Clear ();
}
protected void ShowMaterialEditors (Material [] materials, int startIndex, int count) private int DrawMaskInteraction(int layer)
{ {
for (int i = 0; i < count; i++) Mask mask = layer < tmpMasks.Count ? tmpMasks[layer] : null;
{ MaskIntr intr = (MaskIntr) ((_spMaskInteraction.intValue >> layer * 2) & 0x3);
if (_materialEditors.Count == i) if (!mask)
{ {
_materialEditors.Add (null); return (int) intr;
} }
var mat = materials [startIndex + i]; using (new EditorGUILayout.HorizontalScope())
var editor = _materialEditors [i]; {
if (editor && editor.target != mat) EditorGUILayout.LabelField(mask is SoftMask ? GUIContent.none : s_MaskWarning, GUILayout.Width(16));
{ GUILayout.Space(-5);
DestroyImmediate (editor); EditorGUILayout.ObjectField("Mask " + layer, mask, typeof(Mask), false);
editor = null; GUILayout.Space(-15);
} return (int) (MaskIntr) EditorGUILayout.EnumPopup(intr);
}
}
if (!editor) public override void OnInspectorGUI()
{ {
editor = _materialEditors [i] = Editor.CreateEditor (mat) as MaterialEditor; base.OnInspectorGUI();
}
editor.DrawHeader (); serializedObject.Update();
editor.OnInspectorGUI (); DrawMaskInteractions();
}
}
public void ShowTMProWarning (Shader shader, Shader mobileShader, Shader spriteShader, System.Action<Material> onCreatedMaterial) serializedObject.ApplyModifiedProperties();
{
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; var current = target as SoftMaskable;
if (fontSharedMaterial == null)
{
return;
}
// Is the material preset for dissolve? current.GetComponentsInChildren<Graphic>(true, s_Graphics);
Material m = fontSharedMaterial; var fixTargets = s_Graphics.Where(x =>
if (m.shader != shader && m.shader != mobileShader) x.gameObject != current.gameObject && !x.GetComponent<SoftMaskable>() &&
{ (!x.GetComponent<Mask>() || x.GetComponent<Mask>().showMaskGraphic)).ToList();
EditorGUILayout.BeginHorizontal (); if (0 < fixTargets.Count)
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")) GUILayout.BeginHorizontal();
{ EditorGUILayout.HelpBox(
var correctShader = m.shader.name.Contains ("Mobile") ? mobileShader : shader; "There are child Graphics that does not have a SoftMaskable component.\nAdd SoftMaskable component to them.",
m = ModifyTMProMaterialPreset (m, correctShader, onCreatedMaterial); MessageType.Warning);
s_PiFontSharedMaterial.SetValue (textMeshPro, m, new object [0]); GUILayout.BeginVertical();
} if (GUILayout.Button("Fix"))
EditorGUILayout.EndHorizontal (); {
return; foreach (var p in fixTargets)
} {
p.gameObject.AddComponent<SoftMaskable>();
}
}
// Is the sprite asset for dissolve? if (GUILayout.Button("Ping"))
object spriteAsset = s_PiSpriteAsset.GetValue (textMeshPro, new object [0]) ?? s_miGetSpriteAsset.Invoke (null, new object [0]); {
//TMP_SpriteAsset spriteAsset = textMeshPro.spriteAsset ?? TMP_Settings.GetSpriteAsset (); EditorGUIUtility.PingObject(fixTargets[0]);
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) GUILayout.EndVertical();
{ GUILayout.EndHorizontal();
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 (); if (!DetectMask(current.transform.parent))
EditorGUIUtility.PingObject (mat); {
} GUILayout.BeginHorizontal();
else EditorGUILayout.HelpBox("This is unnecessary SoftMaskable.\nCan't find any SoftMask components above.",
{ MessageType.Warning);
mat.shader = shader; if (GUILayout.Button("Remove", GUILayout.Height(40)))
} {
EditorUtility.SetDirty (mat); DestroyImmediate(current);
return mat;
}
object ModifyTMProSpriteAsset (Material baseMaterial, Shader shader, System.Action<Material> onCreatedMaterial) EditorUtils.MarkPrefabDirty();
{ }
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 (); GUILayout.EndHorizontal();
EditorGUIUtility.PingObject (spriteAsset); }
} }
else
{ static bool DetectMask(Transform transform)
Material m = s_FiMaterial.GetValue (spriteAsset) as Material; {
m.shader = shader; while (transform)
} {
EditorUtility.SetDirty (spriteAsset); if (transform.GetComponent<SoftMask>()) return true;
return spriteAsset;
} transform = transform.parent;
} }
return false;
}
}
} }

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>

File diff suppressed because it is too large Load Diff

View File

@ -6,290 +6,283 @@ using MaskIntr = UnityEngine.SpriteMaskInteraction;
namespace Coffee.UISoftMask namespace Coffee.UISoftMask
{ {
/// <summary> /// <summary>
/// Soft maskable. /// Soft maskable.
/// Add this component to Graphic under SoftMask for smooth masking. /// Add this component to Graphic under SoftMask for smooth masking.
/// </summary> /// </summary>
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
[ExecuteAlways] [ExecuteAlways]
#else #else
[ExecuteInEditMode] [ExecuteInEditMode]
# endif # endif
public class SoftMaskable : MonoBehaviour, IMaterialModifier, ICanvasRaycastFilter public class SoftMaskable : MonoBehaviour, IMaterialModifier, ICanvasRaycastFilter
#if UNITY_EDITOR #if UNITY_EDITOR
, ISerializationCallbackReceiver , ISerializationCallbackReceiver
# endif # endif
{ {
const int kVisibleInside = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6); const int kVisibleInside = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6);
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.")] static int s_SoftMaskTexId;
[System.Obsolete] static int s_StencilCompId;
[HideInInspector] static int s_MaskInteractionId;
[SerializeField] bool m_Inverse = false; static List<SoftMaskable> s_ActiveSoftMaskables;
[Tooltip("The interaction for each masks.")] static int[] s_Interactions = new int[4];
[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; [Tooltip("The graphic will be visible only in areas where no mask is present.")]
SoftMask _softMask = null; [System.Obsolete]
Material _maskMaterial = null; [HideInInspector]
static int s_SoftMaskTexId; [SerializeField]
static int s_StencilCompId; bool m_Inverse = false;
static int s_MaskInteractionId;
static List<SoftMaskable> s_ActiveSoftMaskables;
static int[] s_Interactions = new int[4];
Hash128 _effectMaterialHash;
/// <summary> [Tooltip("The interaction for each masks.")] [HideInInspector] [SerializeField]
/// Perform material modification in this function. int m_MaskInteraction = kVisibleInside;
/// </summary>
/// <returns>Modified material.</returns>
/// <param name="baseMaterial">Configured Material.</param>
public Material GetModifiedMaterial(Material baseMaterial)
{
_softMask = null;
if (!isActiveAndEnabled)
{
return baseMaterial;
}
// Find the nearest parent softmask. [Tooltip("Use stencil to mask.")] [SerializeField]
var parentTransform = transform.parent; bool m_UseStencil = false;
while (parentTransform)
{
var sm = parentTransform.GetComponent<SoftMask>();
if (sm && sm.enabled)
{
_softMask = sm;
break;
}
parentTransform = parentTransform.parent;
}
var oldHash = _effectMaterialHash; [Tooltip("Use soft-masked raycast target.\n\nNote: This option is expensive.")] [SerializeField]
var modifiedMaterial = baseMaterial; bool m_RaycastFilter = false;
if (_softMask)
{ Graphic _graphic = null;
_effectMaterialHash = GetMaterialHash(baseMaterial); SoftMask _softMask = null;
modifiedMaterial = MaterialCache.Register(baseMaterial, _effectMaterialHash, mat => Material _maskMaterial = null;
{ Hash128 _effectMaterialHash;
Debug.Log(mat.shader.name);
mat.shader = Shader.Find(string.Format("Hidden/{0} (SoftMaskable)", mat.shader.name)); /// <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>
/// Perform material modification in this function.
/// </summary>
/// <returns>Modified material.</returns>
/// <param name="baseMaterial">Configured Material.</param>
Material IMaterialModifier.GetModifiedMaterial(Material baseMaterial)
{
_softMask = null;
if (!isActiveAndEnabled)
{
return baseMaterial;
}
// Find the nearest parent softmask.
var parentTransform = transform.parent;
while (parentTransform)
{
var sm = parentTransform.GetComponent<SoftMask>();
if (sm && sm.enabled)
{
_softMask = sm;
break;
}
parentTransform = parentTransform.parent;
}
var oldHash = _effectMaterialHash;
var modifiedMaterial = baseMaterial;
if (_softMask)
{
_effectMaterialHash = GetMaterialHash(baseMaterial);
modifiedMaterial = MaterialCache.Register(baseMaterial, _effectMaterialHash, mat =>
{
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,
mat.SetVector(s_MaskInteractionId, new Vector4( m_UseStencil ? (int) CompareFunction.Equal : (int) CompareFunction.Always);
(m_MaskInteraction & 0x3), mat.SetVector(s_MaskInteractionId, new Vector4(
((m_MaskInteraction >> 2) & 0x3), (m_MaskInteraction & 0x3),
((m_MaskInteraction >> 4) & 0x3), ((m_MaskInteraction >> 2) & 0x3),
((m_MaskInteraction >> 6) & 0x3) ((m_MaskInteraction >> 4) & 0x3),
)); ((m_MaskInteraction >> 6) & 0x3)
}); ));
ReleaseMaterial(ref _maskMaterial); });
_maskMaterial = modifiedMaterial; ReleaseMaterial(ref _maskMaterial);
} _maskMaterial = modifiedMaterial;
}
MaterialCache.Unregister(oldHash); MaterialCache.Unregister(oldHash);
return modifiedMaterial; return modifiedMaterial;
} }
private Hash128 GetMaterialHash(Material material) private Hash128 GetMaterialHash(Material material)
{ {
if (!isActiveAndEnabled || !material || !material.shader) if (!isActiveAndEnabled || !material || !material.shader)
return k_InvalidHash; return k_InvalidHash;
return new Hash128( return new Hash128(
(uint) material.GetInstanceID(), (uint) material.GetInstanceID(),
(uint) m_MaskInteraction, (uint) m_MaskInteraction,
(uint) (m_UseStencil ? 1 : 0), (uint) (m_UseStencil ? 1 : 0),
0 0
); );
} }
/// <summary> /// <summary>
/// Given a point and a camera is the raycast valid. /// Given a point and a camera is the raycast valid.
/// </summary> /// </summary>
/// <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;
if (!RectTransformUtility.RectangleContainsScreenPoint(transform as RectTransform, sp, eventCamera)) if (!RectTransformUtility.RectangleContainsScreenPoint(transform as RectTransform, sp, eventCamera))
return false; return false;
if (!m_RaycastFilter) if (!m_RaycastFilter)
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;
} }
return _softMask.IsRaycastLocationValid(sp, eventCamera, graphic, s_Interactions); return _softMask.IsRaycastLocationValid(sp, eventCamera, graphic, s_Interactions);
} }
/// <summary>
/// Set the interaction for each mask.
/// </summary>
public void SetMaskInteraction(SpriteMaskInteraction intr)
{
SetMaskInteraction(intr, intr, intr, intr);
}
/// <summary>
/// Set the interaction for each mask.
/// </summary>
public void SetMaskInteraction(SpriteMaskInteraction layer0, SpriteMaskInteraction layer1,
SpriteMaskInteraction layer2, SpriteMaskInteraction layer3)
{
m_MaskInteraction = (int) layer0 + ((int) layer1 << 2) + ((int) layer2 << 4) + ((int) layer3 << 6);
graphic.SetMaterialDirtyEx();
}
/// <summary> /// <summary>
/// The graphic will be visible only in areas where no mask is present. /// This function is called when the object becomes enabled and active.
/// </summary> /// </summary>
public bool inverse private void OnEnable()
{ {
get { return m_MaskInteraction == kVisibleOutside; } // Register.
set if (s_ActiveSoftMaskables == null)
{ {
int intValue = value ? kVisibleOutside : kVisibleInside; s_ActiveSoftMaskables = new List<SoftMaskable>();
if (m_MaskInteraction != intValue)
{
m_MaskInteraction = intValue;
graphic.SetMaterialDirtyEx();
}
}
}
/// <summary> s_SoftMaskTexId = Shader.PropertyToID("_SoftMaskTex");
/// Use soft-masked raycast target. This option is expensive. s_StencilCompId = Shader.PropertyToID("_StencilComp");
/// </summary> s_MaskInteractionId = Shader.PropertyToID("_MaskInteraction");
public bool raycastFilter }
{
get { return m_RaycastFilter; }
set { m_RaycastFilter = value; }
}
/// <summary> s_ActiveSoftMaskables.Add(this);
/// The graphic associated with the soft mask.
/// </summary>
public Graphic graphic{ get { return _graphic ? _graphic : _graphic = GetComponent<Graphic>(); } }
/// <summary> graphic.SetMaterialDirtyEx();
/// Set the interaction for each mask. _softMask = null;
/// </summary> }
public void SetMaskInteraction(SpriteMaskInteraction intr)
{
SetMaskInteraction(intr, intr, intr, intr);
}
/// <summary> /// <summary>
/// Set the interaction for each mask. /// This function is called when the behaviour becomes disabled.
/// </summary> /// </summary>
public void SetMaskInteraction(SpriteMaskInteraction layer0, SpriteMaskInteraction layer1, SpriteMaskInteraction layer2, SpriteMaskInteraction layer3) private void OnDisable()
{ {
m_MaskInteraction = (int)layer0 + ((int)layer1 << 2) + ((int)layer2 << 4) + ((int)layer3 << 6); s_ActiveSoftMaskables.Remove(this);
graphic.SetMaterialDirtyEx();
}
graphic.SetMaterialDirtyEx();
_softMask = null;
ReleaseMaterial(ref _maskMaterial);
/// <summary> MaterialCache.Unregister(_effectMaterialHash);
/// This function is called when the object becomes enabled and active. _effectMaterialHash = k_InvalidHash;
/// </summary> }
void OnEnable()
{
// Register.
if (s_ActiveSoftMaskables == null)
{
s_ActiveSoftMaskables = new List<SoftMaskable>();
s_SoftMaskTexId = Shader.PropertyToID("_SoftMaskTex"); /// <summary>
s_StencilCompId = Shader.PropertyToID("_StencilComp"); /// Release the material.
s_MaskInteractionId = Shader.PropertyToID("_MaskInteraction"); /// </summary>
} static void ReleaseMaterial(ref Material mat)
s_ActiveSoftMaskables.Add(this); {
if (!mat) return;
#if UNITY_EDITOR
if (!Application.isPlaying)
DestroyImmediate(mat);
else
#endif
Destroy(mat);
var g = graphic; mat = null;
if (g) }
{
g.SetMaterialDirty();
}
_softMask = null;
}
/// <summary>
/// This function is called when the behaviour becomes disabled.
/// </summary>
void OnDisable()
{
s_ActiveSoftMaskables.Remove(this);
var g = graphic;
if (g)
{
g.SetMaterialDirty();
}
ReleaseMaterial(ref _maskMaterial);
_softMask = null;
MaterialCache.Unregister(_effectMaterialHash);
_effectMaterialHash = k_InvalidHash;
}
/// <summary>
/// Release the material.
/// </summary>
void ReleaseMaterial(ref Material mat)
{
if (mat)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
DestroyImmediate(mat);
}
else
#endif
{
Destroy(mat);
}
mat = null;
}
}
#if UNITY_EDITOR #if UNITY_EDITOR
/// <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>
private void OnValidate() private void OnValidate()
{ {
graphic.SetMaterialDirtyEx(); graphic.SetMaterialDirtyEx();
} }
void ISerializationCallbackReceiver.OnBeforeSerialize() void ISerializationCallbackReceiver.OnBeforeSerialize()
{ {
} }
void ISerializationCallbackReceiver.OnAfterDeserialize() void ISerializationCallbackReceiver.OnAfterDeserialize()
{ {
#pragma warning disable 0612 #pragma warning disable 0612
if (m_Inverse) if (m_Inverse)
{ {
m_Inverse = false; m_Inverse = false;
m_MaskInteraction = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6); m_MaskInteraction = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6);
} }
#pragma warning restore 0612 #pragma warning restore 0612
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();
} }
}; };
#endif #endif
} }
} }
} }