using System;
using System.Collections.Generic;
using UnityEngine;
using FairyGUI.Utils;
namespace FairyGUI
{
///
/// UpdateContext is for internal use.
///
public class UpdateContext
{
public struct ClipInfo
{
public Rect rect;
public Vector4 clipBox;
public bool soft;
public Vector4 softness;//left-top-right-bottom
public uint clipId;
public int rectMaskDepth;
public int referenceValue;
public bool reversed;
}
Stack _clipStack;
public bool clipped;
public ClipInfo clipInfo;
public int renderingOrder;
public int batchingDepth;
public int rectMaskDepth;
public int stencilReferenceValue;
public int stencilCompareValue;
public float alpha;
public bool grayed;
public static UpdateContext current;
public static bool working;
public static event Action OnBegin;
public static event Action OnEnd;
static Action _tmpBegin;
public UpdateContext()
{
_clipStack = new Stack();
}
///
///
///
public void Begin()
{
current = this;
renderingOrder = 0;
batchingDepth = 0;
rectMaskDepth = 0;
stencilReferenceValue = 0;
alpha = 1;
grayed = false;
clipped = false;
_clipStack.Clear();
Stats.ObjectCount = 0;
Stats.GraphicsCount = 0;
_tmpBegin = OnBegin;
OnBegin = null;
//允许OnBegin里再次Add,这里没有做死锁检查
while (_tmpBegin != null)
{
_tmpBegin.Invoke();
_tmpBegin = OnBegin;
OnBegin = null;
}
working = true;
}
///
///
///
public void End()
{
working = false;
if (OnEnd != null)
OnEnd.Invoke();
OnEnd = null;
}
///
///
///
///
///
///
public void EnterClipping(uint clipId, Rect clipRect, Vector4? softness)
{
_clipStack.Push(clipInfo);
if (rectMaskDepth > 0)
clipRect = ToolSet.Intersection(ref clipInfo.rect, ref clipRect);
clipped = true;
clipInfo.rectMaskDepth = ++rectMaskDepth;
/* clipPos = xy * clipBox.zw + clipBox.xy
* 利用这个公式,使clipPos变为当前顶点距离剪切区域中心的距离值,剪切区域的大小为2x2
* 那么abs(clipPos)>1的都是在剪切区域外
*/
clipInfo.rect = clipRect;
clipRect.x = clipRect.x + clipRect.width * 0.5f;
clipRect.y = clipRect.y + clipRect.height * 0.5f;
clipRect.width *= 0.5f;
clipRect.height *= 0.5f;
if (clipRect.width == 0 || clipRect.height == 0)
clipInfo.clipBox = new Vector4(-2, -2, 0, 0);
else
clipInfo.clipBox = new Vector4(-clipRect.x / clipRect.width, -clipRect.y / clipRect.height,
1.0f / clipRect.width, 1.0f / clipRect.height);
clipInfo.clipId = clipId;
clipInfo.soft = softness != null;
if (clipInfo.soft)
{
clipInfo.softness = (Vector4)softness;
float vx = clipInfo.rect.width * Screen.height * 0.25f;
float vy = clipInfo.rect.height * Screen.height * 0.25f;
if (clipInfo.softness.x > 0)
clipInfo.softness.x = vx / clipInfo.softness.x;
else
clipInfo.softness.x = 10000f;
if (clipInfo.softness.y > 0)
clipInfo.softness.y = vy / clipInfo.softness.y;
else
clipInfo.softness.y = 10000f;
if (clipInfo.softness.z > 0)
clipInfo.softness.z = vx / clipInfo.softness.z;
else
clipInfo.softness.z = 10000f;
if (clipInfo.softness.w > 0)
clipInfo.softness.w = vy / clipInfo.softness.w;
else
clipInfo.softness.w = 10000f;
}
}
///
///
///
///
///
public void EnterClipping(uint clipId, bool reversedMask)
{
_clipStack.Push(clipInfo);
if (stencilReferenceValue == 0)
stencilReferenceValue = 1;
else
stencilReferenceValue = stencilReferenceValue << 1;
if (reversedMask)
{
if (clipInfo.reversed)
stencilCompareValue = (stencilReferenceValue >> 1) - 1;
else
stencilCompareValue = stencilReferenceValue - 1;
}
else
stencilCompareValue = (stencilReferenceValue << 1) - 1;
clipInfo.clipId = clipId;
clipInfo.referenceValue = stencilReferenceValue;
clipInfo.reversed = reversedMask;
clipped = true;
}
///
///
///
public void LeaveClipping()
{
clipInfo = _clipStack.Pop();
stencilReferenceValue = clipInfo.referenceValue;
rectMaskDepth = clipInfo.rectMaskDepth;
clipped = stencilReferenceValue != 0 || rectMaskDepth != 0;
}
public void EnterPaintingMode()
{
//Reset clipping
_clipStack.Push(clipInfo);
clipInfo.rectMaskDepth = 0;
clipInfo.referenceValue = 0;
clipInfo.reversed = false;
clipped = false;
}
public void LeavePaintingMode()
{
clipInfo = _clipStack.Pop();
stencilReferenceValue = clipInfo.referenceValue;
rectMaskDepth = clipInfo.rectMaskDepth;
clipped = stencilReferenceValue != 0 || rectMaskDepth != 0;
}
public void ApplyClippingProperties(Material mat, bool isStdMaterial)
{
if (rectMaskDepth > 0) //在矩形剪裁下,且不是遮罩对象
{
mat.SetVector(ShaderConfig.ID_ClipBox, clipInfo.clipBox);
if (clipInfo.soft)
mat.SetVector(ShaderConfig.ID_ClipSoftness, clipInfo.softness);
}
if (stencilReferenceValue > 0)
{
mat.SetInt(ShaderConfig.ID_StencilComp, (int)UnityEngine.Rendering.CompareFunction.Equal);
mat.SetInt(ShaderConfig.ID_Stencil, stencilCompareValue);
mat.SetInt(ShaderConfig.ID_Stencil2, stencilCompareValue);
mat.SetInt(ShaderConfig.ID_StencilOp, (int)UnityEngine.Rendering.StencilOp.Keep);
mat.SetInt(ShaderConfig.ID_StencilReadMask, stencilReferenceValue | (stencilReferenceValue - 1));
mat.SetInt(ShaderConfig.ID_ColorMask, 15);
}
else
{
mat.SetInt(ShaderConfig.ID_StencilComp, (int)UnityEngine.Rendering.CompareFunction.Always);
mat.SetInt(ShaderConfig.ID_Stencil, 0);
mat.SetInt(ShaderConfig.ID_Stencil2, 0);
mat.SetInt(ShaderConfig.ID_StencilOp, (int)UnityEngine.Rendering.StencilOp.Keep);
mat.SetInt(ShaderConfig.ID_StencilReadMask, 255);
mat.SetInt(ShaderConfig.ID_ColorMask, 15);
}
if (!isStdMaterial)
{
if (rectMaskDepth > 0)
{
if (clipInfo.soft)
mat.EnableKeyword("SOFT_CLIPPED");
else
mat.EnableKeyword("CLIPPED");
}
else
{
mat.DisableKeyword("CLIPPED");
mat.DisableKeyword("SOFT_CLIPPED");
}
}
}
public void ApplyAlphaMaskProperties(Material mat, bool erasing)
{
if (!erasing)
{
if (stencilReferenceValue == 1)
{
mat.SetInt(ShaderConfig.ID_StencilComp, (int)UnityEngine.Rendering.CompareFunction.Always);
mat.SetInt(ShaderConfig.ID_Stencil, 1);
mat.SetInt(ShaderConfig.ID_StencilOp, (int)UnityEngine.Rendering.StencilOp.Replace);
mat.SetInt(ShaderConfig.ID_StencilReadMask, 255);
mat.SetInt(ShaderConfig.ID_ColorMask, 0);
}
else
{
if (stencilReferenceValue != 0 & _clipStack.Peek().reversed)
mat.SetInt(ShaderConfig.ID_StencilComp, (int)UnityEngine.Rendering.CompareFunction.NotEqual);
else
mat.SetInt(ShaderConfig.ID_StencilComp, (int)UnityEngine.Rendering.CompareFunction.Equal);
mat.SetInt(ShaderConfig.ID_Stencil, stencilReferenceValue | (stencilReferenceValue - 1));
mat.SetInt(ShaderConfig.ID_StencilOp, (int)UnityEngine.Rendering.StencilOp.Replace);
mat.SetInt(ShaderConfig.ID_StencilReadMask, stencilReferenceValue - 1);
mat.SetInt(ShaderConfig.ID_ColorMask, 0);
}
}
else
{
if (stencilReferenceValue != 0 & _clipStack.Peek().reversed)
{
int refValue = stencilReferenceValue | (stencilReferenceValue - 1);
mat.SetInt(ShaderConfig.ID_StencilComp, (int)UnityEngine.Rendering.CompareFunction.Equal);
mat.SetInt(ShaderConfig.ID_Stencil, refValue);
mat.SetInt(ShaderConfig.ID_StencilOp, (int)UnityEngine.Rendering.StencilOp.Zero);
mat.SetInt(ShaderConfig.ID_StencilReadMask, refValue);
mat.SetInt(ShaderConfig.ID_ColorMask, 0);
}
else
{
int refValue = stencilReferenceValue - 1;
mat.SetInt(ShaderConfig.ID_StencilComp, (int)UnityEngine.Rendering.CompareFunction.Equal);
mat.SetInt(ShaderConfig.ID_Stencil, refValue);
mat.SetInt(ShaderConfig.ID_StencilOp, (int)UnityEngine.Rendering.StencilOp.Replace);
mat.SetInt(ShaderConfig.ID_StencilReadMask, refValue);
mat.SetInt(ShaderConfig.ID_ColorMask, 0);
}
}
}
}
}