123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
-
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Sprites;
- using UnityEngine.UI;
- /* Author: Josh H.
- * Procedural UI Image
- * assetstore.joshh@gmail.com for feedback or questions
- */
- namespace UnityEngine.UI {
- [ExecuteInEditMode]
- [AddComponentMenu("UI/Procedural Image")]
- public class ProceduralImage : ImageProxy {
- [SerializeField]private float borderWidth;
- private ProceduralImageModifier modifier;
- public Material materialInstance;
- public Material materialInstanceClip;
- [SerializeField]private float falloffDistance = 1;
- public bool needClipping = false;
- public float BorderWidth {
- get {
- return borderWidth;
- }
- set {
- borderWidth = value;
- this.SetMaterialDirty();
- }
- }
- public float FalloffDistance {
- get {
- return falloffDistance;
- }
- set {
- falloffDistance = value;
- this.SetMaterialDirty();
- }
- }
- protected ProceduralImageModifier Modifier {
- get {
- if (modifier == null) {
- //try to get the modifier on the object.
- modifier = this.GetComponent<ProceduralImageModifier>();
- //if we did not find any modifier
- if(modifier == null){
- //Add free modifier
- ModifierType = typeof(FreeModifier);
- }
- }
- return modifier;
- }
- set{
- modifier = value;
- }
- }
- /// <summary>
- /// Gets or sets the type of the modifier. Adds a modifier of that type.
- /// </summary>
- /// <value>The type of the modifier.</value>
- public System.Type ModifierType {
- get {
- return Modifier.GetType();
- }
- set {
- if(this.GetComponent<ProceduralImageModifier>()!=null){
- Destroy(this.GetComponent<ProceduralImageModifier>());
- }
- this.gameObject.AddComponent(value);
- Modifier = this.GetComponent<ProceduralImageModifier>();
- this.SetAllDirty();
- }
- }
- override protected void OnEnable()
- {
- base.OnEnable ();
- // ScenceMain.MainUpdateEvent += MainUpdate;
- this.Init ();
- }
- /// <summary>
- /// Initializes this instance.
- /// </summary>
- public void Init (){
- // materialInstance = Image.defaultGraphicMaterial;
- // materialInstanceClip = Image.defaultETC1GraphicMaterial;
- // this.material = materialInstance;
- }
- protected override void OnDisable()
- {
- base.OnDisable();
- // ScenceMain.MainUpdateEvent -= MainUpdate;
- }
- public void Update(){
- if (needClipping) {
- // this.material = materialInstanceClip;
- } else {
- //this.material = materialInstance;
- }
- this.UpdateMaterial ();
- }
- /// <summary>
- /// Prevents radius to get bigger than rect size
- /// </summary>
- /// <returns>The fixed radius.</returns>
- /// <param name="vec">border-radius as Vector4 (starting upper-left, clockwise)</param>
- private Vector4 FixRadius(Vector4 vec){
- Rect r = this.rectTransform.rect;
- vec = new Vector4 (Mathf.Max(vec.x,0),Mathf.Max(vec.y,0),Mathf.Max(vec.z,0),Mathf.Max(vec.w,0));
- //float maxRadiusSums = Mathf.Max (vec.x,vec.z) + Mathf.Max (vec.y,vec.w);
- float scaleFactor = Mathf.Min(r.width/(vec.x+vec.y),r.width/(vec.z+vec.w),r.height/(vec.x+vec.w),r.height/(vec.z+vec.y),1);
- return vec*scaleFactor;
- }
- protected override void OnPopulateMesh(VertexHelper toFill)
- {
- //note: Sliced and Tiled have no effect to this currently.
- if (overrideSprite == null)
- {
- base.OnPopulateMesh(toFill);
- return;
- }
-
- switch (type)
- {
- case Type.Simple:
- GenerateSimpleSprite(toFill);
- break;
- case Type.Sliced:
- GenerateSimpleSprite(toFill);
- break;
- case Type.Tiled:
- GenerateSimpleSprite(toFill);
- break;
- case Type.Filled:
- base.OnPopulateMesh(toFill);
- break;
- }
- }
- #if UNITY_EDITOR
- protected override void Reset (){
- base.Reset ();
- OnEnable ();
- }
- #endif
- private Vector4 GetDrawingDimensions(bool shouldPreserveAspect)
- {
- var padding = overrideSprite == null ? Vector4.zero : DataUtility.GetPadding(overrideSprite);
- Rect r = GetPixelAdjustedRect();
- var size = overrideSprite == null ? new Vector2(r.width, r.height) : new Vector2(overrideSprite.rect.width, overrideSprite.rect.height);
- //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);
- 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;
- }
- }
- var v = new Vector4(
- padding.x / spriteW,
- padding.y / spriteH,
- (spriteW - padding.z) / spriteW,
- (spriteH - padding.w) / spriteH);
- 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;
- }
- //每个角最大的三角形数,一般5-8个就有不错的圆角效果,设置Max防止不必要的性能浪费
- const int MaxTriangleNum = 20;
- const int MinTriangleNum = 1;
- public float Radius=10;
- //使用几个三角形去填充每个角的四分之一圆
- [Range(MinTriangleNum, MaxTriangleNum)]
- public int TriangleNum=6;
- /// <summary>
- /// Generates the Verticies needed.
- /// </summary>
- /// <param name="vh">vertex helper</param>
- void GenerateSimpleSprite(VertexHelper vh){
- Vector4 v = GetDrawingDimensions(false);
- Vector4 uv = overrideSprite != null ? DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
- var color32 = color;
- vh.Clear();
- //对radius的值做限制,必须在0-较小的边的1/2的范围内
- float radius = Radius;
- if (radius > (v.z - v.x) / 2) radius = (v.z - v.x) / 2;
- if (radius > (v.w - v.y) / 2) radius = (v.w - v.y) / 2;
- if (radius < 0) radius = 0;
- //计算出uv中对应的半径值坐标轴的半径
- float uvRadiusX = radius / (v.z - v.x);
- float uvRadiusY = radius / (v.w - v.y);
- //0,1
- vh.AddVert(new Vector3(v.x, v.w - radius), color32, new Vector2(uv.x, uv.w - uvRadiusY));
- vh.AddVert(new Vector3(v.x, v.y + radius), color32, new Vector2(uv.x, uv.y + uvRadiusY));
- //2,3,4,5
- vh.AddVert(new Vector3(v.x + radius, v.w), color32, new Vector2(uv.x + uvRadiusX, uv.w));
- vh.AddVert(new Vector3(v.x + radius, v.w - radius), color32, new Vector2(uv.x + uvRadiusX, uv.w - uvRadiusY));
- vh.AddVert(new Vector3(v.x + radius, v.y + radius), color32, new Vector2(uv.x + uvRadiusX, uv.y + uvRadiusY));
- vh.AddVert(new Vector3(v.x + radius, v.y), color32, new Vector2(uv.x + uvRadiusX, uv.y));
- //6,7,8,9
- vh.AddVert(new Vector3(v.z - radius, v.w), color32, new Vector2(uv.z - uvRadiusX, uv.w));
- vh.AddVert(new Vector3(v.z - radius, v.w - radius), color32, new Vector2(uv.z - uvRadiusX, uv.w - uvRadiusY));
- vh.AddVert(new Vector3(v.z - radius, v.y + radius), color32, new Vector2(uv.z - uvRadiusX, uv.y + uvRadiusY));
- vh.AddVert(new Vector3(v.z - radius, v.y), color32, new Vector2(uv.z - uvRadiusX, uv.y));
- //10,11
- vh.AddVert(new Vector3(v.z, v.w - radius), color32, new Vector2(uv.z, uv.w - uvRadiusY));
- vh.AddVert(new Vector3(v.z, v.y + radius), color32, new Vector2(uv.z, uv.y + uvRadiusY));
- //左边的矩形
- vh.AddTriangle(1, 0, 3);
- vh.AddTriangle(1, 4, 3);
- //中间的矩形
- vh.AddTriangle(5, 2, 6);
- vh.AddTriangle(5, 9, 6);
- //右边的矩形
- vh.AddTriangle(8, 7, 10);
- vh.AddTriangle(8, 11, 10);
- //开始构造四个角
- List<Vector2> vCenterList = new List<Vector2>();
- List<Vector2> uvCenterList = new List<Vector2>();
- List<int> vCenterVertList = new List<int>();
- //右上角的圆心
- vCenterList.Add(new Vector2(v.z - radius, v.w - radius));
- uvCenterList.Add(new Vector2(uv.z - uvRadiusX, uv.w - uvRadiusY));
- vCenterVertList.Add(7);
- //左上角的圆心
- vCenterList.Add(new Vector2(v.x + radius, v.w - radius));
- uvCenterList.Add(new Vector2(uv.x + uvRadiusX, uv.w - uvRadiusY));
- vCenterVertList.Add(3);
- //左下角的圆心
- vCenterList.Add(new Vector2(v.x + radius, v.y + radius));
- uvCenterList.Add(new Vector2(uv.x + uvRadiusX, uv.y + uvRadiusY));
- vCenterVertList.Add(4);
- //右下角的圆心
- vCenterList.Add(new Vector2(v.z - radius, v.y + radius));
- uvCenterList.Add(new Vector2(uv.z - uvRadiusX, uv.y + uvRadiusY));
- vCenterVertList.Add(8);
- //每个三角形的顶角
- float degreeDelta = (float)(Mathf.PI / 2 / TriangleNum);
- //当前的角度
- float curDegree = 0;
- for (int i = 0; i < vCenterVertList.Count; i++)
- {
- int preVertNum = vh.currentVertCount;
- for (int j = 0; j <= TriangleNum; j++)
- {
- float cosA = Mathf.Cos(curDegree);
- float sinA = Mathf.Sin(curDegree);
- Vector3 vPosition = new Vector3(vCenterList[i].x + cosA * radius, vCenterList[i].y + sinA * radius);
- Vector3 uvPosition = new Vector2(uvCenterList[i].x + cosA * uvRadiusX, uvCenterList[i].y + sinA * uvRadiusY);
- vh.AddVert(vPosition, color32, uvPosition);
- curDegree += degreeDelta;
- }
- curDegree -= degreeDelta;
- for (int j = 0; j <= TriangleNum - 1; j++)
- {
- vh.AddTriangle(vCenterVertList[i], preVertNum + j + 1, preVertNum + j);
- }
- }
- }
- /// <summary>
- /// Sets the material values of shader.
- /// Implementation of IMaterialModifier
- /// </summary>
- public override Material GetModifiedMaterial (Material baseMaterial){
- return base.GetModifiedMaterial(baseMaterial);
- Rect rect = this.GetComponent<RectTransform> ().rect;
- //get world-space corners of rect
- Vector3[] corners = new Vector3[4];
- rectTransform.GetWorldCorners (corners);
- float pixelSize = Vector3.Distance (corners [1], corners [2]) / rect.width;
- pixelSize = pixelSize/falloffDistance;
- Vector4 radius = FixRadius (Modifier.CalculateRadius (rect));
- Material m = MaterialHelper.SetMaterialValues (new ProceduralImageMaterialInfo(rect.width+falloffDistance,rect.height+falloffDistance,1,radius,Mathf.Max(borderWidth,0)),baseMaterial);
- return base.GetModifiedMaterial (m);
- }
- }
- }
|