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