| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- using System;
- using UnityEngine.Events;
- using UnityEngine.Rendering;
- namespace UnityEngine.UI
- {
- /// <summary>
- /// A Graphic that is capable of being masked out.
- /// </summary>
- public abstract class MaskableGraphic : Graphic, IClippable, IMaskable, IMaterialModifier
- {
- [NonSerialized]
- protected bool m_ShouldRecalculateStencil = true;
- [NonSerialized]
- protected Material m_MaskMaterial;
- [NonSerialized]
- private RectMask2D m_ParentMask;
- // m_Maskable is whether this graphic is allowed to be masked or not. It has the matching public property maskable.
- // The default for m_Maskable is true, so graphics under a mask are masked out of the box.
- // The maskable property can be turned off from script by the user if masking is not desired.
- // m_IncludeForMasking is whether we actually consider this graphic for masking or not - this is an implementation detail.
- // m_IncludeForMasking should only be true if m_Maskable is true AND a parent of the graphic has an IMask component.
- // Things would still work correctly if m_IncludeForMasking was always true when m_Maskable is, but performance would suffer.
- [SerializeField]
- private bool m_Maskable = true;
- private bool m_IsMaskingGraphic = false;
- [NonSerialized]
- [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
- [Obsolete("Not used anymore.", true)]
- protected bool m_IncludeForMasking = false;
- [Serializable]
- public class CullStateChangedEvent : UnityEvent<bool> {}
- // Event delegates triggered on click.
- [SerializeField]
- private CullStateChangedEvent m_OnCullStateChanged = new CullStateChangedEvent();
- /// <summary>
- /// Callback issued when culling changes.
- /// </summary>
- /// <remarks>
- /// Called whene the culling state of this MaskableGraphic either becomes culled or visible. You can use this to control other elements of your UI as culling happens.
- /// </remarks>
- public CullStateChangedEvent onCullStateChanged
- {
- get { return m_OnCullStateChanged; }
- set { m_OnCullStateChanged = value; }
- }
- /// <summary>
- /// Does this graphic allow masking.
- /// </summary>
- public bool maskable
- {
- get { return m_Maskable; }
- set
- {
- if (value == m_Maskable)
- return;
- m_Maskable = value;
- m_ShouldRecalculateStencil = true;
- SetMaterialDirty();
- }
- }
- /// <summary>
- /// Is this graphic the graphic on the same object as a Mask that is enabled.
- /// </summary>
- /// <remarks>
- /// If toggled ensure to call MaskUtilities.NotifyStencilStateChanged(this); manually as it changes how stenciles are calculated for this image.
- /// </remarks>
- public bool isMaskingGraphic
- {
- get { return m_IsMaskingGraphic; }
- set
- {
- if (value == m_IsMaskingGraphic)
- return;
- m_IsMaskingGraphic = value;
- }
- }
- [NonSerialized]
- [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
- [Obsolete("Not used anymore", true)]
- protected bool m_ShouldRecalculate = true;
- [NonSerialized]
- protected int m_StencilValue;
- /// <summary>
- /// See IMaterialModifier.GetModifiedMaterial
- /// </summary>
- public virtual Material GetModifiedMaterial(Material baseMaterial)
- {
- var toUse = baseMaterial;
- if (m_ShouldRecalculateStencil)
- {
- if (maskable)
- {
- var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
- m_StencilValue = MaskUtilities.GetStencilDepth(transform, rootCanvas);
- }
- else
- m_StencilValue = 0;
- m_ShouldRecalculateStencil = false;
- }
- // if we have a enabled Mask component then it will
- // generate the mask material. This is an optimization
- // it adds some coupling between components though :(
- if (m_StencilValue > 0 && !isMaskingGraphic)
- {
- var maskMat = StencilMaterial.Add(toUse, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);
- StencilMaterial.Remove(m_MaskMaterial);
- m_MaskMaterial = maskMat;
- toUse = m_MaskMaterial;
- }
- return toUse;
- }
- /// <summary>
- /// See IClippable.Cull
- /// </summary>
- public virtual void Cull(Rect clipRect, bool validRect)
- {
- var cull = !validRect || !clipRect.Overlaps(rootCanvasRect, true);
- UpdateCull(cull);
- }
- private void UpdateCull(bool cull)
- {
- if (canvasRenderer.cull != cull)
- {
- canvasRenderer.cull = cull;
- UISystemProfilerApi.AddMarker("MaskableGraphic.cullingChanged", this);
- m_OnCullStateChanged.Invoke(cull);
- OnCullingChanged();
- }
- }
- /// <summary>
- /// See IClippable.SetClipRect
- /// </summary>
- public virtual void SetClipRect(Rect clipRect, bool validRect)
- {
- if (validRect)
- canvasRenderer.EnableRectClipping(clipRect);
- else
- canvasRenderer.DisableRectClipping();
- }
- public virtual void SetClipSoftness(Vector2 clipSoftness)
- {
- canvasRenderer.clippingSoftness = clipSoftness;
- }
- protected override void OnEnable()
- {
- base.OnEnable();
- m_ShouldRecalculateStencil = true;
- UpdateClipParent();
- SetMaterialDirty();
- if (isMaskingGraphic)
- {
- MaskUtilities.NotifyStencilStateChanged(this);
- }
- }
- protected override void OnDisable()
- {
- base.OnDisable();
- m_ShouldRecalculateStencil = true;
- SetMaterialDirty();
- UpdateClipParent();
- StencilMaterial.Remove(m_MaskMaterial);
- m_MaskMaterial = null;
- if (isMaskingGraphic)
- {
- MaskUtilities.NotifyStencilStateChanged(this);
- }
- }
- #if UNITY_EDITOR
- protected override void OnValidate()
- {
- base.OnValidate();
- m_ShouldRecalculateStencil = true;
- UpdateClipParent();
- SetMaterialDirty();
- }
- #endif
- protected override void OnTransformParentChanged()
- {
- base.OnTransformParentChanged();
- if (!isActiveAndEnabled)
- return;
- m_ShouldRecalculateStencil = true;
- UpdateClipParent();
- SetMaterialDirty();
- }
- [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
- [Obsolete("Not used anymore.", true)]
- public virtual void ParentMaskStateChanged() {}
- protected override void OnCanvasHierarchyChanged()
- {
- base.OnCanvasHierarchyChanged();
- if (!isActiveAndEnabled)
- return;
- m_ShouldRecalculateStencil = true;
- UpdateClipParent();
- SetMaterialDirty();
- }
- readonly Vector3[] m_Corners = new Vector3[4];
- private Rect rootCanvasRect
- {
- get
- {
- rectTransform.GetWorldCorners(m_Corners);
- if (canvas)
- {
- Matrix4x4 mat = canvas.rootCanvas.transform.worldToLocalMatrix;
- for (int i = 0; i < 4; ++i)
- m_Corners[i] = mat.MultiplyPoint(m_Corners[i]);
- }
- // bounding box is now based on the min and max of all corners (case 1013182)
- Vector2 min = m_Corners[0];
- Vector2 max = m_Corners[0];
- for (int i = 1; i < 4; i++)
- {
- min.x = Mathf.Min(m_Corners[i].x, min.x);
- min.y = Mathf.Min(m_Corners[i].y, min.y);
- max.x = Mathf.Max(m_Corners[i].x, max.x);
- max.y = Mathf.Max(m_Corners[i].y, max.y);
- }
- return new Rect(min, max - min);
- }
- }
- private void UpdateClipParent()
- {
- var newParent = (maskable && IsActive()) ? MaskUtilities.GetRectMaskForClippable(this) : null;
- // if the new parent is different OR is now inactive
- if (m_ParentMask != null && (newParent != m_ParentMask || !newParent.IsActive()))
- {
- m_ParentMask.RemoveClippable(this);
- UpdateCull(false);
- }
- // don't re-add it if the newparent is inactive
- if (newParent != null && newParent.IsActive())
- newParent.AddClippable(this);
- m_ParentMask = newParent;
- }
- /// <summary>
- /// See IClippable.RecalculateClipping
- /// </summary>
- public virtual void RecalculateClipping()
- {
- UpdateClipParent();
- }
- /// <summary>
- /// See IMaskable.RecalculateMasking
- /// </summary>
- public virtual void RecalculateMasking()
- {
- // Remove the material reference as either the graphic of the mask has been enable/ disabled.
- // This will cause the material to be repopulated from the original if need be. (case 994413)
- StencilMaterial.Remove(m_MaskMaterial);
- m_MaskMaterial = null;
- m_ShouldRecalculateStencil = true;
- SetMaterialDirty();
- }
- }
- }
|