320 lines
11 KiB
C#
320 lines
11 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using FairyGUI.Utils;
|
||
|
||
namespace FairyGUI
|
||
{
|
||
/// <summary>
|
||
/// UpdateContext is for internal use.
|
||
/// </summary>
|
||
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<ClipInfo> _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<ClipInfo>();
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public void End()
|
||
{
|
||
working = false;
|
||
|
||
if (OnEnd != null)
|
||
OnEnd.Invoke();
|
||
|
||
OnEnd = null;
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="clipId"></param>
|
||
/// <param name="clipRect"></param>
|
||
/// <param name="softness"></param>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="clipId"></param>
|
||
/// <param name="reversedMask"></param>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
#if UNITY_2019_3_OR_NEWER
|
||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||
static void InitializeOnLoad()
|
||
{
|
||
OnBegin = null;
|
||
OnEnd = null;
|
||
}
|
||
#endif
|
||
}
|
||
}
|