using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
using System.Collections;
#if UNITY_EDITOR
using System.IO;
using System.Linq;
using UnityEditor;
#endif
namespace Coffee.UIExtensions
{
///
/// UIEffect.
///
[ExecuteInEditMode]
[DisallowMultipleComponent]
public class ShinyEffectForUGUI : BaseMeshEffect
#if UNITY_EDITOR
, ISerializationCallbackReceiver
#endif
{
//################################
// Constant or Static Members.
//################################
public const string shaderName = "UI/Hidden/UI-Effect-Shiny";
//################################
// Serialize Members.
//################################
[SerializeField][Range(0, 1)] float m_Location = 0;
[SerializeField][Range(0, 1)] float m_Width = 0.25f;
[SerializeField][Range(0.01f, 1)] float m_Softness = 1f;
[FormerlySerializedAs("m_Alpha")]
[SerializeField][Range(0, 1)] float m_Brightness = 1f;
[SerializeField][Range(-180, 180)] float m_Rotation;
[SerializeField][Range(0, 1)] float m_Highlight = 1;
[SerializeField] Material m_EffectMaterial;
//################################
// Public Members.
//################################
///
/// Graphic affected by the UIEffect.
///
new public Graphic graphic { get { return base.graphic; } }
///
/// Location for shiny effect.
///
public float location{ get { return m_Location; } set { m_Location = Mathf.Clamp(value, 0, 1); _SetDirty(); } }
///
/// Width for shiny effect.
///
public float width { get { return m_Width; } set { m_Width = Mathf.Clamp(value, 0, 1); _SetDirty(); } }
///
/// Softness for shiny effect.
///
public float softness { get { return m_Softness; } set { m_Softness = Mathf.Clamp(value, 0.01f, 1); _SetDirty(); } }
///
/// Alpha for shiny effect.
///
[System.Obsolete ("Use brightness instead (UnityUpgradable) -> brightness")]
public float alpha { get { return m_Brightness; } set { m_Brightness = Mathf.Clamp(value, 0, 1); _SetDirty(); } }
///
/// Brightness for shiny effect.
///
public float brightness { get { return m_Brightness; } set { m_Brightness = Mathf.Clamp(value, 0, 1); _SetDirty(); } }
///
/// Highlight factor for shiny effect.
///
public float highlight { get { return m_Highlight; } set { m_Highlight = Mathf.Clamp(value, 0, 1); _SetDirty(); } }
///
/// Rotation for shiny effect.
///
public float rotation
{
get
{
return m_Rotation;
}
set { if (!Mathf.Approximately(m_Rotation, value)) { m_Rotation = value; _SetDirty(); } }
}
///
/// Effect material.
///
public virtual Material effectMaterial { get { return m_EffectMaterial; } }
///
/// This function is called when the object becomes enabled and active.
///
protected override void OnEnable()
{
graphic.material = effectMaterial;
base.OnEnable();
}
///
/// This function is called when the behaviour becomes disabled () or inactive.
///
protected override void OnDisable()
{
graphic.material = null;
base.OnDisable();
}
#if UNITY_EDITOR
public void OnBeforeSerialize()
{
}
public void OnAfterDeserialize()
{
var obj = this;
EditorApplication.delayCall += () =>
{
if (Application.isPlaying || !obj)
return;
var mat = GetMaterial(shaderName);
if(m_EffectMaterial == mat && graphic.material == mat)
return;
graphic.material = m_EffectMaterial = mat;
EditorUtility.SetDirty(this);
EditorUtility.SetDirty(graphic);
EditorApplication.delayCall +=AssetDatabase.SaveAssets;
};
}
public static Material GetMaterial(string shaderName)
{
string name = Path.GetFileName (shaderName);
return AssetDatabase.FindAssets("t:Material " + name)
.Select(x => AssetDatabase.GUIDToAssetPath(x))
.SelectMany(x => AssetDatabase.LoadAllAssetsAtPath(x))
.OfType()
.FirstOrDefault(x => x.name == name);
}
#endif
///
/// Modifies the mesh.
///
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive())
return;
// rect.
Rect rect = graphic.rectTransform.rect;
// rotation.
float rad = rotation * Mathf.Deg2Rad;
Vector2 dir = new Vector2(Mathf.Cos(rad), Mathf.Sin(rad));
dir.x *= rect.height / rect.width;
dir = dir.normalized;
// Calculate vertex position.
UIVertex vertex = default(UIVertex);
Vector2 nomalizedPos;
Matrix2x3 localMatrix = new Matrix2x3(rect, dir.x, dir.y); // Get local matrix.
for (int i = 0; i < vh.currentVertCount; i++)
{
vh.PopulateUIVertex(ref vertex, i);
// Normalize vertex position by local matrix.
nomalizedPos = localMatrix * vertex.position;
vertex.uv1 = new Vector2(
_PackToFloat(Mathf.Clamp01(nomalizedPos.y), softness, width, brightness),
_PackToFloat(location, highlight)
);
vh.SetUIVertex(vertex, i);
}
}
///
/// Play effect.
///
public void Play()
{
Play(1);
}
///
/// Play effect.
///
public void Play(float duration)
{
StopAllCoroutines();
StartCoroutine(CoPlay(duration, AnimatorUpdateMode.Normal));
}
///
/// Play effect.
///
public void Play(float duration, AnimatorUpdateMode updateMode)
{
StopAllCoroutines();
StartCoroutine(CoPlay(duration, updateMode));
}
//################################
// Private Members.
//################################
///
/// Mark the UIEffect as dirty.
///
void _SetDirty()
{
if(graphic)
graphic.SetVerticesDirty();
}
IEnumerator CoPlay(float duration, AnimatorUpdateMode updateMode = AnimatorUpdateMode.Normal)
{
float time = 0;
while (time < duration)
{
location = time / duration;
time += updateMode == AnimatorUpdateMode.UnscaledTime
? Time.unscaledDeltaTime
: Time.deltaTime;
yield return null;
}
}
///
/// Pack 4 low-precision [0-1] floats values to a float.
/// Each value [0-1] has 64 steps(6 bits).
///
static float _PackToFloat(float x, float y, float z, float w)
{
const int PRECISION = (1 << 6) - 1;
return (Mathf.FloorToInt(w * PRECISION) << 18)
+ (Mathf.FloorToInt(z * PRECISION) << 12)
+ (Mathf.FloorToInt(y * PRECISION) << 6)
+ Mathf.FloorToInt(x * PRECISION);
}
///
/// Pack 2 low-precision [0-1] floats values to a float.
/// Each value [0-1] has 4096 steps(12 bits).
///
static float _PackToFloat(float x, float y)
{
const int PRECISION = (1 << 12) - 1;
return (Mathf.FloorToInt(y * PRECISION) << 12)
+ Mathf.FloorToInt(x * PRECISION);
}
///
/// Matrix2x3.
///
struct Matrix2x3
{
public float m00, m01, m02, m10, m11, m12;
public Matrix2x3(Rect rect, float cos, float sin)
{
const float center = 0.5f;
float dx = -rect.xMin / rect.width - center;
float dy = -rect.yMin / rect.height - center;
m00 = cos / rect.width;
m01 = -sin / rect.height;
m02 = dx * cos - dy * sin + center;
m10 = sin / rect.width;
m11 = cos / rect.height;
m12 = dx * sin + dy * cos + center;
}
public static Vector2 operator*(Matrix2x3 m, Vector2 v)
{
return new Vector2(
(m.m00 * v.x) + (m.m01 * v.y) + m.m02,
(m.m10 * v.x) + (m.m11 * v.y) + m.m12
);
}
}
}
}