Gradient2.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /// Credit Brad Nelson (playemgames - bitbucket)
  2. /// Modified Gradient effect script from http://answers.unity3d.com/questions/1086415/gradient-text-in-unity-522-basevertexeffect-is-obs.html
  3. /// <summary>
  4. /// -Uses Unity's Gradient class to define the color
  5. /// -Offset is now limited to -1,1
  6. /// -Multiple color blend modes
  7. ///
  8. /// Remember that for radial and diamond gradients, colors are applied per-vertex so if you have multiple points on your gradient where the color changes and there aren't enough vertices, you won't see all of the colors.
  9. /// </summary>
  10. using System;
  11. using System.Collections.Generic;
  12. namespace UnityEngine.UI.Extensions
  13. {
  14. [AddComponentMenu("UI/Effects/Extensions/Gradient2")]
  15. public class Gradient2 : BaseMeshEffect
  16. {
  17. [SerializeField]
  18. Type _gradientType;
  19. [SerializeField]
  20. Blend _blendMode = Blend.Multiply;
  21. [SerializeField]
  22. [Tooltip("Add vertices to display complex gradients. Turn off if your shape is already very complex, like text.")]
  23. bool _modifyVertices = true;
  24. [SerializeField]
  25. [Range(-1, 1)]
  26. float _offset = 0f;
  27. [SerializeField]
  28. [Range(0.1f, 10)]
  29. float _zoom = 1f;
  30. [SerializeField]
  31. UnityEngine.Gradient _effectGradient = new UnityEngine.Gradient() { colorKeys = new GradientColorKey[] { new GradientColorKey(Color.black, 0), new GradientColorKey(Color.white, 1) } };
  32. #region Properties
  33. public Blend BlendMode
  34. {
  35. get { return _blendMode; }
  36. set
  37. {
  38. _blendMode = value;
  39. graphic.SetVerticesDirty();
  40. }
  41. }
  42. public UnityEngine.Gradient EffectGradient
  43. {
  44. get { return _effectGradient; }
  45. set
  46. {
  47. _effectGradient = value;
  48. graphic.SetVerticesDirty();
  49. }
  50. }
  51. public Type GradientType
  52. {
  53. get { return _gradientType; }
  54. set
  55. {
  56. _gradientType = value;
  57. graphic.SetVerticesDirty();
  58. }
  59. }
  60. public bool ModifyVertices
  61. {
  62. get { return _modifyVertices; }
  63. set
  64. {
  65. _modifyVertices = value;
  66. graphic.SetVerticesDirty();
  67. }
  68. }
  69. public float Offset
  70. {
  71. get { return _offset; }
  72. set
  73. {
  74. _offset = Mathf.Clamp(value, -1f, 1f);
  75. graphic.SetVerticesDirty();
  76. }
  77. }
  78. public float Zoom
  79. {
  80. get { return _zoom; }
  81. set
  82. {
  83. _zoom = Mathf.Clamp(value, 0.1f, 10f);
  84. graphic.SetVerticesDirty();
  85. }
  86. }
  87. #endregion
  88. public override void ModifyMesh(VertexHelper helper)
  89. {
  90. if (!IsActive() || helper.currentVertCount == 0)
  91. return;
  92. List<UIVertex> _vertexList = new List<UIVertex>();
  93. helper.GetUIVertexStream(_vertexList);
  94. int nCount = _vertexList.Count;
  95. switch (GradientType)
  96. {
  97. case Type.Horizontal:
  98. case Type.Vertical:
  99. {
  100. Rect bounds = GetBounds(_vertexList);
  101. float min = bounds.xMin;
  102. float w = bounds.width;
  103. Func<UIVertex, float> GetPosition = v => v.position.x;
  104. if (GradientType == Type.Vertical)
  105. {
  106. min = bounds.yMin;
  107. w = bounds.height;
  108. GetPosition = v => v.position.y;
  109. }
  110. float width = w == 0f ? 0f : 1f / w / Zoom;
  111. float zoomOffset = (1 - (1 / Zoom)) * 0.5f;
  112. float offset = (Offset * (1 - zoomOffset)) - zoomOffset;
  113. if (ModifyVertices)
  114. {
  115. SplitTrianglesAtGradientStops(_vertexList, bounds, zoomOffset, helper);
  116. }
  117. UIVertex vertex = new UIVertex();
  118. for (int i = 0; i < helper.currentVertCount; i++)
  119. {
  120. helper.PopulateUIVertex(ref vertex, i);
  121. vertex.color = BlendColor(vertex.color, EffectGradient.Evaluate((GetPosition(vertex) - min) * width - offset));
  122. helper.SetUIVertex(vertex, i);
  123. }
  124. }
  125. break;
  126. case Type.Diamond:
  127. {
  128. Rect bounds = GetBounds(_vertexList);
  129. float height = bounds.height == 0f ? 0f : 1f / bounds.height / Zoom;
  130. float radius = bounds.center.y / 2f;
  131. Vector3 center = (Vector3.right + Vector3.up) * radius + Vector3.forward * _vertexList[0].position.z;
  132. if (ModifyVertices)
  133. {
  134. helper.Clear();
  135. for (int i = 0; i < nCount; i++) helper.AddVert(_vertexList[i]);
  136. UIVertex centralVertex = new UIVertex();
  137. centralVertex.position = center;
  138. centralVertex.normal = _vertexList[0].normal;
  139. centralVertex.uv0 = new Vector2(0.5f, 0.5f);
  140. centralVertex.color = Color.white;
  141. helper.AddVert(centralVertex);
  142. for (int i = 1; i < nCount; i++) helper.AddTriangle(i - 1, i, nCount);
  143. helper.AddTriangle(0, nCount - 1, nCount);
  144. }
  145. UIVertex vertex = new UIVertex();
  146. for (int i = 0; i < helper.currentVertCount; i++)
  147. {
  148. helper.PopulateUIVertex(ref vertex, i);
  149. vertex.color = BlendColor(vertex.color, EffectGradient.Evaluate(
  150. Vector3.Distance(vertex.position, center) * height - Offset));
  151. helper.SetUIVertex(vertex, i);
  152. }
  153. }
  154. break;
  155. case Type.Radial:
  156. {
  157. Rect bounds = GetBounds(_vertexList);
  158. float width = bounds.width == 0f ? 0f : 1f / bounds.width / Zoom;
  159. float height = bounds.height == 0f ? 0f : 1f / bounds.height / Zoom;
  160. if (ModifyVertices)
  161. {
  162. helper.Clear();
  163. float radiusX = bounds.width / 2f;
  164. float radiusY = bounds.height / 2f;
  165. UIVertex centralVertex = new UIVertex();
  166. centralVertex.position = Vector3.right * bounds.center.x + Vector3.up * bounds.center.y + Vector3.forward * _vertexList[0].position.z;
  167. centralVertex.normal = _vertexList[0].normal;
  168. centralVertex.uv0 = new Vector2(0.5f, 0.5f);
  169. centralVertex.color = Color.white;
  170. int steps = 64;
  171. for (int i = 0; i < steps; i++)
  172. {
  173. UIVertex curVertex = new UIVertex();
  174. float angle = (float)i * 360f / (float)steps;
  175. float cosX = Mathf.Cos(Mathf.Deg2Rad * angle);
  176. float cosY = Mathf.Sin(Mathf.Deg2Rad * angle);
  177. curVertex.position = Vector3.right * cosX * radiusX + Vector3.up * cosY * radiusY + Vector3.forward * _vertexList[0].position.z;
  178. curVertex.normal = _vertexList[0].normal;
  179. curVertex.uv0 = new Vector2((cosX + 1) * 0.5f, (cosY + 1) * 0.5f);
  180. curVertex.color = Color.white;
  181. helper.AddVert(curVertex);
  182. }
  183. helper.AddVert(centralVertex);
  184. for (int i = 1; i < steps; i++) helper.AddTriangle(i - 1, i, steps);
  185. helper.AddTriangle(0, steps - 1, steps);
  186. }
  187. UIVertex vertex = new UIVertex();
  188. for (int i = 0; i < helper.currentVertCount; i++)
  189. {
  190. helper.PopulateUIVertex(ref vertex, i);
  191. vertex.color = BlendColor(vertex.color, EffectGradient.Evaluate(
  192. Mathf.Sqrt(
  193. Mathf.Pow(Mathf.Abs(vertex.position.x - bounds.center.x) * width, 2f) +
  194. Mathf.Pow(Mathf.Abs(vertex.position.y - bounds.center.y) * height, 2f)) * 2f - Offset));
  195. helper.SetUIVertex(vertex, i);
  196. }
  197. }
  198. break;
  199. }
  200. }
  201. Rect GetBounds(List<UIVertex> vertices)
  202. {
  203. float left = vertices[0].position.x;
  204. float right = left;
  205. float bottom = vertices[0].position.y;
  206. float top = bottom;
  207. for (int i = vertices.Count - 1; i >= 1; --i)
  208. {
  209. float x = vertices[i].position.x;
  210. float y = vertices[i].position.y;
  211. if (x > right) right = x;
  212. else if (x < left) left = x;
  213. if (y > top) top = y;
  214. else if (y < bottom) bottom = y;
  215. }
  216. return new Rect(left, bottom, right - left, top - bottom);
  217. }
  218. void SplitTrianglesAtGradientStops(List<UIVertex> _vertexList, Rect bounds, float zoomOffset, VertexHelper helper)
  219. {
  220. List<float> stops = FindStops(zoomOffset, bounds);
  221. if (stops.Count > 0)
  222. {
  223. helper.Clear();
  224. int nCount = _vertexList.Count;
  225. for (int i = 0; i < nCount; i += 3)
  226. {
  227. float[] positions = GetPositions(_vertexList, i);
  228. List<int> originIndices = new List<int>(3);
  229. List<UIVertex> starts = new List<UIVertex>(3);
  230. List<UIVertex> ends = new List<UIVertex>(2);
  231. for (int s = 0; s < stops.Count; s++)
  232. {
  233. int initialCount = helper.currentVertCount;
  234. bool hadEnds = ends.Count > 0;
  235. bool earlyStart = false;
  236. // find any start vertices for this stop
  237. for (int p = 0; p < 3; p++)
  238. {
  239. if (!originIndices.Contains(p) && positions[p] < stops[s])
  240. {
  241. // make sure the first index crosses the stop
  242. int p1 = (p + 1) % 3;
  243. var start = _vertexList[p + i];
  244. if (positions[p1] > stops[s])
  245. {
  246. originIndices.Insert(0, p);
  247. starts.Insert(0, start);
  248. earlyStart = true;
  249. }
  250. else
  251. {
  252. originIndices.Add(p);
  253. starts.Add(start);
  254. }
  255. }
  256. }
  257. // bail if all before or after the stop
  258. if (originIndices.Count == 0)
  259. continue;
  260. if (originIndices.Count == 3)
  261. break;
  262. // report any start vertices
  263. foreach (var start in starts)
  264. helper.AddVert(start);
  265. // make two ends, splitting at the stop
  266. ends.Clear();
  267. foreach (int index in originIndices)
  268. {
  269. int oppositeIndex = (index + 1) % 3;
  270. if (positions[oppositeIndex] < stops[s])
  271. oppositeIndex = (oppositeIndex + 1) % 3;
  272. ends.Add(CreateSplitVertex(_vertexList[index + i], _vertexList[oppositeIndex + i], stops[s]));
  273. }
  274. if (ends.Count == 1)
  275. {
  276. int oppositeIndex = (originIndices[0] + 2) % 3;
  277. ends.Add(CreateSplitVertex(_vertexList[originIndices[0] + i], _vertexList[oppositeIndex + i], stops[s]));
  278. }
  279. // report end vertices
  280. foreach (var end in ends)
  281. helper.AddVert(end);
  282. // make triangles
  283. if (hadEnds)
  284. {
  285. helper.AddTriangle(initialCount - 2, initialCount, initialCount + 1);
  286. helper.AddTriangle(initialCount - 2, initialCount + 1, initialCount - 1);
  287. if (starts.Count > 0)
  288. {
  289. if (earlyStart)
  290. helper.AddTriangle(initialCount - 2, initialCount + 3, initialCount);
  291. else
  292. helper.AddTriangle(initialCount + 1, initialCount + 3, initialCount - 1);
  293. }
  294. }
  295. else
  296. {
  297. int vertexCount = helper.currentVertCount;
  298. helper.AddTriangle(initialCount, vertexCount - 2, vertexCount - 1);
  299. if (starts.Count > 1)
  300. helper.AddTriangle(initialCount, vertexCount - 1, initialCount + 1);
  301. }
  302. starts.Clear();
  303. }
  304. // clean up after looping through gradient stops
  305. if (ends.Count > 0)
  306. {
  307. // find any final vertices after the gradient stops
  308. if (starts.Count == 0)
  309. {
  310. for (int p = 0; p < 3; p++)
  311. {
  312. if (!originIndices.Contains(p) && positions[p] > stops[stops.Count - 1])
  313. {
  314. int p1 = (p + 1) % 3;
  315. UIVertex end = _vertexList[p + i];
  316. if (positions[p1] > stops[stops.Count - 1])
  317. starts.Insert(0, end);
  318. else
  319. starts.Add(end);
  320. }
  321. }
  322. }
  323. // report final vertices
  324. foreach (var start in starts)
  325. helper.AddVert(start);
  326. // make final triangle(s)
  327. int vertexCount = helper.currentVertCount;
  328. if (starts.Count > 1)
  329. {
  330. helper.AddTriangle(vertexCount - 4, vertexCount - 2, vertexCount - 1);
  331. helper.AddTriangle(vertexCount - 4, vertexCount - 1, vertexCount - 3);
  332. }
  333. else if (starts.Count > 0)
  334. {
  335. helper.AddTriangle(vertexCount - 3, vertexCount - 1, vertexCount - 2);
  336. }
  337. }
  338. else
  339. {
  340. // if the triangle wasn't split, add it as-is
  341. helper.AddVert(_vertexList[i]);
  342. helper.AddVert(_vertexList[i + 1]);
  343. helper.AddVert(_vertexList[i + 2]);
  344. int vertexCount = helper.currentVertCount;
  345. helper.AddTriangle(vertexCount - 3, vertexCount - 2, vertexCount - 1);
  346. }
  347. }
  348. }
  349. }
  350. float[] GetPositions(List<UIVertex> _vertexList, int index)
  351. {
  352. float[] positions = new float[3];
  353. if (GradientType == Type.Horizontal)
  354. {
  355. positions[0] = _vertexList[index].position.x;
  356. positions[1] = _vertexList[index + 1].position.x;
  357. positions[2] = _vertexList[index + 2].position.x;
  358. }
  359. else
  360. {
  361. positions[0] = _vertexList[index].position.y;
  362. positions[1] = _vertexList[index + 1].position.y;
  363. positions[2] = _vertexList[index + 2].position.y;
  364. }
  365. return positions;
  366. }
  367. List<float> FindStops(float zoomOffset, Rect bounds)
  368. {
  369. List<float> stops = new List<float>();
  370. var offset = Offset * (1 - zoomOffset);
  371. var startBoundary = zoomOffset - offset;
  372. var endBoundary = (1 - zoomOffset) - offset;
  373. foreach (var color in EffectGradient.colorKeys)
  374. {
  375. if (color.time >= endBoundary)
  376. break;
  377. if (color.time > startBoundary)
  378. stops.Add((color.time - startBoundary) * Zoom);
  379. }
  380. foreach (var alpha in EffectGradient.alphaKeys)
  381. {
  382. if (alpha.time >= endBoundary)
  383. break;
  384. if (alpha.time > startBoundary)
  385. stops.Add((alpha.time - startBoundary) * Zoom);
  386. }
  387. float min = bounds.xMin;
  388. float size = bounds.width;
  389. if (GradientType == Type.Vertical)
  390. {
  391. min = bounds.yMin;
  392. size = bounds.height;
  393. }
  394. stops.Sort();
  395. for (int i = 0; i < stops.Count; i++)
  396. {
  397. stops[i] = (stops[i] * size) + min;
  398. if (i > 0 && Math.Abs(stops[i] - stops[i - 1]) < 2)
  399. {
  400. stops.RemoveAt(i);
  401. --i;
  402. }
  403. }
  404. return stops;
  405. }
  406. UIVertex CreateSplitVertex(UIVertex vertex1, UIVertex vertex2, float stop)
  407. {
  408. if (GradientType == Type.Horizontal)
  409. {
  410. float sx = vertex1.position.x - stop;
  411. float dx = vertex1.position.x - vertex2.position.x;
  412. float dy = vertex1.position.y - vertex2.position.y;
  413. float uvx = vertex1.uv0.x - vertex2.uv0.x;
  414. float uvy = vertex1.uv0.y - vertex2.uv0.y;
  415. float ratio = sx / dx;
  416. float splitY = vertex1.position.y - (dy * ratio);
  417. UIVertex splitVertex = new UIVertex();
  418. splitVertex.position = new Vector3(stop, splitY, vertex1.position.z);
  419. splitVertex.normal = vertex1.normal;
  420. splitVertex.uv0 = new Vector2(vertex1.uv0.x - (uvx * ratio), vertex1.uv0.y - (uvy * ratio));
  421. splitVertex.color = Color.white;
  422. return splitVertex;
  423. }
  424. else
  425. {
  426. float sy = vertex1.position.y - stop;
  427. float dy = vertex1.position.y - vertex2.position.y;
  428. float dx = vertex1.position.x - vertex2.position.x;
  429. float uvx = vertex1.uv0.x - vertex2.uv0.x;
  430. float uvy = vertex1.uv0.y - vertex2.uv0.y;
  431. float ratio = sy / dy;
  432. float splitX = vertex1.position.x - (dx * ratio);
  433. UIVertex splitVertex = new UIVertex();
  434. splitVertex.position = new Vector3(splitX, stop, vertex1.position.z);
  435. splitVertex.normal = vertex1.normal;
  436. splitVertex.uv0 = new Vector2(vertex1.uv0.x - (uvx * ratio), vertex1.uv0.y - (uvy * ratio));
  437. splitVertex.color = Color.white;
  438. return splitVertex;
  439. }
  440. }
  441. Color BlendColor(Color colorA, Color colorB)
  442. {
  443. switch (BlendMode)
  444. {
  445. default: return colorB;
  446. case Blend.Add: return colorA + colorB;
  447. case Blend.Multiply: return colorA * colorB;
  448. }
  449. }
  450. public enum Type
  451. {
  452. Horizontal,
  453. Vertical,
  454. Radial,
  455. Diamond
  456. }
  457. public enum Blend
  458. {
  459. Override,
  460. Add,
  461. Multiply
  462. }
  463. }
  464. }