using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.Serialization; #if UNITY_EDITOR using System.IO; using System.Linq; using UnityEditor; #endif namespace Coffee.UIEffects { /// /// UIEffect. /// [ExecuteInEditMode] [RequireComponent(typeof(Graphic))] [DisallowMultipleComponent] [AddComponentMenu("UI/UIEffects/UIEffect", 1)] public class UIEffect : BaseMaterialEffect, IMaterialModifier { private const uint k_ShaderId = 2 << 3; private static readonly ParameterTexture s_ParamTex = new ParameterTexture(4, 1024, "_ParamTex"); [FormerlySerializedAs("m_ToneLevel")] [Tooltip("Effect factor between 0(no effect) and 1(complete effect).")] [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; [FormerlySerializedAs("m_Blur")] [Tooltip("How far is the blurring from the graphic.")] [SerializeField] [Range(0, 1)] float m_BlurFactor = 1; [FormerlySerializedAs("m_ToneMode")] [Tooltip("Effect mode")] [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.None; [Tooltip("Advanced blurring remove common artifacts in the blur effect for uGUI.")] [SerializeField] bool m_AdvancedBlur = false; private enum BlurEx { None = 0, Ex = 1, } /// /// Additional canvas shader channels to use this component. /// public AdditionalCanvasShaderChannels uvMaskChannel { get { return connector.extraChannel; } } /// /// Effect factor between 0(no effect) and 1(complete effect). /// public float effectFactor { get { return m_EffectFactor; } set { value = Mathf.Clamp(value, 0, 1); if (Mathf.Approximately(m_EffectFactor, value)) return; m_EffectFactor = value; SetEffectParamsDirty(); } } /// /// Color effect factor between 0(no effect) and 1(complete effect). /// public float colorFactor { get { return m_ColorFactor; } set { value = Mathf.Clamp(value, 0, 1); if (Mathf.Approximately(m_ColorFactor, value)) return; m_ColorFactor = value; SetEffectParamsDirty(); } } /// /// How far is the blurring from the graphic. /// public float blurFactor { get { return m_BlurFactor; } set { value = Mathf.Clamp(value, 0, 1); if (Mathf.Approximately(m_BlurFactor, value)) return; m_BlurFactor = value; SetEffectParamsDirty(); } } /// /// Effect mode. /// public EffectMode effectMode { get { return m_EffectMode; } set { if (m_EffectMode == value) return; m_EffectMode = value; SetMaterialDirty(); } } /// /// Color effect mode. /// public ColorMode colorMode { get { return m_ColorMode; } set { if (m_ColorMode == value) return; m_ColorMode = value; SetMaterialDirty(); } } /// /// Blur effect mode(readonly). /// public BlurMode blurMode { get { return m_BlurMode; } set { if (m_BlurMode == value) return; m_BlurMode = value; SetMaterialDirty(); } } /// /// Gets the parameter texture. /// public override ParameterTexture paramTex { get { return s_ParamTex; } } /// /// Advanced blurring remove common artifacts in the blur effect for uGUI. /// public bool advancedBlur { get { return m_AdvancedBlur; } set { if (m_AdvancedBlur == value) return; m_AdvancedBlur = value; SetVerticesDirty(); SetMaterialDirty(); } } public override Hash128 GetMaterialHash(Material material) { if (!isActiveAndEnabled || !material || !material.shader) return k_InvalidHash; var shaderVariantId = (uint) (((int) m_EffectMode << 6) + ((int) m_ColorMode << 9) + ((int) m_BlurMode << 11) + ((m_AdvancedBlur ? 1 : 0) << 13)); return new Hash128( (uint) material.GetInstanceID(), k_ShaderId + shaderVariantId, 0, 0 ); } public override void ModifyMaterial(Material newMaterial, Graphic graphic) { var connector = GraphicConnector.FindConnector(graphic); newMaterial.shader = Shader.Find(string.Format("Hidden/{0} (UIEffect)", newMaterial.shader.name)); SetShaderVariants(newMaterial, m_EffectMode, m_ColorMode, m_BlurMode, m_AdvancedBlur ? BlurEx.Ex : BlurEx.None); paramTex.RegisterMaterial(newMaterial); } /// /// Modifies the mesh. /// public override void ModifyMesh(VertexHelper vh, Graphic graphic) { if (!isActiveAndEnabled) { return; } var normalizedIndex = paramTex.GetNormalizedIndex(this); if (m_BlurMode != BlurMode.None && advancedBlur) { vh.GetUIVertexStream(s_TempVerts); vh.Clear(); var count = s_TempVerts.Count; // Bundle int bundleSize = connector.IsText(graphic) ? 6 : count; Rect posBounds = default(Rect); Rect uvBounds = default(Rect); Vector3 size = default(Vector3); Vector3 tPos = default(Vector3); Vector3 tUV = default(Vector3); float expand = (float) blurMode * 6 * 2; for (int i = 0; i < count; i += bundleSize) { // min/max for bundled-quad GetBounds(s_TempVerts, i, bundleSize, ref posBounds, ref uvBounds, true); // Pack uv mask. Vector2 uvMask = new Vector2(Packer.ToFloat(uvBounds.xMin, uvBounds.yMin), Packer.ToFloat(uvBounds.xMax, uvBounds.yMax)); // Quad for (int j = 0; j < bundleSize; j += 6) { Vector3 cornerPos1 = s_TempVerts[i + j + 1].position; Vector3 cornerPos2 = s_TempVerts[i + j + 4].position; // Is outer quad? bool hasOuterEdge = (bundleSize == 6) || !posBounds.Contains(cornerPos1) || !posBounds.Contains(cornerPos2); if (hasOuterEdge) { Vector3 cornerUv1 = s_TempVerts[i + j + 1].uv0; Vector3 cornerUv2 = s_TempVerts[i + j + 4].uv0; Vector3 centerPos = (cornerPos1 + cornerPos2) / 2; Vector3 centerUV = (cornerUv1 + cornerUv2) / 2; size = (cornerPos1 - cornerPos2); size.x = 1 + expand / Mathf.Abs(size.x); size.y = 1 + expand / Mathf.Abs(size.y); size.z = 1 + expand / Mathf.Abs(size.z); tPos = centerPos - Vector3.Scale(size, centerPos); tUV = centerUV - Vector3.Scale(size, centerUV); } // Vertex for (int k = 0; k < 6; k++) { UIVertex vt = s_TempVerts[i + j + k]; Vector3 pos = vt.position; Vector2 uv0 = vt.uv0; if (hasOuterEdge && (pos.x < posBounds.xMin || posBounds.xMax < pos.x)) { pos.x = pos.x * size.x + tPos.x; uv0.x = uv0.x * size.x + tUV.x; } if (hasOuterEdge && (pos.y < posBounds.yMin || posBounds.yMax < pos.y)) { pos.y = pos.y * size.y + tPos.y; uv0.y = uv0.y * size.y + tUV.y; } vt.uv0 = new Vector2(Packer.ToFloat((uv0.x + 0.5f) / 2f, (uv0.y + 0.5f) / 2f), normalizedIndex); vt.position = pos; connector.SetExtraChannel(ref vt, uvMask); s_TempVerts[i + j + k] = vt; } } } vh.AddUIVertexTriangleStream(s_TempVerts); s_TempVerts.Clear(); } else { int count = vh.currentVertCount; UIVertex vt = default(UIVertex); for (int i = 0; i < count; i++) { vh.PopulateUIVertex(ref vt, i); Vector2 uv0 = vt.uv0; vt.uv0 = new Vector2( Packer.ToFloat((uv0.x + 0.5f) / 2f, (uv0.y + 0.5f) / 2f), normalizedIndex ); vh.SetUIVertex(vt, i); } } } protected override void SetEffectParamsDirty() { paramTex.SetData(this, 0, m_EffectFactor); // param.x : effect factor paramTex.SetData(this, 1, m_ColorFactor); // param.y : color factor paramTex.SetData(this, 2, m_BlurFactor); // param.z : blur factor } static void GetBounds(List verts, int start, int count, ref Rect posBounds, ref Rect uvBounds, bool global) { Vector2 minPos = new Vector2(float.MaxValue, float.MaxValue); Vector2 maxPos = new Vector2(float.MinValue, float.MinValue); Vector2 minUV = new Vector2(float.MaxValue, float.MaxValue); Vector2 maxUV = new Vector2(float.MinValue, float.MinValue); for (int i = start; i < start + count; i++) { UIVertex vt = verts[i]; Vector2 uv = vt.uv0; Vector3 pos = vt.position; // Left-Bottom if (minPos.x >= pos.x && minPos.y >= pos.y) { minPos = pos; } // Right-Top else if (maxPos.x <= pos.x && maxPos.y <= pos.y) { maxPos = pos; } // Left-Bottom if (minUV.x >= uv.x && minUV.y >= uv.y) { minUV = uv; } // Right-Top else if (maxUV.x <= uv.x && maxUV.y <= uv.y) { maxUV = uv; } } // Shrink coordinate for detect edge posBounds.Set(minPos.x + 0.001f, minPos.y + 0.001f, maxPos.x - minPos.x - 0.002f, maxPos.y - minPos.y - 0.002f); uvBounds.Set(minUV.x, minUV.y, maxUV.x - minUV.x, maxUV.y - minUV.y); } } }