From 30629e52717e5f5a05b4dd947a17bc9bea0ced5f Mon Sep 17 00:00:00 2001 From: mob-sakai <12690315+mob-sakai@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:58:24 +0900 Subject: [PATCH] feat: gradation feature close #277 --- Packages/src/Editor/UIEffectEditor.cs | 30 ++ Packages/src/Runtime/Enums.cs | 12 + Packages/src/Runtime/UIEffect.cs | 65 ++++ Packages/src/Runtime/UIEffectContext.cs | 12 + .../src/Runtime/Utilities/GradientUtil.cs | 281 ++++++++++++++++++ .../Runtime/Utilities/GradientUtil.cs.meta | 11 + 6 files changed, 411 insertions(+) create mode 100644 Packages/src/Runtime/Utilities/GradientUtil.cs create mode 100644 Packages/src/Runtime/Utilities/GradientUtil.cs.meta diff --git a/Packages/src/Editor/UIEffectEditor.cs b/Packages/src/Editor/UIEffectEditor.cs index a481a033..d3f3caf0 100644 --- a/Packages/src/Editor/UIEffectEditor.cs +++ b/Packages/src/Editor/UIEffectEditor.cs @@ -58,6 +58,11 @@ public class UIEffect2Editor : Editor private SerializedProperty _shadowEffectOnOrigin; private SerializedProperty _shadowMirrorScale; + private SerializedProperty _gradationMode; + private SerializedProperty _gradationColor1; + private SerializedProperty _gradationColor2; + private SerializedProperty _gradationGradient; + private void OnEnable() { if (target == null) return; @@ -104,6 +109,11 @@ private void OnEnable() _shadowFade = serializedObject.FindProperty("m_ShadowFade"); _shadowEffectOnOrigin = serializedObject.FindProperty("m_ShadowEffectOnOrigin"); _shadowMirrorScale = serializedObject.FindProperty("m_ShadowMirrorScale"); + + _gradationMode = serializedObject.FindProperty("m_GradationMode"); + _gradationColor1 = serializedObject.FindProperty("m_GradationColor1"); + _gradationColor2 = serializedObject.FindProperty("m_GradationColor2"); + _gradationGradient = serializedObject.FindProperty("m_GradationGradient"); } public override void OnInspectorGUI() @@ -233,6 +243,26 @@ public void DrawProperties() EditorGUILayout.PropertyField(_shadowEffectOnOrigin); EditorGUI.indentLevel--; } + + // Gradient + DrawSeparator(); + if (DrawHeaderPopup(_gradationMode)) + { + EditorGUI.indentLevel++; + switch ((GradationMode)_gradationMode.intValue) + { + case GradationMode.HorizontalGradient: + case GradationMode.VerticalGradient: + EditorGUILayout.PropertyField(_gradationGradient); + break; + default: + EditorGUILayout.PropertyField(_gradationColor1); + EditorGUILayout.PropertyField(_gradationColor2); + break; + } + + EditorGUI.indentLevel--; + } } private static void DrawColor(SerializedProperty filter, SerializedProperty color, ColorFilter prevFilter) diff --git a/Packages/src/Runtime/Enums.cs b/Packages/src/Runtime/Enums.cs index 494dba0c..6aa8deff 100644 --- a/Packages/src/Runtime/Enums.cs +++ b/Packages/src/Runtime/Enums.cs @@ -76,6 +76,18 @@ public enum ShadowMode Mirror } + public enum GradationMode + { + None = 0, + Horizontal, + HorizontalGradient, + Vertical, + VerticalGradient, + Radial, + DiagonalLeftTopToRightBottom, + DiagonalRightTopToLeftBottom, + } + public enum PreferSamplingSize { None = 0, diff --git a/Packages/src/Runtime/UIEffect.cs b/Packages/src/Runtime/UIEffect.cs index 0ca5fbd3..193c92ba 100644 --- a/Packages/src/Runtime/UIEffect.cs +++ b/Packages/src/Runtime/UIEffect.cs @@ -127,6 +127,18 @@ public class UIEffect : UIEffectBase [SerializeField] protected float m_ShadowMirrorScale = 0.5f; + [SerializeField] + protected GradationMode m_GradationMode = GradationMode.None; + + [SerializeField] + protected Color m_GradationColor1 = Color.white; + + [SerializeField] + protected Color m_GradationColor2 = Color.white; + + [SerializeField] + private Gradient m_GradationGradient = new Gradient(); + public ToneFilter toneFilter { get => m_ToneFilter; @@ -521,6 +533,50 @@ public float shadowMirrorScale } } + public GradationMode gradationMode + { + get => m_GradationMode; + set + { + if (m_GradationMode == value) return; + context.gradationMode = m_GradationMode = value; + SetVerticesDirty(); + } + } + + public Color gradationColor1 + { + get => m_GradationColor1; + set + { + if (m_GradationColor1 == value) return; + context.gradationColor1 = m_GradationColor1 = value; + SetVerticesDirty(); + } + } + + public Color gradationColor2 + { + get => m_GradationColor2; + set + { + if (m_GradationColor2 == value) return; + context.gradationColor2 = m_GradationColor2 = value; + SetVerticesDirty(); + } + } + + public Gradient gradationGradient + { + get => m_GradationGradient; + set + { + if (m_GradationGradient == value) return; + context.gradationGradient = m_GradationGradient = value; + SetVerticesDirty(); + } + } + public List replicas => _replicas ??= ListPool.Rent(); private List _replicas; @@ -604,6 +660,10 @@ protected override void UpdateContext(UIEffectContext c) c.shadowFade = m_ShadowFade; c.shadowEffectOnOrigin = m_ShadowEffectOnOrigin; c.shadowMirrorScale = m_ShadowMirrorScale; + c.gradationMode = m_GradationMode; + c.gradationColor1 = m_GradationColor1; + c.gradationColor2 = m_GradationColor2; + c.gradationGradient = m_GradationGradient; } public override void ApplyContextToMaterial() @@ -749,6 +809,11 @@ internal void CopyFrom(UIEffectContext c) m_ShadowEffectOnOrigin = c.shadowEffectOnOrigin; m_ShadowMirrorScale = c.shadowMirrorScale; + m_GradationMode = c.gradationMode; + m_GradationColor1 = c.gradationColor1; + m_GradationColor2 = c.gradationColor2; + m_GradationGradient = c.gradationGradient; + UpdateContext(context); ApplyContextToMaterial(); SetVerticesDirty(); diff --git a/Packages/src/Runtime/UIEffectContext.cs b/Packages/src/Runtime/UIEffectContext.cs index fc872cc6..fffdf618 100644 --- a/Packages/src/Runtime/UIEffectContext.cs +++ b/Packages/src/Runtime/UIEffectContext.cs @@ -132,6 +132,10 @@ public class UIEffectContext public float shadowFade = 0.9f; public bool shadowEffectOnOrigin = false; public float shadowMirrorScale = 0.5f; + public GradationMode gradationMode; + public Color gradationColor1; + public Color gradationColor2; + public Gradient gradationGradient; public bool willModifyMaterial => samplingFilter != SamplingFilter.None || transitionFilter != TransitionFilter.None @@ -184,6 +188,11 @@ public void CopyFrom(UIEffectContext preset) shadowIteration = preset.shadowIteration; shadowFade = preset.shadowFade; shadowEffectOnOrigin = preset.shadowEffectOnOrigin; + + gradationMode = preset.gradationMode; + gradationColor1 = preset.gradationColor1; + gradationColor2 = preset.gradationColor2; + gradationGradient = preset.gradationGradient; } public void ApplyToMaterial(Material material, float actualSamplingScale = 1f) @@ -385,6 +394,9 @@ public void ModifyMesh(Graphic graphic, RectTransform transitionRoot, VertexHelp } } } + + // Apply gradation. + GradientUtil.DoGradient(gradationMode, s_WorkingVertices, gradationColor1, gradationColor2, gradationGradient, rect); // Apply shadow. if (shadowMode != ShadowMode.None) diff --git a/Packages/src/Runtime/Utilities/GradientUtil.cs b/Packages/src/Runtime/Utilities/GradientUtil.cs new file mode 100644 index 00000000..ae039bf7 --- /dev/null +++ b/Packages/src/Runtime/Utilities/GradientUtil.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Coffee.UIEffects +{ + public static class GradientUtil + { + private static readonly List s_KeyTimes = new List(); + + public static void DoGradient(GradationMode mode, List verts, Color a, Color b, Gradient gradient, + Rect rect) + { + switch (mode) + { + case GradationMode.Horizontal: + DoHorizontalGradient(verts, a, b, rect); + break; + case GradationMode.Vertical: + DoVerticalGradient(verts, a, b, rect); + break; + case GradationMode.DiagonalLeftTopToRightBottom: + DoDiagonalGradientLeftTopToRightBottom(verts, a, b, rect); + break; + case GradationMode.DiagonalRightTopToLeftBottom: + DoDiagonalGradientRightTopToLeftBottom(verts, a, b, rect); + break; + case GradationMode.Radial: + DoRadialGradient(verts, a, b, rect); + break; + case GradationMode.HorizontalGradient: + DoHorizontalGradient(verts, gradient, rect); + break; + case GradationMode.VerticalGradient: + DoVerticalGradient(verts, gradient, rect); + break; + } + } + + private static void DoHorizontalGradient(List verts, Color a, Color b, Rect rect) + { + DoGradient(verts, a, b, rect, (x, y) => x); + } + + private static void DoHorizontalGradient(List verts, Gradient gradient, Rect rect) + { + GetKeyTimes(gradient, s_KeyTimes); + var count = verts.Count; + for (var i = 0; i < verts.Count; i += 6) + { + GetBundle(verts, i, out var lb, out var lt, out var rt, out var rb); + + if (i + 5 < count) + { + SplitHorizontal(verts, lb, lt, ref rb, ref rt, rect, s_KeyTimes); + } + + lb.color *= gradient.Evaluate(Mathf.InverseLerp(rect.xMin, rect.xMax, lb.position.x + 0.0001f)); + lt.color *= gradient.Evaluate(Mathf.InverseLerp(rect.xMin, rect.xMax, lt.position.x + 0.0001f)); + rt.color *= gradient.Evaluate(Mathf.InverseLerp(rect.xMin, rect.xMax, rt.position.x - 0.0001f)); + rb.color *= gradient.Evaluate(Mathf.InverseLerp(rect.xMin, rect.xMax, rb.position.x - 0.0001f)); + SetBundle(verts, i, lb, lt, rt, rb); + } + } + + private static void DoVerticalGradient(List verts, Color a, Color b, Rect rect) + { + DoGradient(verts, a, b, rect, (x, y) => 1 - y); + } + + private static void DoVerticalGradient(List verts, Gradient gradient, Rect rect) + { + GetKeyTimes(gradient, s_KeyTimes); + var count = verts.Count; + for (var i = 0; i < verts.Count; i += 6) + { + GetBundle(verts, i, out var lb, out var lt, out var rt, out var rb); + + if (i + 5 < count) + { + SplitVertical(verts, lb, ref lt, rb, ref rt, rect, s_KeyTimes); + } + + lb.color *= gradient.Evaluate(Mathf.InverseLerp(rect.yMin, rect.yMax, lb.position.y + 0.0001f)); + lt.color *= gradient.Evaluate(Mathf.InverseLerp(rect.yMin, rect.yMax, lt.position.y - 0.0001f)); + rt.color *= gradient.Evaluate(Mathf.InverseLerp(rect.yMin, rect.yMax, rt.position.y - 0.0001f)); + rb.color *= gradient.Evaluate(Mathf.InverseLerp(rect.yMin, rect.yMax, rb.position.y + 0.0001f)); + SetBundle(verts, i, lb, lt, rt, rb); + } + } + + private static void DoDiagonalGradientLeftTopToRightBottom(List verts, Color a, Color b, Rect rect) + { + DoGradient(verts, a, b, rect, (x, y) => 1 - (1 - x + y) / 2); + } + + private static void DoDiagonalGradientRightTopToLeftBottom(List verts, Color a, Color b, Rect rect) + { + DoGradient(verts, a, b, rect, (x, y) => 1 - (x + y) / 2); + } + + private static void DoRadialGradient(List verts, Color a, Color b, Rect rect) + { + for (var i = 0; i < verts.Count; i += 6) + { + GetBundle(verts, i, out var lb, out var lt, out var rt, out var rb); + + SplitHorizontal(verts, lb, lt, ref rb, ref rt, rect, 0.25f); + SplitHorizontal(verts, lb, lt, ref rb, ref rt, rect, 0.5f); + SplitHorizontal(verts, lb, lt, ref rb, ref rt, rect, 0.75f); + SplitVertical(verts, lb, ref lt, rb, ref rt, rect, 0.25f); + SplitVertical(verts, lb, ref lt, rb, ref rt, rect, 0.5f); + SplitVertical(verts, lb, ref lt, rb, ref rt, rect, 0.75f); + + lb.color *= DoGradient(lb, a, b, rect, Rad); + lt.color *= DoGradient(lt, a, b, rect, Rad); + rt.color *= DoGradient(rt, a, b, rect, Rad); + rb.color *= DoGradient(rb, a, b, rect, Rad); + SetBundle(verts, i, lb, lt, rt, rb); + continue; + + float Rad(float x, float y) + { + x -= 0.5f; + y -= 0.5f; + return Mathf.Sqrt(x * x + y * y); + } + } + } + + private static void DoGradient(List verts, Color a, Color b, Rect rect, + Func getTime) + { + for (var i = 0; i < verts.Count; i++) + { + var vt = verts[i]; + vt.color *= DoGradient(vt, a, b, rect, getTime); + verts[i] = vt; + } + } + + private static Color DoGradient(UIVertex vt, Color a, Color b, Rect rect, Func getTime) + { + var x = Mathf.InverseLerp(rect.xMin, rect.xMax, vt.position.x); + var y = Mathf.InverseLerp(rect.yMin, rect.yMax, vt.position.y); + return Color.Lerp(a, b, getTime(x, y)); + } + + private static void SplitHorizontal(List verts, UIVertex lb, UIVertex lt, ref UIVertex rb, + ref UIVertex rt, Rect rect, float t) + { + var min = Mathf.InverseLerp(rect.xMin, rect.xMax, lb.position.x); + var max = Mathf.InverseLerp(rect.xMin, rect.xMax, rt.position.x); + SplitHorizontal(verts, lb, lt, ref rb, ref rt, min, max, t); + } + + private static void SplitHorizontal(List verts, UIVertex lb, UIVertex lt, ref UIVertex rb, + ref UIVertex rt, Rect rect, List keyTimes) + { + if (keyTimes == null || keyTimes.Count == 0) return; + + var min = Mathf.InverseLerp(rect.xMin, rect.xMax, lb.position.x); + foreach (var keyTime in keyTimes) + { + if (keyTime <= 0 || 1 <= keyTime) continue; + + var max = Mathf.InverseLerp(rect.xMin, rect.xMax, rt.position.x); + SplitHorizontal(verts, lb, lt, ref rb, ref rt, min, max, keyTime); + } + } + + private static void SplitHorizontal(List verts, UIVertex lb, UIVertex lt, ref UIVertex rb, + ref UIVertex rt, float min, float max, float t) + { + if (t <= min || max <= t) return; + + t = Mathf.InverseLerp(min, max, t); + var mb = VertexLerp(lb, rb, t); + var mt = VertexLerp(lt, rt, t); + verts.Add(mb); + verts.Add(mt); + verts.Add(rt); + verts.Add(rt); + verts.Add(rb); + verts.Add(mb); + rt = mt; + rb = mb; + } + + private static void SplitVertical(List verts, UIVertex lb, ref UIVertex lt, UIVertex rb, + ref UIVertex rt, Rect rect, float t) + { + var min = Mathf.InverseLerp(rect.yMin, rect.yMax, lb.position.y); + var max = Mathf.InverseLerp(rect.yMin, rect.yMax, rt.position.y); + SplitVertical(verts, lb, ref lt, rb, ref rt, min, max, t); + } + + private static void SplitVertical(List verts, UIVertex lb, ref UIVertex lt, UIVertex rb, + ref UIVertex rt, Rect rect, List keyTimes) + { + var min = Mathf.InverseLerp(rect.yMin, rect.yMax, lb.position.y); + foreach (var keyTime in keyTimes) + { + var max = Mathf.InverseLerp(rect.yMin, rect.yMax, rt.position.y); + SplitVertical(verts, lb, ref lt, rb, ref rt, min, max, keyTime); + } + } + + private static void SplitVertical(List verts, UIVertex lb, ref UIVertex lt, UIVertex rb, + ref UIVertex rt, float min, float max, float t) + { + if (t <= min || max <= t) return; + + t = Mathf.InverseLerp(min, max, t); + var lm = VertexLerp(lb, lt, t); + var rm = VertexLerp(rb, rt, t); + verts.Add(lm); + verts.Add(lt); + verts.Add(rt); + verts.Add(rt); + verts.Add(rm); + verts.Add(lm); + lt = lm; + rt = rm; + } + + private static void GetKeyTimes(Gradient gradient, List results) + { + results.Clear(); + var colors = gradient.colorKeys; + for (var i = 0; i < colors.Length; i++) + { + var t = colors[i].time; + if (t <= 0 || 1 <= t) continue; + + results.Add(colors[i].time); + } + + var alphas = gradient.alphaKeys; + for (var i = 0; i < alphas.Length; i++) + { + var t = alphas[i].time; + if (t <= 0 || 1 <= t || results.Contains(t)) continue; + + results.Add(t); + } + + results.Sort((a, b) => b.CompareTo(a)); + } + + private static void GetBundle(List verts, int i, out UIVertex lb, out UIVertex lt, out UIVertex rt, + out UIVertex rb) + { + lb = verts[i]; + lt = verts[i + 1]; + rt = verts[i + 2]; + rb = verts[i + 4]; + } + + private static void SetBundle(List verts, int i, UIVertex lb, UIVertex lt, UIVertex rt, UIVertex rb) + { + verts[i] = verts[i + 5] = lb; + verts[i + 1] = lt; + verts[i + 2] = verts[i + 3] = rt; + verts[i + 4] = rb; + } + + private static UIVertex VertexLerp(UIVertex a, UIVertex b, float t) + { + var vt = new UIVertex(); + vt.position = Vector3.Lerp(a.position, b.position, t); + vt.normal = Vector3.Lerp(a.normal, b.normal, t); + vt.tangent = Vector4.Lerp(a.tangent, b.tangent, t); + vt.color = Color.Lerp(a.color, b.color, t); + vt.uv0 = Vector4.Lerp(a.uv0, b.uv0, t); + vt.uv1 = Vector4.Lerp(a.uv1, b.uv1, t); + vt.uv2 = Vector4.Lerp(a.uv2, b.uv2, t); + return vt; + } + } +} diff --git a/Packages/src/Runtime/Utilities/GradientUtil.cs.meta b/Packages/src/Runtime/Utilities/GradientUtil.cs.meta new file mode 100644 index 00000000..7b5379c0 --- /dev/null +++ b/Packages/src/Runtime/Utilities/GradientUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 23cc1f211cd264050a641c01c4a6d77a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: