using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Serialization; using UnityEngine.UI; #if UNITY_EDITOR using System.IO; using System.Linq; using UnityEditor; #endif namespace Coffee.UIExtensions { /// /// UIEffect. /// [RequireComponent(typeof(Graphic))] [AddComponentMenu("UI/UIEffect/UIShadow", 100)] public class UIShadow : BaseMeshEffect, IParameterTexture #if UNITY_EDITOR , ISerializationCallbackReceiver #endif { /// /// Additional shadow. /// [System.Obsolete] [System.Serializable] public class AdditionalShadow { /// /// How far is the blurring shadow from the graphic. /// [FormerlySerializedAs("shadowBlur")] [Range(0, 1)] public float blur = 0.25f; /// /// Shadow effect mode. /// [FormerlySerializedAs("shadowMode")] public ShadowStyle style = ShadowStyle.Shadow; /// /// Color for the shadow effect. /// [FormerlySerializedAs("shadowColor")] public Color effectColor = Color.black; /// /// How far is the shadow from the graphic. /// public Vector2 effectDistance = new Vector2(1f, -1f); /// /// Should the shadow inherit the alpha from the graphic? /// public bool useGraphicAlpha = true; } //################################ // Serialize Members. //################################ [Tooltip("How far is the blurring shadow from the graphic.")] [FormerlySerializedAs("m_Blur")] [SerializeField][Range(0, 1)] float m_BlurFactor = 1; [Tooltip("Shadow effect style.")] [SerializeField] ShadowStyle m_Style = ShadowStyle.Shadow; #pragma warning disable 0414 [HideInInspector][System.Obsolete] [SerializeField] List m_AdditionalShadows = new List(); #pragma warning restore 0414 [SerializeField] private Color m_EffectColor = new Color (0f, 0f, 0f, 0.5f); [SerializeField] private Vector2 m_EffectDistance = new Vector2 (1f, -1f); [SerializeField] private bool m_UseGraphicAlpha = true; private const float kMaxEffectDistance = 600f; public Color effectColor { get { return m_EffectColor; } set { m_EffectColor = value; if (graphic != null) graphic.SetVerticesDirty (); } } public Vector2 effectDistance { get { return m_EffectDistance; } set { if (value.x > kMaxEffectDistance) value.x = kMaxEffectDistance; if (value.x < -kMaxEffectDistance) value.x = -kMaxEffectDistance; if (value.y > kMaxEffectDistance) value.y = kMaxEffectDistance; if (value.y < -kMaxEffectDistance) value.y = -kMaxEffectDistance; if (m_EffectDistance == value) return; m_EffectDistance = value; if (graphic != null) graphic.SetVerticesDirty (); } } public bool useGraphicAlpha { get { return m_UseGraphicAlpha; } set { m_UseGraphicAlpha = value; if (graphic != null) graphic.SetVerticesDirty (); } } //################################ // Public Members. //################################ /// /// How far is the blurring shadow from the graphic. /// [System.Obsolete("Use blurFactor instead (UnityUpgradable) -> blurFactor")] public float blur { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 2); _SetDirty(); } } /// /// How far is the blurring shadow from the graphic. /// public float blurFactor { get { return m_BlurFactor; } set { m_BlurFactor = Mathf.Clamp(value, 0, 2); _SetDirty(); } } /// /// Shadow effect style. /// public ShadowStyle style { get { return m_Style; } set { m_Style = value; _SetDirty(); } } /// /// Gets or sets the parameter index. /// public int parameterIndex { get; set; } /// /// Gets the parameter texture. /// public ParameterTexture ptex{ get; private set; } int _graphicVertexCount; static readonly List tmpShadows = new List(); protected override void OnEnable() { base.OnEnable(); _uiEffect = GetComponent(); if (_uiEffect) { ptex = _uiEffect.ptex; ptex.Register(this); } #if TMP_PRESENT if (isTMPro) { textMeshPro.onCullStateChanged.AddListener (OnCullStateChanged); } #endif } protected override void OnDisable() { base.OnDisable(); _uiEffect = null; if (ptex != null) { ptex.Unregister(this); ptex = null; } } #if UNITY_EDITOR protected override void OnValidate () { effectDistance = m_EffectDistance; base.OnValidate (); } #endif #if TMP_PRESENT protected void OnCullStateChanged (bool state) { SetVerticesDirty (); } Vector2 res; protected override void LateUpdate () { if (res.x != Screen.width || res.y != Screen.height) { res.x = Screen.width; res.y = Screen.height; SetVerticesDirty (); } if (textMeshPro && transform.hasChanged) { transform.hasChanged = false; } base.LateUpdate (); } #endif /// /// Modifies the mesh. /// public override void ModifyMesh(VertexHelper vh) { if (!isActiveAndEnabled || vh.currentVertCount <= 0 || m_Style == ShadowStyle.None) { return; } vh.GetUIVertexStream(s_Verts); GetComponents(tmpShadows); foreach (var s in tmpShadows) { if (s.isActiveAndEnabled) { if (s == this) { foreach (var s2 in tmpShadows) { s2._graphicVertexCount = s_Verts.Count; } } break; } } tmpShadows.Clear(); //================================ // Append shadow vertices. //================================ { _uiEffect = _uiEffect ?? GetComponent(); var start = s_Verts.Count - _graphicVertexCount; var end = s_Verts.Count; if (ptex != null && _uiEffect && _uiEffect.isActiveAndEnabled) { ptex.SetData(this, 0, _uiEffect.effectFactor); // param.x : effect factor ptex.SetData(this, 1, 255); // param.y : color factor ptex.SetData(this, 2, m_BlurFactor); // param.z : blur factor } _ApplyShadow(s_Verts, effectColor, ref start, ref end, effectDistance, style, useGraphicAlpha); } vh.Clear(); vh.AddUIVertexTriangleStream(s_Verts); s_Verts.Clear(); } UIEffect _uiEffect; //################################ // Private Members. //################################ static readonly List s_Verts = new List(4096); /// /// Append shadow vertices. /// * It is similar to Shadow component implementation. /// void _ApplyShadow(List verts, Color color, ref int start, ref int end, Vector2 effectDistance, ShadowStyle style, bool useGraphicAlpha) { if (style == ShadowStyle.None || color.a <= 0) return; // Append Shadow. _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, effectDistance.x, effectDistance.y, useGraphicAlpha); // Append Shadow3. if (ShadowStyle.Shadow3 == style) { _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, effectDistance.x, 0, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, 0, effectDistance.y, useGraphicAlpha); } // Append Outline. else if (ShadowStyle.Outline == style) { _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, effectDistance.x, -effectDistance.y, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, -effectDistance.x, effectDistance.y, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, -effectDistance.x, -effectDistance.y, useGraphicAlpha); } // Append Outline8. else if (ShadowStyle.Outline8 == style) { _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, effectDistance.x, -effectDistance.y, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, -effectDistance.x, effectDistance.y, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, -effectDistance.x, -effectDistance.y, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, -effectDistance.x, 0, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, 0, -effectDistance.y, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, effectDistance.x, 0, useGraphicAlpha); _ApplyShadowZeroAlloc(s_Verts, color, ref start, ref end, 0, effectDistance.y, useGraphicAlpha); } } /// /// Append shadow vertices. /// * It is similar to Shadow component implementation. /// void _ApplyShadowZeroAlloc(List verts, Color color, ref int start, ref int end, float x, float y, bool useGraphicAlpha) { // Check list capacity. int count = end - start; var neededCapacity = verts.Count + count; if (verts.Capacity < neededCapacity) verts.Capacity *= 2; float normalizedIndex = ptex != null && _uiEffect && _uiEffect.isActiveAndEnabled ? ptex.GetNormalizedIndex(this) : -1; // Add UIVertex vt = default(UIVertex); for (int i = 0; i < count; i++) { verts.Add(vt); } // Move for (int i = verts.Count - 1; count <= i; i--) { verts[i] = verts[i - count]; } // Append shadow vertices to the front of list. // * The original vertex is pushed backward. for (int i = 0; i < count; ++i) { vt = verts[i + start + count]; Vector3 v = vt.position; vt.position.Set(v.x + x, v.y + y, v.z); Color vertColor = effectColor; vertColor.a = useGraphicAlpha ? color.a * vt.color.a / 255 : color.a; vt.color = vertColor; // Set UIEffect prameters if (0 <= normalizedIndex) { vt.uv0 = new Vector2( vt.uv0.x, normalizedIndex ); } verts[i] = vt; } // Update next shadow offset. start = end; end = verts.Count; } /// /// Mark the UIEffect as dirty. /// void _SetDirty() { if (graphic) graphic.SetVerticesDirty(); } #if UNITY_EDITOR public void OnBeforeSerialize() { } public void OnAfterDeserialize() { EditorApplication.delayCall += UpgradeIfNeeded; } #pragma warning disable 0612 void UpgradeIfNeeded() { if (0 < m_AdditionalShadows.Count) { 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_AdditionalShadows = null; } } #pragma warning restore 0612 #endif } }