diff options
Diffstat (limited to 'Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs')
-rw-r--r-- | Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs new file mode 100644 index 0000000..0d0f327 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs @@ -0,0 +1,235 @@ +using System; +using UnityEngine.Events; +using UnityEngine.Rendering; + +namespace UnityEngine.UI +{ + 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. + [NonSerialized] + private bool m_Maskable = true; + + [NonSerialized] + [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(); + + public CullStateChangedEvent onCullStateChanged + { + get { return m_OnCullStateChanged; } + set { m_OnCullStateChanged = value; } + } + + public bool maskable + { + get { return m_Maskable; } + set + { + if (value == m_Maskable) + return; + m_Maskable = value; + m_ShouldRecalculateStencil = true; + SetMaterialDirty(); + } + } + + [NonSerialized] + [Obsolete("Not used anymore", true)] + protected bool m_ShouldRecalculate = true; + + [NonSerialized] + protected int m_StencilValue; + + public virtual Material GetModifiedMaterial(Material baseMaterial) + { + var toUse = baseMaterial; + + if (m_ShouldRecalculateStencil) + { + var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); + // Graphic在masks下的深度,如果不是0且没有mask组件意味着是普通的非mask用graphic(如果是0一定是mask用的graphic) + m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0; + m_ShouldRecalculateStencil = false; + } + + // if we have a enabled Mask component then it will + // generate the mask material. This is an optimisation + // it adds some coupling between components though :( + Mask maskComponent = GetComponent<Mask>(); + if (m_StencilValue > 0 && (maskComponent == null || !maskComponent.IsActive())) + { + // Ref = (1 << stencilValue) - 1 + // Op = Keep + // Func = Equal + // ReadMask = (1 << stencilValue) - 1 + // WriteMask = 0 + 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; + } + + public virtual void Cull(Rect clipRect, bool validRect) + { + var cull = !validRect || !clipRect.Overlaps(rootCanvasRect, true); + UpdateCull(cull); + } + + private void UpdateCull(bool cull) + { + var cullingChanged = canvasRenderer.cull != cull; + canvasRenderer.cull = cull; + + if (cullingChanged) + { + UISystemProfilerApi.AddMarker("MaskableGraphic.cullingChanged", this); + m_OnCullStateChanged.Invoke(cull); + SetVerticesDirty(); // 这里需要更新一下canvasRenderer的网格数据 + } + } + + public virtual void SetClipRect(Rect clipRect, bool validRect) + { + if (validRect) + canvasRenderer.EnableRectClipping(clipRect); + else + canvasRenderer.DisableRectClipping(); + } + + protected override void OnEnable() + { + base.OnEnable(); + m_ShouldRecalculateStencil = true; + UpdateClipParent(); + SetMaterialDirty(); + + if (GetComponent<Mask>() != null) + { + MaskUtilities.NotifyStencilStateChanged(this); + } + } + + protected override void OnDisable() + { + base.OnDisable(); + m_ShouldRecalculateStencil = true; + SetMaterialDirty(); + UpdateClipParent(); + StencilMaterial.Remove(m_MaskMaterial); + m_MaskMaterial = null; + + if (GetComponent<Mask>() != null) + { + 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(); + } + + [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) + { + Canvas rootCanvas = canvas.rootCanvas; + for (int i = 0; i < 4; ++i) + m_Corners[i] = rootCanvas.transform.InverseTransformPoint(m_Corners[i]); + } + + return new Rect(m_Corners[0].x, m_Corners[0].y, m_Corners[2].x - m_Corners[0].x, m_Corners[2].y - m_Corners[0].y); + } + } + + 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; + } + + public virtual void RecalculateClipping() + { + UpdateClipParent(); + } + + public virtual void RecalculateMasking() + { + m_ShouldRecalculateStencil = true; + SetMaterialDirty(); + } + } +} |