2016年2月3日水曜日

UnityEngine.UI.Imageを継承した, 任意Polygon描画クラス

適当に作る.
Github


一応はWYSIWYGと言えるかも.
要望次第で, Polygon Collider 2Dの編集インターフェイスと同じにするかもしれません.

タイルベースのレンダリングプロセスには, 扇形は細長い三角形を生成しがちなので良くない.
足りなければ考える.


Nexus4で実行.
original UnityEngine.UI.Image
----------------------------------------
Android Unity internal profiler stats:
cpu-player>    min: 22.9   max: 103.2   avg: 76.4
cpu-ogles-drv> min:  0.0   max:  0.0   avg:  0.0
gpu>           min:  0.0   max:  0.0   avg:  0.0
cpu-present>   min:  0.0   max: 24.5   avg:  0.6
frametime>     min: 44.5   max: 103.2   avg: 77.0
batches>       min:   4    max:   4    avg:   4
draw calls>    min:   4    max:   4    avg:   4
tris>          min:  1324  max:  1324  avg:  1324
verts>         min:  2632  max:  2632  avg:  2632
dynamic batching> batched draw calls:   0 batches:   0 tris:     0 verts:     0
static batching>  batched draw calls:   0 batches:   0 tris:     0 verts:     0
player-detail> physx:  5.1 animation:  0.0 culling  0.0 skinning:  0.0 batching:  0.0 render:  5.3 fixed-update-count: 2 .. 5
managed-scripts>  update: 11.0   fixedUpdate:  0.0 coroutines:  0.0 
managed-memory>   used heap: 2334720 allocated heap: 2928640, max number of collections: 0 collection total duration:  0.0


Utility.PolygonalImage2D
約2倍の頂点数で, 描画ピクセル数は80%ぐらい?
----------------------------------------
Android Unity internal profiler stats:
cpu-player>    min: 28.9   max: 81.2   avg: 56.6
cpu-ogles-drv> min:  0.0   max:  0.0   avg:  0.0
gpu>           min:  0.0   max:  0.0   avg:  0.0
cpu-present>   min:  0.0   max:  4.0   avg:  0.2
frametime>     min: 28.9   max: 81.9   avg: 56.7
batches>       min:   4    max:   4    avg:   4
draw calls>    min:   4    max:   4    avg:   4
tris>          min:  3888  max:  3888  avg:  3888
verts>         min:  5200  max:  5200  avg:  5200
dynamic batching> batched draw calls:   0 batches:   0 tris:     0 verts:     0
static batching>  batched draw calls:   0 batches:   0 tris:     0 verts:     0
player-detail> physx:  3.0 animation:  0.0 culling  0.0 skinning:  0.0 batching:  0.0 render:  5.0 fixed-update-count: 1 .. 4
managed-scripts>  update:  9.8   fixedUpdate:  0.0 coroutines:  0.0
managed-memory>   used heap: 2199552 allocated heap: 2928640, max number of collections: 0 collection total duration:  0.0
namespace LUtil
{
    [RequireComponent(typeof(CanvasRenderer)), ExecuteInEditMode]
    public class PolygonalImage2D : UnityEngine.UI.Image
    {
        public Vector2[] points_;

        protected override void Awake()
        {

            base.Awake();
            if(null == points_){
                resetPoints();
            }
        }

        private static Vector2 calcUV(RectTransform rectTrans, Sprite sprite, Vector2 position)
        {
            Rect rect = rectTrans.rect;
            Vector2 uv = new Vector2(position.x - rect.xMin, position.y-rect.yMin);
            if(1.0e-4f < rect.width) {
                uv.x /= rect.width;
            }
            if(1.0e-4f < rect.height) {
                uv.y /= rect.height;
            }

            return uv;
        }

        public void resetPointsSmaller()
        {
            RectTransform rectTrans = GetComponent<RectTransform>();
            Rect rect = rectTrans.rect;

            points_ = new Vector2[4];
            float w = rect.width * 0.05f;
            float h = rect.height * 0.05f;

            points_[0] = new Vector2(rect.xMin+w, rect.yMax-h);
            points_[1] = new Vector2(rect.xMax-w, rect.yMax-h);
            points_[2] = new Vector2(rect.xMax-w, rect.yMin+h);
            points_[3] = new Vector2(rect.xMin+w, rect.yMin+h);
        }

        public void resetPoints()
        {
            RectTransform rectTrans = GetComponent<RectTransform>();
            points_ = new Vector2[4];
            points_[0] = new Vector2(rectTrans.rect.xMin, rectTrans.rect.yMax);
            points_[1] = new Vector2(rectTrans.rect.xMax, rectTrans.rect.yMax);
            points_[2] = new Vector2(rectTrans.rect.xMax, rectTrans.rect.yMin);
            points_[3] = new Vector2(rectTrans.rect.xMin, rectTrans.rect.yMin);
        }

