Mask.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. using System;
  2. using UnityEngine;
  3. using UnityEngine.EventSystems;
  4. using UnityEngine.Rendering;
  5. using UnityEngine.Serialization;
  6. namespace UnityEngine.UI
  7. {
  8. [AddComponentMenu("UI/Mask", 13)]
  9. [ExecuteAlways]
  10. [RequireComponent(typeof(RectTransform))]
  11. [DisallowMultipleComponent]
  12. /// <summary>
  13. /// A component for masking children elements.
  14. /// </summary>
  15. /// <remarks>
  16. /// By using this element any children elements that have masking enabled will mask where a sibling Graphic would write 0 to the stencil buffer.
  17. /// </remarks>
  18. public class Mask : UIBehaviour, ICanvasRaycastFilter, IMaterialModifier
  19. {
  20. [NonSerialized]
  21. private RectTransform m_RectTransform;
  22. public RectTransform rectTransform
  23. {
  24. get { return m_RectTransform ?? (m_RectTransform = GetComponent<RectTransform>()); }
  25. }
  26. [SerializeField]
  27. private bool m_ShowMaskGraphic = true;
  28. /// <summary>
  29. /// Show the graphic that is associated with the Mask render area.
  30. /// </summary>
  31. public bool showMaskGraphic
  32. {
  33. get { return m_ShowMaskGraphic; }
  34. set
  35. {
  36. if (m_ShowMaskGraphic == value)
  37. return;
  38. m_ShowMaskGraphic = value;
  39. if (graphic != null)
  40. graphic.SetMaterialDirty();
  41. }
  42. }
  43. [NonSerialized]
  44. private Graphic m_Graphic;
  45. /// <summary>
  46. /// The graphic associated with the Mask.
  47. /// </summary>
  48. public Graphic graphic
  49. {
  50. get { return m_Graphic ?? (m_Graphic = GetComponent<Graphic>()); }
  51. }
  52. [NonSerialized]
  53. private Material m_MaskMaterial;
  54. [NonSerialized]
  55. private Material m_UnmaskMaterial;
  56. protected Mask()
  57. {}
  58. public virtual bool MaskEnabled() { return IsActive() && graphic != null; }
  59. [Obsolete("Not used anymore.")]
  60. public virtual void OnSiblingGraphicEnabledDisabled() {}
  61. protected override void OnEnable()
  62. {
  63. base.OnEnable();
  64. if (graphic != null)
  65. {
  66. graphic.canvasRenderer.hasPopInstruction = true;
  67. graphic.SetMaterialDirty();
  68. // Default the graphic to being the maskable graphic if its found.
  69. if (graphic is MaskableGraphic)
  70. (graphic as MaskableGraphic).isMaskingGraphic = true;
  71. }
  72. MaskUtilities.NotifyStencilStateChanged(this);
  73. }
  74. protected override void OnDisable()
  75. {
  76. // we call base OnDisable first here
  77. // as we need to have the IsActive return the
  78. // correct value when we notify the children
  79. // that the mask state has changed.
  80. base.OnDisable();
  81. if (graphic != null)
  82. {
  83. graphic.SetMaterialDirty();
  84. graphic.canvasRenderer.hasPopInstruction = false;
  85. graphic.canvasRenderer.popMaterialCount = 0;
  86. if (graphic is MaskableGraphic)
  87. (graphic as MaskableGraphic).isMaskingGraphic = false;
  88. }
  89. StencilMaterial.Remove(m_MaskMaterial);
  90. m_MaskMaterial = null;
  91. StencilMaterial.Remove(m_UnmaskMaterial);
  92. m_UnmaskMaterial = null;
  93. MaskUtilities.NotifyStencilStateChanged(this);
  94. }
  95. #if UNITY_EDITOR
  96. protected override void OnValidate()
  97. {
  98. base.OnValidate();
  99. if (!IsActive())
  100. return;
  101. if (graphic != null)
  102. {
  103. // Default the graphic to being the maskable graphic if its found.
  104. if (graphic is MaskableGraphic)
  105. (graphic as MaskableGraphic).isMaskingGraphic = true;
  106. graphic.SetMaterialDirty();
  107. }
  108. MaskUtilities.NotifyStencilStateChanged(this);
  109. }
  110. #endif
  111. public virtual bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
  112. {
  113. if (!isActiveAndEnabled)
  114. return true;
  115. return RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera);
  116. }
  117. /// Stencil calculation time!
  118. public virtual Material GetModifiedMaterial(Material baseMaterial)
  119. {
  120. if (!MaskEnabled())
  121. return baseMaterial;
  122. var rootSortCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
  123. var stencilDepth = MaskUtilities.GetStencilDepth(transform, rootSortCanvas);
  124. if (stencilDepth >= 8)
  125. {
  126. Debug.LogWarning("Attempting to use a stencil mask with depth > 8", gameObject);
  127. return baseMaterial;
  128. }
  129. int desiredStencilBit = 1 << stencilDepth;
  130. // if we are at the first level...
  131. // we want to destroy what is there
  132. if (desiredStencilBit == 1)
  133. {
  134. var maskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Replace, CompareFunction.Always, m_ShowMaskGraphic ? ColorWriteMask.All : 0);
  135. StencilMaterial.Remove(m_MaskMaterial);
  136. m_MaskMaterial = maskMaterial;
  137. var unmaskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Zero, CompareFunction.Always, 0);
  138. StencilMaterial.Remove(m_UnmaskMaterial);
  139. m_UnmaskMaterial = unmaskMaterial;
  140. graphic.canvasRenderer.popMaterialCount = 1;
  141. graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);
  142. return m_MaskMaterial;
  143. }
  144. //otherwise we need to be a bit smarter and set some read / write masks
  145. var maskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit | (desiredStencilBit - 1), StencilOp.Replace, CompareFunction.Equal, m_ShowMaskGraphic ? ColorWriteMask.All : 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));
  146. StencilMaterial.Remove(m_MaskMaterial);
  147. m_MaskMaterial = maskMaterial2;
  148. graphic.canvasRenderer.hasPopInstruction = true;
  149. var unmaskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit - 1, StencilOp.Replace, CompareFunction.Equal, 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1));
  150. StencilMaterial.Remove(m_UnmaskMaterial);
  151. m_UnmaskMaterial = unmaskMaterial2;
  152. graphic.canvasRenderer.popMaterialCount = 1;
  153. graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);
  154. return m_MaskMaterial;
  155. }
  156. }
  157. }