| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- using System;
- using System.Collections.Generic;
- namespace UnityEngine.UI
- {
- [AddComponentMenu("UI/PolyImage", 51)]
- [DisallowMultipleComponent]
- public class PolyImage : Image
- {
- #region Image.cs
- /// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top.
- private Vector4 _GetDrawingDimensions(bool shouldPreserveAspect)
- {
- var padding = overrideSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(overrideSprite);
- var size = overrideSprite == null ? Vector2.zero : new Vector2(overrideSprite.rect.width, overrideSprite.rect.height);
- Rect r = GetPixelAdjustedRect();
- // Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));
- int spriteW = Mathf.RoundToInt(size.x);
- int spriteH = Mathf.RoundToInt(size.y);
- var v = new Vector4(
- padding.x / spriteW,
- padding.y / spriteH,
- (spriteW - padding.z) / spriteW,
- (spriteH - padding.w) / spriteH);
- if (shouldPreserveAspect && size.sqrMagnitude > 0.0f)
- {
- var spriteRatio = size.x / size.y;
- var rectRatio = r.width / r.height;
- if (spriteRatio > rectRatio)
- {
- var oldHeight = r.height;
- r.height = r.width * (1.0f / spriteRatio);
- r.y += (oldHeight - r.height) * rectTransform.pivot.y;
- }
- else
- {
- var oldWidth = r.width;
- r.width = r.height * spriteRatio;
- r.x += (oldWidth - r.width) * rectTransform.pivot.x;
- }
- }
- v = new Vector4(
- r.x + r.width * v.x,
- r.y + r.height * v.y,
- r.x + r.width * v.z,
- r.y + r.height * v.w
- );
- return v;
- }
- Vector4 _GetAdjustedBorders(Vector4 border, Rect rect)
- {
- for (int axis = 0; axis <= 1; axis++)
- {
- // If the rect is smaller than the combined borders, then there's not room for the borders at their normal size.
- // In order to avoid artefacts with overlapping borders, we scale the borders down to fit.
- float combinedBorders = border[axis] + border[axis + 2];
- if (rect.size[axis] < combinedBorders && combinedBorders != 0)
- {
- float borderScaleRatio = rect.size[axis] / combinedBorders;
- border[axis] *= borderScaleRatio;
- border[axis + 2] *= borderScaleRatio;
- }
- }
- return border;
- }
- #endregion
- #if UNITY_5_0 || UNITY_5_1
- static VertexHelper toFill = new VertexHelper();
- protected override void OnFillVBO(List<UIVertex> vbo)
- #elif UNITY_5_2
- protected override void OnPopulateMesh(Mesh mesh)
- #else
- protected override void OnPopulateMesh(VertexHelper toFill)
- #endif
- {
- // Tiled & Filled not supported yet
- if (overrideSprite == null || type == Type.Tiled || type == Type.Filled)
- {
- #if UNITY_5_0 || UNITY_5_1
- base.OnFillVBO(vbo);
- #elif UNITY_5_2
- base.OnPopulateMesh(mesh);
- #else
- base.OnPopulateMesh(toFill);
- #endif
- return;
- }
-
- toFill.Clear();
- switch (type)
- {
- case Type.Simple:
- GenerateSimplePolySprite(toFill, preserveAspect);
- break;
- case Type.Sliced:
- GenerateSlicedPolySprite(toFill);
- break;
- }
- #if UNITY_5_0 || UNITY_5_1
- toFill.FillVBO(vbo);
- #elif UNITY_5_2
- toFill.FillMesh(mesh);
- #endif
- }
- void GenerateSimplePolySprite(VertexHelper vh, bool lPreserveAspect)
- {
- Vector4 v = _GetDrawingDimensions(lPreserveAspect);
- Vector4 uv = Sprites.DataUtility.GetOuterUV(overrideSprite);
- Color color32 = color;
- Vector2[] uvs = overrideSprite.uv;
- float invU = 1 / (uv.z - uv.x);
- float invV = 1 / (uv.w - uv.y);
- for (int i = 0; i < uvs.Length; i++)
- {
- float u2 = invU * (uvs[i].x - uv.x);
- float v2 = invV * (uvs[i].y - uv.y);
- float x = u2 * (v.z - v.x) + v.x;
- float y = v2 * (v.w - v.y) + v.y;
- vh.AddVert(new Vector2(x, y), color32, uvs[i]);
- }
- ushort[] triangles = overrideSprite.triangles;
- for (int i = 0; i < triangles.Length; i += 3)
- {
- vh.AddTriangle(triangles[i], triangles[i + 1], triangles[i + 2]);
- }
- }
- public static float Cross(Vector2 lhs, Vector2 rhs)
- {
- return lhs.x * rhs.y - lhs.y * rhs.x;
- }
- // idea comes from RadialCut
- private static List<ushort> LineCut(
- List<Vector2> uvs, List<ushort> triangles,
- Vector2 start, float angle, Func<Vector2, bool> predict = null)
- {
- List<ushort> splitTriangles = new List<ushort>();
- List<ushort> splitTriCache1 = new List<ushort>();
- List<ushort> splitTriCache2 = new List<ushort>();
- var offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
-
- for (int i = 0; i < triangles.Count; i += 3)
- {
- splitTriCache1.Clear();
- splitTriCache2.Clear();
- for (int j = 0; j < 3; j++)
- {
- ushort tri1 = triangles[i + j], tri2 = triangles[i + (j + 1) % 3];
- Vector2 uv1 = uvs[tri1], uv2 = uvs[tri2];
- float sign1 = Cross(offset, uv1 - start), sign2 = Cross(offset, uv2 - start);
- if (sign1 <= 0)
- splitTriCache1.Add(tri1);
- if (sign1 >= 0)
- splitTriCache2.Add(tri1);
- if (sign1 * sign2 < 0)
- {
- // Line Intersects!
- var diff = uv2 - uv1;
- float t1 = -sign1 / Cross(offset, uv2 - uv1);
- var p = uv1 + diff * t1;
- ushort idx = (ushort)uvs.Count;
- uvs.Add(p);
- splitTriCache1.Add(idx);
- splitTriCache2.Add(idx);
- }
- }
- if (splitTriCache1.Count >= 3)
- {
- for (int j = 2; j < splitTriCache1.Count; j++)
- {
- if(predict != null)
- {
- Vector2 center = (uvs[splitTriCache1[0]] + uvs[splitTriCache1[j - 1]] + uvs[splitTriCache1[j]]) / 3;
- if (!predict(center)) continue;
- }
- splitTriangles.Add(splitTriCache1[0]);
- splitTriangles.Add(splitTriCache1[j - 1]);
- splitTriangles.Add(splitTriCache1[j]);
- }
- }
- if (splitTriCache2.Count >= 3)
- {
- for (int j = 2; j < splitTriCache2.Count; j++)
- {
- if (predict != null)
- {
- Vector2 center = (uvs[splitTriCache2[0]] + uvs[splitTriCache2[j - 1]] + uvs[splitTriCache2[j]]) / 3;
- if (!predict(center)) continue;
- }
- splitTriangles.Add(splitTriCache2[0]);
- splitTriangles.Add(splitTriCache2[j - 1]);
- splitTriangles.Add(splitTriCache2[j]);
- }
- }
- }
- return splitTriangles;
- }
- static readonly Vector2[] s_VertScratch = new Vector2[4];
- static readonly Vector2[] s_UVScratch = new Vector2[4];
-
- private static int XSlot(float x)
- {
- for (int idx = 0; idx < 3; idx++)
- {
- if (s_UVScratch[idx].x < s_UVScratch[idx + 1].x && x <= s_UVScratch[idx + 1].x)
- return idx;
- }
- return 2;
- }
- private static int YSlot(float y)
- {
- for (int idx = 0; idx < 3; idx++)
- {
- if (s_UVScratch[idx].y < s_UVScratch[idx + 1].y && y <= s_UVScratch[idx + 1].y)
- return idx;
- }
- return 2;
- }
-
- private void GenerateSlicedPolySprite(VertexHelper toFill)
- {
- if (!hasBorder)
- {
- GenerateSimplePolySprite(toFill, false);
- return;
- }
- Vector4 outer = Sprites.DataUtility.GetOuterUV(overrideSprite);
- Vector4 inner = Sprites.DataUtility.GetInnerUV(overrideSprite);
- Vector4 padding = Sprites.DataUtility.GetPadding(overrideSprite);
- Vector4 border = overrideSprite.border;
- Rect rect = GetPixelAdjustedRect();
- border = _GetAdjustedBorders(border / pixelsPerUnit, rect);
- padding = padding / pixelsPerUnit;
- s_VertScratch[0] = new Vector2(padding.x, padding.y);
- s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
- s_VertScratch[1].x = border.x;
- s_VertScratch[1].y = border.y;
- s_VertScratch[2].x = rect.width - border.z;
- s_VertScratch[2].y = rect.height - border.w;
- for (int i = 0; i < 4; ++i)
- {
- s_VertScratch[i].x += rect.x;
- s_VertScratch[i].y += rect.y;
- }
- s_UVScratch[0] = new Vector2(outer.x, outer.y);
- s_UVScratch[1] = new Vector2(inner.x, inner.y);
- s_UVScratch[2] = new Vector2(inner.z, inner.w);
- s_UVScratch[3] = new Vector2(outer.z, outer.w);
-
- List<Vector2> uvs = new List<Vector2>(overrideSprite.uv);
- List<ushort> triangles = new List<ushort>(overrideSprite.triangles);
- var splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.x, inner.y), Mathf.PI / 2);
- splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.z, inner.w), 0);
- splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.x, inner.y), Mathf.PI / 2);
- splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.z, inner.w), 0);
- splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.x, inner.y), Mathf.PI / 2);
- splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.z, inner.w), 0);
- splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.x, inner.y), Mathf.PI / 2);
- splitedTriangles = LineCut(uvs, triangles, new Vector2(inner.z, inner.w), 0);
- for (int i = 0; i < uvs.Count; i++)
- {
- int x = XSlot(uvs[i].x);
- int y = YSlot(uvs[i].y);
- float kX = (uvs[i].x - s_UVScratch[x].x) / (s_UVScratch[x + 1].x - s_UVScratch[x].x);
- float kY = (uvs[i].y - s_UVScratch[y].y) / (s_UVScratch[y + 1].y - s_UVScratch[y].y);
- Vector2 pos = new Vector2(kX * (s_VertScratch[x + 1].x - s_VertScratch[x].x) + s_VertScratch[x].x,
- kY * (s_VertScratch[y + 1].y - s_VertScratch[y].y) + s_VertScratch[y].y);
- toFill.AddVert(pos, color, uvs[i]);
- }
- for (int i = 0; i < splitedTriangles.Count; i += 3)
- {
- int x = XSlot(uvs[splitedTriangles[i + 0]].x);
- int y = YSlot(uvs[splitedTriangles[i + 0]].y);
- if (x == 1 && y == 1 && !fillCenter) continue;
- toFill.AddTriangle(splitedTriangles[i + 0], splitedTriangles[i + 1], splitedTriangles[i + 2]);
- }
- }
- }
- }
|