readonly IndexedSet<IClipper> m_Clippers = new IndexedSet<IClipper>();
publicstatic ClipperRegistry instance { get { if (s_Instance == null) s_Instance = new ClipperRegistry(); return s_Instance; } }
publicvoidCull() { for (var i = 0; i < m_Clippers.Count; ++i) { m_Clippers[i].PerformClipping(); } }
///<summary> /// Register a unique IClipper element ///</summary> ///<param name="c">The clipper element to add</param> publicstaticvoidRegister(IClipper c) { if (c == null) return; instance.m_Clippers.AddUnique(c); }
///<summary> /// UnRegister a IClipper element ///</summary> ///<param name="c">The Element to try and remove.</param> publicstaticvoidUnregister(IClipper c) { instance.m_Clippers.Remove(c); } }
///<summary> /// Interface that can be used to recieve clipping callbacks as part of the canvas update loop. ///</summary> publicinterfaceIClipper { ///<summary> /// Function to to cull / clip children elements. ///</summary> ///<remarks> /// Called after layout and before Graphic update of the Canvas update loop. ///</remarks>
voidPerformClipping(); }
///<summary> /// Interface for elements that can be clipped if they are under an IClipper ///</summary> publicinterfaceIClippable { ///<summary> /// GameObject of the IClippable object ///</summary> GameObject gameObject { get; }
///<summary> /// Will be called when the state of a parent IClippable changed. ///</summary> voidRecalculateClipping();
///<summary> /// The RectTransform of the clippable. ///</summary> RectTransform rectTransform { get; }
///<summary> /// Clip and cull the IClippable given a specific clipping rect ///</summary> ///<param name="clipRect">The Rectangle in which to clip against.</param> ///<param name="validRect">Is the Rect valid. If not then the rect has 0 size.</param> voidCull(Rect clipRect, bool validRect);
///<summary> /// Set the clip rect for the IClippable. ///</summary> ///<param name="value">The Rectangle for the clipping</param> ///<param name="validRect">Is the rect valid.</param> voidSetClipRect(Rect value, bool validRect); }
///<summary> /// This element is capable of being masked out. ///</summary> publicinterfaceIMaskable { ///<summary> /// Recalculate masking for this element and all children elements. ///</summary> ///<remarks> /// Use this to update the internal state (recreate materials etc). ///</remarks> voidRecalculateMasking(); }
publicinterfaceIMaterialModifier { ///<summary> /// Perform material modification in this function. ///</summary> ///<param name="baseMaterial">The material that is to be modified</param> ///<returns>The modified material.</returns> Material GetModifiedMaterial(Material baseMaterial); }
///<summary> /// A 2D rectangular mask that allows for clipping / masking of areas outside the mask. ///</summary> ///<remarks> /// The RectMask2D behaves in a similar way to a standard Mask component. It differs though in some of the restrictions that it has. /// A RectMask2D: /// *Only works in the 2D plane /// *Requires elements on the mask to be coplanar. /// *Does not require stencil buffer / extra draw calls /// *Requires fewer draw calls /// *Culls elements that are outside the mask area. ///</remarks>
protectedoverridevoidOnDisable() { // we call base OnDisable first here // as we need to have the IsActive return the // correct value when we notify the children // that the mask state has changed. base.OnDisable(); m_ClipTargets.Clear(); m_MaskableTargets.Clear(); m_Clippers.Clear(); ClipperRegistry.Unregister(this); MaskUtilities.Notify2DMaskStateChanged(this); }
if (m_ShouldRecalculateClipRects) { MaskUtilities.GetRectMasksForClip(this, m_Clippers); m_ShouldRecalculateClipRects = false; }
// get the compound rects from // the clippers that are valid bool validRect = true; Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect);
// If the mask is in ScreenSpaceOverlay/Camera render mode, its content is only rendered when its rect // overlaps that of the root canvas. RenderMode renderMode = Canvas.rootCanvas.renderMode; bool maskIsCulled = (renderMode == RenderMode.ScreenSpaceCamera || renderMode == RenderMode.ScreenSpaceOverlay) && !clipRect.Overlaps(rootCanvasRect, true);
// 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. [NonSerialized] privatebool m_Maskable = true;
///<summary> /// Does this graphic allow masking. ///</summary> publicbool maskable { get { return m_Maskable; } set { if (value == m_Maskable) return; m_Maskable = value; m_ShouldRecalculateStencil = true; SetMaterialDirty(); } }
// 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; } }
// 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> publicvirtualvoidRecalculateClipping() { UpdateClipParent(); }
publicvirtual Material GetModifiedMaterial(Material baseMaterial) { var toUse = baseMaterial;
if (m_ShouldRecalculateStencil) { var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); 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())) { 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; }
publicvirtualvoidRecalculateMasking() { // 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(); }