Search code examples
c#unity-game-enginetexturescurvetexture2d

Split Texture using a Curved Line in Unity3D C#


I have a texture that I want to slice into 2 parts, using a Vector2 array. I have all the Vector2 points for the curved line.

Question

How can I slice the texture into 2 parts using the curved line of points.

Alternative Solutions/Questions

How can I 'pixel' fill a Vector2[] shape to create a Texture?


My attempts

1) Generating Vector2 points to create a square, with the top part being the curve edge. Looked promising but when I tried generating a Mesh, the points sorting was incorrect.

2) Dynamically created a Polygon2D Collider - mimicking the bottom part of the sliced texture - this had the same issue as attempt 1, the point ordering. So when convert the Collider to Mesh, it obviously had the same results as attempt


In the picture below:

  1. The red line simulates my Vector2 array
  2. The gray+green square is the texture 1024 x 1024 pixels
  3. The green area is the target area I want

Texture Slice Example


Solution

  • This makes a mesh that is the shape you want (but with jagged edges on top), hopefully that is a step in the right direction. The Vector2 points[] array contains your red line. It should be sorted by the x coordinate, and all the numbers should be between 0 and 1. Needs a mesh filter and a mesh renderer with your texture.

    using UnityEngine;
    
    [RequireComponent(typeof(MeshFilter))]
    [RequireComponent(typeof(MeshRenderer))]
    public class createMesh : MonoBehaviour {
    
        void Start () {
            Vector2[] points = new Vector2[4];
            points [0] = new Vector2 (0, .5f);
            points [1] = new Vector2 (.33f, 1f);
            points [2] = new Vector2 (.66f, .5f);
            points [3] = new Vector2 (1, 1f);
    
            MeshFilter mf = GetComponent<MeshFilter> ();
            Mesh mesh = new Mesh();
    
    
            Vector3[] verticies = new Vector3[points.Length * 2];
            int[] triangles = new int[(points.Length - 1)*6];
            Vector3[] normals = new Vector3[points.Length * 2];
            Vector2[] uv = new Vector2[points.Length * 2];
    
            int vIndex = 0;
            int tIndex = 0;
            int nIndex = 0;
            int uvIndex = 0;
    
            for (int i = 0; i< points.Length; i++) {
                Vector3 topVert = points[i];
                Vector3 bottomVert = topVert;
                bottomVert.y = 0;
                verticies[vIndex++]= bottomVert;
                verticies[vIndex++]=topVert;
    
                //uv
                uv[uvIndex++] = bottomVert;
                uv[uvIndex++] = topVert;
    
                //normals
                normals[nIndex++] = -Vector3.forward;
                normals[nIndex++] = -Vector3.forward;
    
                if (i<points.Length - 1) {
                    //triangles
                    triangles[tIndex++] = (i)*2;
                    triangles[tIndex++] = (i)*2+1;
                    triangles[tIndex++] = (i)*2+2;
                    triangles[tIndex++] = (i)*2+2;
                    triangles[tIndex++] = (i)*2+1;
                    triangles[tIndex++] = (i)*2+3;
    
                }
            }
    
            mesh.vertices = verticies;
            mesh.triangles = triangles;
            mesh.normals = normals;
            mesh.uv = uv;
    
            mf.mesh = mesh;
        }
    }
    

    Bonus: here's a way to do it just with the texture. To use this the bitmap has to be set to Advanced, with read/write enabled in the import settings. This method uses 0 to 1023 (or however large your texture is) for coordinates, and should work for numbers out of that range too.

    using UnityEngine;
    using System.Collections;
    
    
    public class tex2d : MonoBehaviour {
    
        public Vector2[] points;
    
        void Start () {
            MeshRenderer mr;
            Texture2D t2d;
            Texture2D newTex = new Texture2D (1024, 1024);
    
            mr = GetComponent<MeshRenderer> ();
            t2d = mr.material.GetTexture (0) as Texture2D;
            MakeTex (points, t2d, ref newTex, 1024);
            mr.material.SetTexture (0, newTex);
        }
    
    
        void MakeTex(Vector2[] pnts, Texture2D inputTex, ref Texture2D outputTex, int size){
    
            Color bgcolor = new Color (1, 0, 1, 1);
            for (int i=0; i<(pnts.Length-1); i++) {
                Vector2 p1=pnts[i];
                Vector2 p2=pnts[i+1];
    
                //skip points that are out of range
                if ((p1.x <0 && p2.x <0) || (p1.x > size && p2.x>size)) continue;
    
                for (int x =(int)p1.x; x<(int)p2.x; x++) {
                    if (x<0) continue;
                    if (x>=size) break;
    
                    float interpX = (x-p1.x)/(p2.x-p1.x);
                    int interpY = (int) ((p2.y-p1.y)*interpX + p1.y);
    
                    for (int y=0; y<interpY; y++) {
                        outputTex.SetPixel(x,y,inputTex.GetPixel(x,y));
                    }
                    for (int y= interpY; y<size; y++) {
                        outputTex.SetPixel(x,y,bgcolor);
                    }
    
                }
    
            }
            outputTex.Apply ();
        }
    }