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.UIExtensions { /// /// UIEffect. /// [ExecuteInEditMode] [RequireComponent(typeof(Graphic))] [DisallowMultipleComponent] [AddComponentMenu("UI/UIEffect/UIEffect", 1)] public class UIEffect : UIEffectBase { //################################ // Constant or Static Members. //################################ public const string shaderName = "UI/Hidden/UI-Effect"; static readonly ParameterTexture _ptex = new ParameterTexture(4, 1024, "_ParamTex"); //################################ // Serialize Members. //################################ [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; #pragma warning disable 0414 [Obsolete][HideInInspector] [SerializeField][Range(0, 1)] float m_ShadowBlur = 1; [Obsolete][HideInInspector] [SerializeField] ShadowStyle m_ShadowStyle; [Obsolete][HideInInspector] [SerializeField] Color m_ShadowColor = Color.black; [Obsolete][HideInInspector] [SerializeField] Vector2 m_EffectDistance = new Vector2(1f, -1f); [Obsolete][HideInInspector] [SerializeField] bool m_UseGraphicAlpha = true; [Obsolete][HideInInspector] [SerializeField] Color m_EffectColor = Color.white; [Obsolete][HideInInspector] [SerializeField] List m_AdditionalShadows = new List(); #pragma warning restore 0414 public enum BlurEx { None = 0, Ex = 1, } //################################ // Public Members. //################################ #if UNITY_2017_1_OR_NEWER public override AdditionalCanvasShaderChannels requiredChannels { get { if (advancedBlur) { return isTMPro ? AdditionalCanvasShaderChannels.TexCoord1 | AdditionalCanvasShaderChannels.TexCoord2 : AdditionalCanvasShaderChannels.TexCoord1; } return AdditionalCanvasShaderChannels.None; } } #endif /// /// Effect factor between 0(no effect) and 1(complete effect). /// [System.Obsolete("Use effectFactor instead (UnityUpgradable) -> effectFactor")] public float toneLevel { get { return m_EffectFactor; } set { m_EffectFactor = Mathf.Clamp(value, 0, 1); SetDirty(); } } /// /// Effect factor between 0(no effect) and 1(complete effect). /// public float effectFactor { get { return m_EffectFactor; } set { m_EffectFactor = Mathf.Clamp(value, 0, 1); SetDirty(); } } /// /// Color effect factor between 0(no effect) and 1(complete effect). /// public float colorFactor { get { return m_ColorFactor; } set { m_ColorFactor = Mathf.Clamp(value, 0, 1); SetDirty(); } } /// /// How far is the blurring from the graphic. /// [System.Obsolete("Use blurFactor instead (UnityUpgradable) -> blurFactor")] public float blur { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 1); SetDirty(); } } /// /// How far is the blurring from the graphic. /// [System.Obsolete("Use effectFactor instead (UnityUpgradable) -> effectFactor")] public float blurFactor { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 1); SetDirty(); } } /// /// Effect mode(readonly). /// [System.Obsolete("Use effectMode instead (UnityUpgradable) -> effectMode")] public EffectMode toneMode { get { return m_EffectMode; } } /// /// Effect mode(readonly). /// public EffectMode effectMode { get { return m_EffectMode; } } /// /// Color effect mode(readonly). /// public ColorMode colorMode { get { return m_ColorMode; } } /// /// Blur effect mode(readonly). /// public BlurMode blurMode { get { return m_BlurMode; } } /// /// Color for the color effect. /// public Color effectColor { get { return graphic.color; } set { graphic.color = value; SetDirty(); } } /// /// Gets the parameter texture. /// public override ParameterTexture ptex { get { return _ptex; } } /// /// Advanced blurring remove common artifacts in the blur effect for uGUI. /// public bool advancedBlur { get { return isTMPro ? (material && material.IsKeywordEnabled("EX")) : m_AdvancedBlur; } } /// /// Modifies the mesh. /// public override void ModifyMesh(VertexHelper vh) { if (!isActiveAndEnabled) { return; } float normalizedIndex = ptex.GetNormalizedIndex(this); if (m_BlurMode != BlurMode.None && advancedBlur) { vh.GetUIVertexStream(tempVerts); vh.Clear(); var count = tempVerts.Count; // Bundle int bundleSize = (targetGraphic is Text || isTMPro) ? 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(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 = tempVerts[i + j + 1].position; Vector3 cornerPos2 = tempVerts[i + j + 4].position; // Is outer quad? bool hasOuterEdge = (bundleSize == 6) || !posBounds.Contains(cornerPos1) || !posBounds.Contains(cornerPos2); if (hasOuterEdge) { Vector3 cornerUv1 = tempVerts[i + j + 1].uv0; Vector3 cornerUv2 = 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 = 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; if (isTMPro) { #if UNITY_2017_1_OR_NEWER vt.uv2 = uvMask; #endif } else { vt.uv1 = uvMask; } tempVerts[i + j + k] = vt; } } } vh.AddUIVertexTriangleStream(tempVerts); 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 SetDirty() { foreach (var m in materials) { ptex.RegisterMaterial (m); } ptex.SetData(this, 0, m_EffectFactor); // param.x : effect factor ptex.SetData(this, 1, m_ColorFactor); // param.y : color factor ptex.SetData(this, 2, m_BlurFactor); // param.z : blur factor } #if UNITY_EDITOR /// /// Gets the material. /// /// The material. protected override Material GetMaterial() { if (isTMPro) { return null; } return MaterialResolver.GetOrGenerateMaterialVariant(Shader.Find(shaderName), m_EffectMode, m_ColorMode, m_BlurMode, m_AdvancedBlur ? BlurEx.Ex : BlurEx.None); } #pragma warning disable 0612 protected override void UpgradeIfNeeded() { // Upgrade for v3.0.0 if (IsShouldUpgrade(300)) { if (m_ColorMode != ColorMode.Multiply) { Color col = targetGraphic.color; col.r = m_EffectColor.r; col.g = m_EffectColor.g; col.b = m_EffectColor.b; targetGraphic.color = col; m_ColorFactor = m_EffectColor.a; } if (m_ShadowStyle != ShadowStyle.None || m_AdditionalShadows.Any(x => x.style != ShadowStyle.None)) { if (m_ShadowStyle != ShadowStyle.None) { var shadow = gameObject.GetComponent() ?? gameObject.AddComponent(); shadow.style = m_ShadowStyle; shadow.effectDistance = m_EffectDistance; shadow.effectColor = m_ShadowColor; shadow.useGraphicAlpha = m_UseGraphicAlpha; shadow.blurFactor = m_ShadowBlur; } foreach (var s in m_AdditionalShadows) { if (s.style == ShadowStyle.None) { continue; } var shadow = gameObject.AddComponent(); shadow.style = s.style; shadow.effectDistance = s.effectDistance; shadow.effectColor = s.effectColor; shadow.useGraphicAlpha = s.useGraphicAlpha; shadow.blurFactor = s.blur; } m_ShadowStyle = ShadowStyle.None; m_AdditionalShadows = null; if (m_EffectMode == EffectMode.None && m_ColorMode == ColorMode.Multiply && m_BlurMode == BlurMode.None) { DestroyImmediate(this, true); } } int tone = (int)m_EffectMode; const int Mono = 5; const int Cutoff = 6; const int Hue = 7; if (tone == Hue) { var go = gameObject; var hue = m_EffectFactor; DestroyImmediate(this, true); var hsv = go.GetComponent() ?? go.AddComponent(); hsv.hue = hue; hsv.range = 1; } // Cutoff/Mono if (tone == Cutoff || tone == Mono) { var go = gameObject; var factor = m_EffectFactor; var transitionMode = tone == Cutoff ? UITransitionEffect.EffectMode.Cutoff : UITransitionEffect.EffectMode.Fade; DestroyImmediate(this, true); var trans = go.GetComponent() ?? go.AddComponent(); trans.effectFactor = factor; var sp = new SerializedObject(trans).FindProperty("m_EffectMode"); sp.intValue = (int)transitionMode; sp.serializedObject.ApplyModifiedProperties(); } } } #pragma warning restore 0612 #endif //################################ // Private Members. //################################ 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); } } }