using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; namespace Coffee.UIExtensions { /// /// Reverse masking for parent Mask component. /// [ExecuteInEditMode] [AddComponentMenu("UI/Unmask/Unmask", 1)] public class Unmask : MonoBehaviour, IMaterialModifier { //################################ // Constant or Static Members. //################################ static readonly Vector2 s_Center = new Vector2(0.5f, 0.5f); //################################ // Serialize Members. //################################ [Tooltip("Fit graphic's transform to target transform.")] [SerializeField] RectTransform m_FitTarget; [Tooltip("Fit graphic's transform to target transform on LateUpdate every frame.")] [SerializeField] bool m_FitOnLateUpdate; [Tooltip ("Unmask affects only for children.")] [SerializeField] bool m_OnlyForChildren = false; [Tooltip("Show the graphic that is associated with the unmask render area.")] [SerializeField] bool m_ShowUnmaskGraphic = false; //################################ // Public Members. //################################ /// /// The graphic associated with the unmask. /// public Graphic graphic{ get { return _graphic ?? (_graphic = GetComponent()); } } /// /// Fit graphic's transform to target transform. /// public RectTransform fitTarget { get { return m_FitTarget; } set { m_FitTarget = value; FitTo(m_FitTarget); } } /// /// Fit graphic's transform to target transform on LateUpdate every frame. /// public bool fitOnLateUpdate{ get { return m_FitOnLateUpdate; } set { m_FitOnLateUpdate = value; } } /// /// Show the graphic that is associated with the unmask render area. /// public bool showUnmaskGraphic { get { return m_ShowUnmaskGraphic; } set { m_ShowUnmaskGraphic = value; SetDirty(); } } /// /// Unmask affects only for children. /// public bool onlyForChildren { get { return m_OnlyForChildren; } set { m_OnlyForChildren = value; SetDirty (); } } /// /// Perform material modification in this function. /// /// Modified material. /// Configured Material. public Material GetModifiedMaterial(Material baseMaterial) { if (!isActiveAndEnabled) { return baseMaterial; } Transform stopAfter = MaskUtilities.FindRootSortOverrideCanvas(transform); var stencilDepth = MaskUtilities.GetStencilDepth(transform, stopAfter); StencilMaterial.Remove(_unmaskMaterial); _unmaskMaterial = StencilMaterial.Add(baseMaterial, (1 << stencilDepth) - 1, StencilOp.Zero, CompareFunction.Always, m_ShowUnmaskGraphic ? ColorWriteMask.All : (ColorWriteMask)0, 0, (1 << stencilDepth) - 1); // Unmask affects only for children. var canvasRenderer = graphic.canvasRenderer; if (m_OnlyForChildren) { StencilMaterial.Remove (_revertUnmaskMaterial); _revertUnmaskMaterial = StencilMaterial.Add (baseMaterial, (1 << stencilDepth) - 1, StencilOp.Replace, CompareFunction.NotEqual, (ColorWriteMask)0); canvasRenderer.hasPopInstruction = true; canvasRenderer.popMaterialCount = 1; canvasRenderer.SetPopMaterial (_revertUnmaskMaterial, 0); } else { canvasRenderer.hasPopInstruction = false; canvasRenderer.popMaterialCount = 0; } return _unmaskMaterial; } /// /// Fit to target transform. /// /// Target transform. public void FitTo(RectTransform target) { var rt = transform as RectTransform; rt.position = target.position; rt.rotation = target.rotation; var s1 = target.lossyScale; var s2 = rt.parent.lossyScale; rt.localScale = new Vector3(s1.x / s2.x, s1.y / s2.y, s1.z / s2.z); rt.sizeDelta = target.rect.size; rt.anchorMax = rt.anchorMin = s_Center; } //################################ // Private Members. //################################ Material _unmaskMaterial; Material _revertUnmaskMaterial; Graphic _graphic; /// /// This function is called when the object becomes enabled and active. /// void OnEnable() { if (m_FitTarget) { FitTo(m_FitTarget); } SetDirty(); } /// /// This function is called when the behaviour becomes disabled () or inactive. /// void OnDisable() { StencilMaterial.Remove (_unmaskMaterial); StencilMaterial.Remove (_revertUnmaskMaterial); _unmaskMaterial = null; _revertUnmaskMaterial = null; if (graphic) { var canvasRenderer = graphic.canvasRenderer; canvasRenderer.hasPopInstruction = false; canvasRenderer.popMaterialCount = 0; graphic.SetMaterialDirty(); } SetDirty (); } /// /// LateUpdate is called every frame, if the Behaviour is enabled. /// void LateUpdate() { #if UNITY_EDITOR if (m_FitTarget && (m_FitOnLateUpdate || !Application.isPlaying)) #else if (m_FitTarget && m_FitOnLateUpdate) #endif { FitTo(m_FitTarget); } } #if UNITY_EDITOR /// /// This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only). /// void OnValidate() { SetDirty(); } #endif /// /// Mark the graphic as dirty. /// void SetDirty() { if (graphic) { graphic.SetMaterialDirty(); } } } }