        protected override void OnPopulateMesh(UnityEngine.UI.VertexHelper toFill)
        {
            if(null == points_) {
                resetPoints();
            }

            if(points_.Length < 3) {
                return;
            }
            int numPoints = points_.Length;
            RectTransform rectTrans = GetComponent<RectTransform>();
            UIVertex vertex = new UIVertex();

            Vector2 localOffset;
            localOffset.x = rectTrans.pivot.x * rectTrans.rect.width;
            localOffset.y = rectTrans.pivot.y * rectTrans.rect.height;

            Vector2 isize;
            Vector2 local;
            toFill.Clear();

            if(null == sprite) {
                isize.x = (1.0e-4f < rectTrans.rect.width) ? 1.0f / rectTrans.rect.width : 1.0f;
                isize.y = (1.0e-4f < rectTrans.rect.height) ? 1.0f / rectTrans.rect.height : 1.0f;

                for(int i = 0; i < numPoints; ++i) {
                    vertex.position = points_[i];
                    vertex.color = this.color;

                    local = points_[i] + localOffset;

                    vertex.uv0.x = (local.x) * isize.x;
                    vertex.uv0.y = (local.y) * isize.y;

                    toFill.AddVert(vertex);
                }

                for(int i = 2; i < numPoints; ++i) {
                    toFill.AddTriangle(0, i - 1, i);
                }
                return;
            }

            //Debug.Log("OnPopulateMesh:" + gameObject.name + " " + sprite.packed);

            isize.x = (1 < sprite.texture.width) ? 1.0f / sprite.texture.width : 1.0f;
            isize.y = (1 < sprite.texture.height) ? 1.0f / sprite.texture.height : 1.0f;

            Vector2 localToSprite;
            localToSprite.x = sprite.rect.width/rectTrans.rect.width;
            localToSprite.y = sprite.rect.height/rectTrans.rect.height;

            //Debug.Log("sprite.packed " + sprite.packed);
            //Debug.Log("sprite.textureRectOffset " + sprite.textureRectOffset);
            //Debug.Log("sprite.textureRect " + sprite.textureRect);
            //Debug.Log("sprite.rect " + sprite.rect);
            //Debug.Log("sprite.pivot " + sprite.pivot);

            //Debug.Log("RectTransform.rect " + rectTrans.rect);
            //Debug.Log("RectTransform.pivot " + rectTrans.pivot);

            if(sprite.packed) {
                localOffset.x += sprite.textureRect.xMin;
                localOffset.y += sprite.textureRect.yMin;
                for(int i = 0; i < numPoints; ++i) {
                    vertex.position = points_[i];
                    vertex.color = this.color;

                    local = points_[i] + localOffset;
                    local.x *= localToSprite.x;
                    local.y *= localToSprite.y;

                    vertex.uv0.x = (local.x) * isize.x;
                    vertex.uv0.y = (local.y) * isize.y;

                    toFill.AddVert(vertex);
                }

            } else {
                for(int i = 0; i < numPoints; ++i) {
                    vertex.position = points_[i];
                    vertex.color = this.color;

                    local = points_[i] + localOffset;
                    local.x *= localToSprite.x;
                    local.y *= localToSprite.y;

                    vertex.uv0.x = (local.x) * isize.x;
                    vertex.uv0.y = (local.y) * isize.y;

                    toFill.AddVert(vertex);
                }
            }

            for(int i = 2; i < numPoints; ++i) {
                toFill.AddTriangle(0, i-1, i);
            }
        }

        // Implements ICanvasRaycastFilter
        public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
        {
            if(!RectTransformUtility.RectangleContainsScreenPoint(rectTransform, screenPoint, eventCamera)) {
                return true;
            }

            Vector2 localPoint;
            if(!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out localPoint)){
                return false;
            }

            int i0=points_.Length-1, i1=0;
            bool yflag0 = (localPoint.y <= points_[i0].y);

            bool flag = false;
            for(; i1<points_.Length; i0=i1, ++i1) {
                bool yflag1 = (localPoint.y <= points_[i1].y);
                if(yflag0 != yflag1) {
                    if(((localPoint.x-points_[i0].x)*(points_[i1].y - points_[i0].y) <= (points_[i1].x - points_[i0].x) * (localPoint.y - points_[i0].y)) == yflag1) {
                        flag = !flag;
                    }
                }
                yflag0 = yflag1;
            }//for(; i1

            return flag;
        }
    }
}

0 件のコメント:

コメントを投稿