AxibugEmuOnline/AxibugEmuOnline.Client.Transplant/Assets/Plugins/Coffee/UIExtensions/UIEffect/Scripts/UIEffectCapturedImage.cs

552 lines
18 KiB
C#

using System.Collections;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace Coffee.UIExtensions
{
/// <summary>
/// UIEffectCapturedImage
/// </summary>
[AddComponentMenu("UI/UIEffect/UIEffectCapturedImage", 200)]
public class UIEffectCapturedImage : RawImage
#if UNITY_EDITOR
, ISerializationCallbackReceiver
#endif
{
//################################
// Constant or Static Members.
//################################
public const string shaderName = "UI/Hidden/UI-EffectCapture";
/// <summary>
/// Desampling rate.
/// </summary>
public enum DesamplingRate
{
None = 0,
x1 = 1,
x2 = 2,
x4 = 4,
x8 = 8,
}
//################################
// Serialize Members.
//################################
[Tooltip("Effect factor between 0(no effect) and 1(complete effect).")]
[FormerlySerializedAs("m_ToneLevel")]
[SerializeField][Range(0, 1)] float m_EffectFactor = 1;
[Tooltip("Color effect factor between 0(no effect) and 1(complete effect).")]
[SerializeField][Range(0, 1)] float m_ColorFactor = 1;
[Tooltip("How far is the blurring from the graphic.")]
[FormerlySerializedAs("m_Blur")]
[SerializeField][Range(0, 1)] float m_BlurFactor = 1;
[Tooltip("Effect mode.")]
[FormerlySerializedAs("m_ToneMode")]
[SerializeField] EffectMode m_EffectMode = EffectMode.None;
[Tooltip("Color effect mode.")]
[SerializeField] ColorMode m_ColorMode = ColorMode.Multiply;
[Tooltip("Blur effect mode.")]
[SerializeField] BlurMode m_BlurMode = BlurMode.DetailBlur;
[Tooltip("Color for the color effect.")]
[SerializeField] Color m_EffectColor = Color.white;
[Tooltip("Desampling rate of the generated RenderTexture.")]
[SerializeField] DesamplingRate m_DesamplingRate = DesamplingRate.x1;
[Tooltip("Desampling rate of reduction buffer to apply effect.")]
[SerializeField] DesamplingRate m_ReductionRate = DesamplingRate.x1;
[Tooltip("FilterMode for capturing.")]
[SerializeField] FilterMode m_FilterMode = FilterMode.Bilinear;
[Tooltip("Effect material.")]
[SerializeField] Material m_EffectMaterial = null;
[Tooltip("Blur iterations.")]
[FormerlySerializedAs("m_Iterations")]
[SerializeField][Range(1, 8)] int m_BlurIterations = 3;
[Tooltip("Fits graphic size to screen on captured.")]
[FormerlySerializedAs("m_KeepCanvasSize")]
[SerializeField] bool m_FitToScreen = true;
[Tooltip("Capture automatically on enable.")]
[SerializeField] bool m_CaptureOnEnable = false;
//################################
// Public Members.
//################################
/// <summary>
/// Effect factor between 0(no effect) and 1(complete effect).
/// </summary>
[System.Obsolete("Use effectFactor instead (UnityUpgradable) -> effectFactor")]
public float toneLevel { get { return m_EffectFactor; } set { m_EffectFactor = Mathf.Clamp(value, 0, 1); } }
/// <summary>
/// Effect factor between 0(no effect) and 1(complete effect).
/// </summary>
public float effectFactor { get { return m_EffectFactor; } set { m_EffectFactor = Mathf.Clamp(value, 0, 1); } }
/// <summary>
/// Color effect factor between 0(no effect) and 1(complete effect).
/// </summary>
public float colorFactor { get { return m_ColorFactor; } set { m_ColorFactor = Mathf.Clamp(value, 0, 1); } }
/// <summary>
/// How far is the blurring from the graphic.
/// </summary>
[System.Obsolete("Use blurFactor instead (UnityUpgradable) -> blurFactor")]
public float blur { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 4); } }
/// <summary>
/// How far is the blurring from the graphic.
/// </summary>
public float blurFactor { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 4); } }
/// <summary>
/// Tone effect mode.
/// </summary>
[System.Obsolete("Use effectMode instead (UnityUpgradable) -> effectMode")]
public EffectMode toneMode { get { return m_EffectMode; } }
/// <summary>
/// Effect mode.
/// </summary>
public EffectMode effectMode { get { return m_EffectMode; } }
/// <summary>
/// Color effect mode.
/// </summary>
public ColorMode colorMode { get { return m_ColorMode; } }
/// <summary>
/// Blur effect mode.
/// </summary>
public BlurMode blurMode { get { return m_BlurMode; } }
/// <summary>
/// Color for the color effect.
/// </summary>
public Color effectColor { get { return m_EffectColor; } set { m_EffectColor = value; } }
/// <summary>
/// Effect material.
/// </summary>
public virtual Material effectMaterial { get { return m_EffectMaterial; } }
/// <summary>
/// Desampling rate of the generated RenderTexture.
/// </summary>
public DesamplingRate desamplingRate { get { return m_DesamplingRate; } set { m_DesamplingRate = value; } }
/// <summary>
/// Desampling rate of reduction buffer to apply effect.
/// </summary>
public DesamplingRate reductionRate { get { return m_ReductionRate; } set { m_ReductionRate = value; } }
/// <summary>
/// FilterMode for capturing.
/// </summary>
public FilterMode filterMode { get { return m_FilterMode; } set { m_FilterMode = value; } }
/// <summary>
/// Captured texture.
/// </summary>
public RenderTexture capturedTexture { get { return _rt; } }
/// <summary>
/// Blur iterations.
/// </summary>
[System.Obsolete("Use blurIterations instead (UnityUpgradable) -> blurIterations")]
public int iterations { get { return m_BlurIterations; } set { m_BlurIterations = value; } }
/// <summary>
/// Blur iterations.
/// </summary>
public int blurIterations { get { return m_BlurIterations; } set { m_BlurIterations = value; } }
/// <summary>
/// Fits graphic size to screen.
/// </summary>
[System.Obsolete("Use fitToScreen instead (UnityUpgradable) -> fitToScreen")]
public bool keepCanvasSize { get { return m_FitToScreen; } set { m_FitToScreen = value; } }
/// <summary>
/// Fits graphic size to screen on captured.
/// </summary>
public bool fitToScreen { get { return m_FitToScreen; } set { m_FitToScreen = value; } }
/// <summary>
/// Target RenderTexture to capture.
/// </summary>
[System.Obsolete]
public RenderTexture targetTexture { get { return null; } set { } }
/// <summary>
/// Capture automatically on enable.
/// </summary>
public bool captureOnEnable { get { return m_CaptureOnEnable; } set { m_CaptureOnEnable = value; } }
/// <summary>
/// This function is called when the object becomes enabled and active.
/// </summary>
protected override void OnEnable()
{
base.OnEnable();
if (m_CaptureOnEnable && Application.isPlaying)
{
Capture();
}
}
protected override void OnDisable()
{
base.OnDisable();
if (m_CaptureOnEnable && Application.isPlaying)
{
_Release(false);
texture = null;
}
}
/// <summary>
/// This function is called when the MonoBehaviour will be destroyed.
/// </summary>
protected override void OnDestroy()
{
Release();
base.OnDestroy();
}
/// <summary>
/// Callback function when a UI element needs to generate vertices.
/// </summary>
protected override void OnPopulateMesh(VertexHelper vh)
{
// When not displaying, clear vertex.
if (texture == null || color.a < 1 / 255f || canvasRenderer.GetAlpha() < 1 / 255f)
{
vh.Clear();
}
else
{
base.OnPopulateMesh(vh);
int count = vh.currentVertCount;
UIVertex vt = default(UIVertex);
Color c = color;
for (int i = 0; i < count; i++)
{
vh.PopulateUIVertex(ref vt, i);
vt.color = c;
vh.SetUIVertex(vt, i);
}
}
}
/// <summary>
/// Gets the size of the desampling.
/// </summary>
public void GetDesamplingSize(DesamplingRate rate, out int w, out int h)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
var res = UnityEditor.UnityStats.screenRes.Split('x');
w = int.Parse(res[0]);
h = int.Parse(res[1]);
}
else
#endif
{
w = Screen.width;
h = Screen.height;
}
if (rate == DesamplingRate.None)
return;
float aspect = (float)w / h;
if (w < h)
{
h = Mathf.ClosestPowerOfTwo(h / (int)rate);
w = Mathf.CeilToInt(h * aspect);
}
else
{
w = Mathf.ClosestPowerOfTwo(w / (int)rate);
h = Mathf.CeilToInt(w / aspect);
}
}
/// <summary>
/// Capture rendering result.
/// </summary>
public void Capture()
{
// Fit to screen.
var rootCanvas = canvas.rootCanvas;
if (m_FitToScreen)
{
var rootTransform = rootCanvas.transform as RectTransform;
var size = rootTransform.rect.size;
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x);
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y);
rectTransform.position = rootTransform.position;
}
// Cache some ids.
if (s_CopyId == 0)
{
s_CopyId = Shader.PropertyToID("_UIEffectCapturedImage_ScreenCopyId");
s_EffectId1 = Shader.PropertyToID("_UIEffectCapturedImage_EffectId1");
s_EffectId2 = Shader.PropertyToID("_UIEffectCapturedImage_EffectId2");
s_EffectFactorId = Shader.PropertyToID("_EffectFactor");
s_ColorFactorId = Shader.PropertyToID("_ColorFactor");
s_CommandBuffer = new CommandBuffer();
}
// If size of result RT has changed, release it.
int w, h;
GetDesamplingSize(m_DesamplingRate, out w, out h);
if (_rt && (_rt.width != w || _rt.height != h))
{
_Release(ref _rt);
}
// Generate RT for result.
if (_rt == null)
{
_rt = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default);
_rt.filterMode = m_FilterMode;
_rt.useMipMap = false;
_rt.wrapMode = TextureWrapMode.Clamp;
_rtId = new RenderTargetIdentifier(_rt);
}
SetupCommandBuffer();
}
void SetupCommandBuffer()
{
// Material for effect.
Material mat = m_EffectMaterial;
if (s_CommandBuffer == null)
{
s_CommandBuffer = new CommandBuffer();
}
// [1] Capture from back buffer (back buffer -> copied screen).
int w, h;
GetDesamplingSize(DesamplingRate.None, out w, out h);
s_CommandBuffer.GetTemporaryRT(s_CopyId, w, h, 0, m_FilterMode);
#if UNITY_EDITOR
s_CommandBuffer.Blit(Resources.FindObjectsOfTypeAll<RenderTexture>().FirstOrDefault(x => x.name == "GameView RT"), s_CopyId);
#else
s_CommandBuffer.Blit(BuiltinRenderTextureType.BindableTexture, s_CopyId);
#endif
// Set properties for effect.
s_CommandBuffer.SetGlobalVector(s_EffectFactorId, new Vector4(m_EffectFactor, 0));
s_CommandBuffer.SetGlobalVector(s_ColorFactorId, new Vector4(m_EffectColor.r, m_EffectColor.g, m_EffectColor.b, m_EffectColor.a));
// [2] Apply base effect with reduction buffer (copied screen -> effect1).
GetDesamplingSize(m_ReductionRate, out w, out h);
s_CommandBuffer.GetTemporaryRT(s_EffectId1, w, h, 0, m_FilterMode);
s_CommandBuffer.Blit(s_CopyId, s_EffectId1, mat, 0);
s_CommandBuffer.ReleaseTemporaryRT(s_CopyId);
// Iterate blurring operation.
if (m_BlurMode != BlurMode.None)
{
s_CommandBuffer.GetTemporaryRT(s_EffectId2, w, h, 0, m_FilterMode);
for (int i = 0; i < m_BlurIterations; i++)
{
// [3] Apply blurring with reduction buffer (effect1 -> effect2, or effect2 -> effect1).
s_CommandBuffer.SetGlobalVector(s_EffectFactorId, new Vector4(m_BlurFactor, 0));
s_CommandBuffer.Blit(s_EffectId1, s_EffectId2, mat, 1);
s_CommandBuffer.SetGlobalVector(s_EffectFactorId, new Vector4(0, m_BlurFactor));
s_CommandBuffer.Blit(s_EffectId2, s_EffectId1, mat, 1);
}
s_CommandBuffer.ReleaseTemporaryRT(s_EffectId2);
}
// [4] Copy to result RT.
s_CommandBuffer.Blit(s_EffectId1, _rtId);
s_CommandBuffer.ReleaseTemporaryRT(s_EffectId1);
#if UNITY_EDITOR
if (!Application.isPlaying)
{
Graphics.ExecuteCommandBuffer(s_CommandBuffer);
UpdateTexture();
return;
}
#endif
// Execute command buffer.
canvas.rootCanvas.GetComponent<CanvasScaler>().StartCoroutine(_CoUpdateTextureOnNextFrame());
}
/// <summary>
/// Release captured image.
/// </summary>
public void Release()
{
_Release(true);
texture = null;
_SetDirty();
}
#if UNITY_EDITOR
protected override void Reset()
{
// Set parameters as 'Medium'.
m_BlurIterations = 3;
m_FilterMode = FilterMode.Bilinear;
m_DesamplingRate = DesamplingRate.x1;
m_ReductionRate = DesamplingRate.x1;
base.Reset();
}
/// <summary>
/// Raises the before serialize event.
/// </summary>
public void OnBeforeSerialize()
{
}
/// <summary>
/// Raises the after deserialize event.
/// </summary>
public void OnAfterDeserialize()
{
UnityEditor.EditorApplication.delayCall += () => UpdateMaterial(true);
}
/// <summary>
/// Raises the validate event.
/// </summary>
protected override void OnValidate()
{
base.OnValidate();
UnityEditor.EditorApplication.delayCall += () => UpdateMaterial(false);
}
/// <summary>
/// Updates the material.
/// </summary>
/// <param name="ignoreInPlayMode">If set to <c>true</c> ignore in play mode.</param>
protected void UpdateMaterial(bool ignoreInPlayMode)
{
if (!this || ignoreInPlayMode && Application.isPlaying)
{
return;
}
var mat = MaterialResolver.GetOrGenerateMaterialVariant(Shader.Find(shaderName), m_EffectMode, m_ColorMode, m_BlurMode);
if (m_EffectMaterial != mat)
{
material = null;
m_EffectMaterial = mat;
_SetDirty();
}
}
#endif
//################################
// Private Members.
//################################
RenderTexture _rt;
RenderTargetIdentifier _rtId;
static int s_CopyId;
static int s_EffectId1;
static int s_EffectId2;
static int s_EffectFactorId;
static int s_ColorFactorId;
static CommandBuffer s_CommandBuffer;
/// <summary>
/// Release genarated objects.
/// </summary>
/// <param name="releaseRT">If set to <c>true</c> release cached RenderTexture.</param>
void _Release(bool releaseRT)
{
if (releaseRT)
{
texture = null;
_Release(ref _rt);
}
if (s_CommandBuffer != null)
{
s_CommandBuffer.Clear();
if (releaseRT)
{
s_CommandBuffer.Release();
s_CommandBuffer = null;
}
}
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void _SetDirty()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
}
void _Release(ref RenderTexture obj)
{
if (obj)
{
obj.Release();
RenderTexture.ReleaseTemporary(obj);
obj = null;
}
}
/// <summary>
/// Set texture on next frame.
/// </summary>
IEnumerator _CoUpdateTextureOnNextFrame()
{
yield return new WaitForEndOfFrame();
UpdateTexture();
}
void UpdateTexture()
{
#if !UNITY_EDITOR
// Execute command buffer.
Graphics.ExecuteCommandBuffer (s_CommandBuffer);
#endif
_Release(false);
texture = capturedTexture;
_SetDirty();
}
}
}