Mesh网格自定义编辑自定义任意形状【插件编写】

参考资料

http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf

预览图

整体效果预览:

1

插件菜单栏:

5

 

mesh编辑:

2

vine编辑

3

插件脚本预览:

4

 

插件下载地址

链接:http://pan.baidu.com/s/1jGGKJ4M 密码:8pwl

CreationMesh.cs

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class CreationMesh : MonoBehaviour
{/// <summary>
    /// 藤显示网格顶点坐标
    /// </summary>
    //[HideInInspector]
    public Vector3[] MeshVertices;
    /// <summary>
    /// 藤UV纹理坐标
    /// </summary>
    //[HideInInspector]
    public Vector2[] MeshUV;
    /// <summary>
    /// 藤网格三角形索引
    /// </summary>
    [HideInInspector]
    public int[] MeshTriangles;
	void Start() 
    {
	
	}
	
	void Update ()
    {
	
	}
}

FoundMeshEntity.cs

using UnityEditor;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[CustomEditor(typeof(CreationMesh))]
public class FoundMeshEntity : Editor 
{
    /// <summary>
    /// 网格对象
    /// </summary>
    private CreationMesh meshs;
    /// <summary>
    /// 藤显示网格顶点坐标
    /// </summary>
    private Vector3[] MeshVertices;
    /// <summary>
    /// 藤UV纹理坐标
    /// </summary>
    private Vector2[] MeshUV;
    /// <summary>
    /// 藤网格三角形索引
    /// </summary>
    private int[] MeshTriangles;
    /// <summary>
    /// 藤显示网格顶点坐标
    /// </summary>
    private List<Vector3> MeshPath;
    /// <summary>
    /// 创建的网格
    /// </summary>
    private MeshFilter mesh;
    /// <summary>
    /// 控制开关
    /// </summary>
    private bool acceptInput;
    private bool isEnable;
    /// <summary>
    /// 当选中的游戏对象含有 CreationVine类的时候调用该方法初始化
    /// </summary>
    public void OnEnable()
    {
        isEnable = true;
        meshs = (CreationMesh)target;
        MeshPath = new List<Vector3>();
        mesh = meshs.GetComponent<MeshFilter>();
        Vector3 ver = meshs.transform.position;
        if (meshs.MeshVertices == null || meshs.MeshVertices.Length == 0)
        {
            MeshPath.Add(new Vector3(-1 + ver.x, -1 + ver.y, 0));
            MeshPath.Add(new Vector3(1 + ver.x, -1 + ver.y, 0));
            MeshPath.Add(new Vector3(1 + ver.x, 1 + ver.y, 0));
            MeshPath.Add(new Vector3(-1 + ver.x, 1 + ver.y, 0));
            
        }
        else
        {
            MeshVertices = meshs.MeshVertices;
            for(int x = 0; x < MeshVertices.Length; x++)
            {
                MeshVertices[x] = MeshVertices[x] + ver;
                MeshPath.Add(MeshVertices[x]);
            }
        }
    }


    [MenuItem("Plug/AddMesh")]
    public static void AddMesh()
    {
        GameObject obj = new GameObject("Mesh");
        obj.AddComponent<CreationMesh>();
        //obj.GetComponent<MeshRenderer>().materials[0];
    }

    bool showPosition;
    public override void OnInspectorGUI()
    {
        GUI.changed = false;
        showPosition = EditorGUILayout.Foldout(showPosition, "MeshPoint");
        if (showPosition)
        {
            for(int x = 0; x < MeshPath.Count;x++)
            {
                EditorGUILayout.Vector3Field("P" + x ,MeshPath[x]);
            }
        }
        EditorGUILayout.Space();
        EditorGUILayout.Space();
        if (acceptInput)
        {
            GUI.backgroundColor = Color.red;
            if (GUILayout.Button("关 闭 Mesh 编 辑"))
            {
                isEnable = false;
                //line.enabled = false;
                ///关闭插件
                meshs.MeshVertices = GetMeshVertices(MeshPath);
                acceptInput = false;
                SceneView.RepaintAll();
            }

        }
        else
        {
            GUI.backgroundColor = Color.green;
            if (GUILayout.Button("开 始 Mesh 编 辑"))
            {
                if (!isEnable)
                {
                    OnEnable();
                }
                acceptInput = true;
                SceneView.RepaintAll();
            }
        }
    }
    Vector3 tp;
    public void OnSceneGUI()
    {
        if (!acceptInput)
            return;

        DrawHandleGUI(MeshPath);
        #region 创建藤节点坐标显示轴
        for (int i = 0; i < MeshPath.Count; i++)
        {
            MeshPath[i] = Handles.PositionHandle(MeshPath[i], Quaternion.identity);
            tp = MeshPath[i];
            if (tp != MeshPath[i])
            {
                Vector3 p = MeshPath[i];
                p.z = MeshPath[i].z;
                MeshPath[i] = p;

            }
        }
        #endregion

        MeshVertices = GetMeshVertices(MeshPath);
        MeshTriangles = Triangulate.Points(MeshVertices);
        Mesh meshS = new Mesh();
        meshS.name = "Mesh2";
        meshS.vertices = MeshVertices;
        meshS.uv = GetMeshUV(MeshPath);
        meshS.triangles = MeshTriangles;
        mesh.mesh = meshS;

    }

    private Vector2[] GetMeshUV(List<Vector3> _Points)
    {
        Vector2[] ver = new Vector2[_Points.Count];
        for (int x = 0; x < ver.Length; x++)
        {
            ver[x].x = _Points[x].x;
            ver[x].y = _Points[x].y;
        }
        return ver;
    }
    private Vector3[] GetMeshVertices(List<Vector3> _Points)
    {
        Vector3[] ver = new Vector3[_Points.Count];
        for (int x = 0; x < ver.Length; x++)
        {
            ver[x] = _Points[x] - meshs.transform.position;
        }
        return ver;
    }

    /// <summary>
    /// 显示藤转折节点
    /// </summary>
    /// <param name="points">藤节点</param>
    public void DrawHandleGUI(List<Vector3> points)
    {
        if (points == null || points.Count < 1)
            return;
        Handles.BeginGUI();
        for (int i = 0; i < points.Count; i++)
        {
            Vector2 p = HandleUtility.WorldToGUIPoint(points[i]);

            GUI.backgroundColor = Color.red;
            if (GUI.Button(new Rect(p.x - 30, p.y - 30, 25, 25), "x"))
                DeletePoint(i);

            GUI.backgroundColor = Color.green;
            if (GUI.Button(new Rect(p.x + 10, p.y - 30, 25, 25), "+"))
                AddPoint(i);

            GUI.Label(new Rect(p.x, p.y - 50, 200, 25), "Point: " + i.ToString());
        }
        GUI.backgroundColor = Color.white;
        Handles.EndGUI();
    }
    /// <summary>
    /// 删除该节点
    /// </summary>
    /// <param name="index">节点下标</param>
    public void DeletePoint(int index)
    {
        Debug.Log("Delete Point" + index);
        MeshPath.RemoveAt(index);
        SceneView.RepaintAll();//重新绘制所有
    }

    /// <summary>
    /// 添加新的节点
    /// </summary>
    /// <param name="index"></param>
    public void AddPoint(int index)
    {
        if (index == MeshPath.Count - 1)
        {
            MeshPath.Add(new Vector3(MeshPath[index].x + 1, MeshPath[index].y, MeshPath[index].z));
        }
        else
        {
            Vector3 pon = new Vector3(MeshPath[index].x + 0.5f, MeshPath[index].y, MeshPath[index].z);
            Vector3 outs;
            MeshPath.Add(MeshPath[MeshPath.Count - 1]);
            for (int x = index + 1; x < MeshPath.Count; x++)
            {
                outs = MeshPath[x];
                MeshPath[x] = pon;
                pon = outs;
            }
        }
        SceneView.RepaintAll();//重新绘制所有
    }
}

 

CreationVineEntity.cs

using UnityEditor;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[CustomEditor(typeof(CreationVine))]
public class CreationVineEntity : Editor
{
    #region 藤网格数据
    /// <summary>
    /// 藤生成方法
    /// </summary>
    private CreationVine vine;
    /// <summary>
    /// 藤显示网格顶点坐标
    /// </summary>
    private Vector3[] VineVertices;
    /// <summary>
    /// 藤UV纹理坐标
    /// </summary>
    private Vector2[] VineUV;
    /// <summary>
    /// 藤网格三角形索引
    /// </summary>
    private int[] VineTriangles;
    /// <summary>
    /// 创建的网格
    /// </summary>
    private MeshFilter mesh;
    /// <summary>
    /// 存储移动的骨骼点
    /// </summary>
    private List<Vector3> MoveVertexs;
    /// <summary>
    /// 藤的宽度
    /// </summary>
    private float VineWidth = 1.5f;
    /// <summary>
    /// 藤的宽度的最小值
    /// </summary>
    private const float MIN_VINE_WIDTH = .1f;
    /// <summary>
    /// 藤的宽度的最大值
    /// </summary>
    private const float MAX_VINE_WIDTH = 1f;
    /// <summary>
    /// 是否开始显示
    /// </summary>
    private bool acceptInput = false;
    #endregion

    /// <summary>
    /// 当选中的游戏对象含有 CreationVine类的时候调用该方法初始化
    /// </summary>
    public void OnEnable()
    {
        vine = (CreationVine)target;

        mesh = vine.GetComponent<MeshFilter>();
        Vector3 ver = mesh.transform.position;
        if (vine.MoveVertexs == null || vine.MoveVertexs.Count == 0)
        {
            MoveVertexs = new List<Vector3>();
            MoveVertexs.Add(new Vector3(-1 + ver.x, ver.y, 0));
            MoveVertexs.Add(new Vector3(1 + ver.x, ver.y, 0));
        }
        else
        {
            MoveVertexs = vine.MoveVertexs;
            for (int x = 0; x < MoveVertexs.Count; x++)
            {
                MoveVertexs[x] = MoveVertexs[x] + ver;
                MoveVertexs.Add(MoveVertexs[x]);
            }
        }
    }
    [MenuItem("Plug/AddVine")]
    public static void AddMesh()
    {
        GameObject obj = new GameObject("Vine");
        obj.AddComponent<CreationVine>();
        //obj.GetComponent<MeshRenderer>().materials[0];
    }

    public override void OnInspectorGUI()
    {
        GUI.changed = false;
        vine.VineWidth = EditorGUILayout.Slider("Vine Width",vine.VineWidth,MIN_VINE_WIDTH,MAX_VINE_WIDTH);

        EditorGUILayout.Space();
        EditorGUILayout.Space();
        if (acceptInput)
        {
            GUI.backgroundColor = Color.red;
            if (GUILayout.Button("关 闭 Vine 编 辑"))
            {
                //line.enabled = false;
                ///关闭插件
                acceptInput = false;
                SceneView.RepaintAll();
            }

        }
        else
        {
            GUI.backgroundColor = Color.green;
            if (GUILayout.Button("开 始 Vine 编 辑"))
            {
                //line.enabled = true;
                acceptInput = true;
                SceneView.RepaintAll();
            }
        }
    }
    Vector3 tp;
    public void OnSceneGUI()
    {
        if (!acceptInput)
            return;

        DrawHandleGUI(MoveVertexs);

        #region 创建藤节点坐标显示轴
        for (int i = 0; i < MoveVertexs.Count; i++)
        {
            tp = MoveVertexs[i];
            MoveVertexs[i] = Handles.PositionHandle(MoveVertexs[i], Quaternion.identity);

            if (tp != MoveVertexs[i])
            {
                Vector3 p = MoveVertexs[i];
                p.z = MoveVertexs[i].z;
                MoveVertexs[i] = p;
                
            }
        }
        #endregion


        VineVertices = WireToMesh.GetMeshVertexs(MoveVertexs, vine.transform.position,2.56f);
        VineTriangles = WireToMesh.GetMeshTriangles(VineVertices);
        VineUV = WireToMesh.CountMeshUV(VineVertices,vine.VineWidth);
        Mesh meshs = new Mesh();
        meshs.name = "Mesh";
        meshs.vertices = VineVertices;
        meshs.uv = VineUV;
        meshs.triangles = VineTriangles;
        mesh.mesh = meshs;

    }

    /// <summary>
    /// 显示藤转折节点
    /// </summary>
    /// <param name="points">藤节点</param>
    public void DrawHandleGUI(List<Vector3> points)
    {
        if (points == null || points.Count < 1)
            return;
        Handles.BeginGUI();
        for (int i = 0; i < points.Count; i++)
        {
            Vector2 p = HandleUtility.WorldToGUIPoint(points[i]);

            GUI.backgroundColor = Color.red;
            if (GUI.Button(new Rect(p.x - 30, p.y - 30, 25, 25), "x"))
                DeletePoint(i);

            GUI.backgroundColor = Color.green;
            if (GUI.Button(new Rect(p.x + 10, p.y - 30, 25, 25), "+"))
                AddPoint(i);

            GUI.Label(new Rect(p.x, p.y - 50, 200, 25), "Point: " + i.ToString());
        }
        GUI.backgroundColor = Color.white;
        Handles.EndGUI();
    }
    /// <summary>
    /// 删除该节点
    /// </summary>
    /// <param name="index">节点下标</param>
    public void DeletePoint(int index)
    {
        Debug.Log("Delete Point" + index);
        MoveVertexs.RemoveAt(index);
        SceneView.RepaintAll();//重新绘制所有
    }

    /// <summary>
    /// 添加新的节点
    /// </summary>
    /// <param name="index"></param>
    public void AddPoint(int index)
    {
        if (index == MoveVertexs.Count - 1)
        {
            MoveVertexs.Add(new Vector3(MoveVertexs[index].x + 1,MoveVertexs[index].y,MoveVertexs[index].z));
        }
        else
        {
            Vector3 pon = new Vector3(MoveVertexs[index].x + 0.5f,MoveVertexs[index].y,MoveVertexs[index].z);
            Vector3 outs;
            MoveVertexs.Add(MoveVertexs[MoveVertexs.Count - 1]);
            for (int x = index + 1; x < MoveVertexs.Count; x++)
            {
                outs = MoveVertexs[x];
                MoveVertexs[x] = pon;
                pon = outs;
            }
        }
        SceneView.RepaintAll();//重新绘制所有
    }
}

TerrainManager.cs

using UnityEngine;
using System.Collections;

public class TerrainManager : MonoBehaviour
{
    //材质和高度图
    public Material diffuseMap;
    public Texture2D heightMap;
    //顶点、uv、索引信息
    private Vector3[] vertives;
    private Vector2[] uvs;
    private int[] triangles;

    //生成信息
    private Vector2 size;//长宽
    private float minHeight = -10;
    private float maxHeight = 10;
    private Vector2 segment;
    private float unitH;

    //面片mesh
    private GameObject terrain;

    // Use this for initialization
    void Start()
    {
        //默认生成一个地形,如果不喜欢,注销掉然后用参数生成
        SetTerrain();
    }


    /// <summary>
    /// 生成默认地形
    /// </summary>
    public void SetTerrain()
    {
        SetTerrain(1000, 1000, 500, 500, -10, 10);
    }

    /// <summary>
    /// 通过参数生成地形
    /// </summary>
    /// <param name="width">地形宽度</param>
    /// <param name="height">地形长度</param>
    /// <param name="segmentX">宽度的段数</param>
    /// <param name="segmentY">长度的段数</param>
    /// <param name="min">最低高度</param>
    /// <param name="max">最高高度</param>
    public void SetTerrain(float width, float height, uint segmentX, uint segmentY, int min, int max)
    {
        Init(width, height, segmentX, segmentY, min, max);
        GetVertives();
        DrawMesh();
    }

    /// <summary>
    /// 初始化计算某些值
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="segmentX"></param>
    /// <param name="segmentY"></param>
    /// <param name="min"></param>
    /// <param name="max"></param>
    private void Init(float width, float height, uint segmentX, uint segmentY, int min, int max)
    {
        size = new Vector2(width, height);
        maxHeight = max;
        minHeight = min;
        unitH = maxHeight - minHeight;
        segment = new Vector2(segmentX, segmentY);
        if (terrain != null)
        {
            Destroy(terrain);
        }
        terrain = new GameObject();
        terrain.name = "plane";
    }

    /// <summary>
    /// 绘制网格
    /// </summary>
    private void DrawMesh()
    {
        Mesh mesh = terrain.AddComponent<MeshFilter>().mesh;
        terrain.AddComponent<MeshRenderer>();
        if (diffuseMap == null)
        {
            Debug.LogWarning("No material,Create diffuse!!");
            diffuseMap = new Material(Shader.Find("Diffuse"));
        }
        if (heightMap == null)
        {
            Debug.LogWarning("No heightMap!!!");
        }
        terrain.renderer.material = diffuseMap;
        //给mesh 赋值
        mesh.Clear();
        mesh.vertices = vertives;//,pos);
        mesh.uv = uvs;
        mesh.triangles = triangles;
        //重置法线
        mesh.RecalculateNormals();
        //重置范围
        mesh.RecalculateBounds();
    }

    /// <summary>
    /// 生成顶点信息
    /// </summary>
    /// <returns></returns>
    private Vector3[] GetVertives()
    {
        int sum = Mathf.FloorToInt((segment.x + 1) * (segment.y + 1));
        float w = size.x / segment.x;
        float h = size.y / segment.y;

        int index = 0;
        GetUV();
        GetTriangles();
        vertives = new Vector3[sum];
        for (int i = 0; i < segment.y + 1; i++)
        {
            for (int j = 0; j < segment.x + 1; j++)
            {
                float tempHeight = 0;
                if (heightMap != null)
                {
                    tempHeight = GetHeight(heightMap, uvs[index]);
                }
                vertives[index] = new Vector3(j * w, tempHeight, i * h);
                index++;
            }
        }
        return vertives;
    }

    /// <summary>
    /// 生成UV信息
    /// </summary>
    /// <returns></returns>
    private Vector2[] GetUV()
    {
        int sum = Mathf.FloorToInt((segment.x + 1) * (segment.y + 1));
        uvs = new Vector2[sum];
        float u = 1.0F / segment.x;
        float v = 1.0F / segment.y;
        uint index = 0;
        for (int i = 0; i < segment.y + 1; i++)
        {
            for (int j = 0; j < segment.x + 1; j++)
            {
                uvs[index] = new Vector2(j * u, i * v);
                index++;
            }
        }
        return uvs;
    }

    /// <summary>
    /// 生成索引信息
    /// </summary>
    /// <returns></returns>
    private int[] GetTriangles()
    {
        int sum = Mathf.FloorToInt(segment.x * segment.y * 6);
        triangles = new int[sum];
        uint index = 0;
        for (int i = 0; i < segment.y; i++)
        {
            for (int j = 0; j < segment.x; j++)
            {
                int role = Mathf.FloorToInt(segment.x) + 1;
                int self = j + (i * role);
                int next = j + ((i + 1) * role);
                triangles[index] = self;
                triangles[index + 1] = next + 1;
                triangles[index + 2] = self + 1;
                triangles[index + 3] = self;
                triangles[index + 4] = next;
                triangles[index + 5] = next + 1;
                index += 6;
            }
        }
        return triangles;
    }

    private float GetHeight(Texture2D texture, Vector2 uv)
    {
        if (texture != null)
        {
            //提取灰度。如果强制读取某个通道,可以忽略
            Color c = GetColor(texture, uv);
            float gray = c.grayscale;//或者可以自己指定灰度提取算法,比如:gray = 0.3F * c.r + 0.59F * c.g + 0.11F * c.b;
            float h = unitH * gray;
            return h;
        }
        else
        {
            return 0;
        }
    }
    /// <summary>
    /// 获取图片上某个点的颜色
    /// </summary>
    /// <param name="texture"></param>
    /// <param name="uv"></param>
    /// <returns></returns>
    private Color GetColor(Texture2D texture, Vector2 uv)
    {

        Color color = texture.GetPixel(Mathf.FloorToInt(texture.width * uv.x), Mathf.FloorToInt(texture.height * uv.y));
        return color;
    }

    /// <summary>
    /// 从外部设置地形的位置坐标
    /// </summary>
    /// <param name="pos"></param>
    public void SetPos(Vector3 pos)
    {
        if (terrain)
        {
            terrain.transform.position = pos;
        }
        else
        {
            SetTerrain();
            terrain.transform.position = pos;
        }
    }
}

Triangulate.cs

using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// 得到三角形索引
/// </summary>
public static class Triangulate
{
    /// <summary>
    /// 根据传入的多边形坐标返回该多边形的三角形顶点索引-构成多边形的索引
    /// </summary>
    /// <param name="points"></param>
    /// <returns></returns>
    public static int[] Points(Vector3[] points)
    {
        var indices = new List<int>();

        int n = points.Length;
        if (n < 3)
            return indices.ToArray();

        int[] V = new int[n];
        if (Area(points) > 0)
        {
            for (int v = 0; v < n; v++)
                V[v] = v;
        }
        else
        {
            for (int v = 0; v < n; v++)
                V[v] = (n - 1) - v;
        }

        int nv = n;
        int count = 2 * nv;
        for (int m = 0, v = nv - 1; nv > 2; )
        {
            if ((count--) <= 0)
                return indices.ToArray();

            int u = v;
            if (nv <= u)
                u = 0;
            v = u + 1;
            if (nv <= v)
                v = 0;
            int w = v + 1;
            if (nv <= w)
                w = 0;

            if (Snip(points, u, v, w, nv, V))
            {
                int a, b, c, s, t;
                a = V[u];
                b = V[v];
                c = V[w];
                indices.Add(a);
                indices.Add(b);
                indices.Add(c);
                m++;
                for (s = v, t = v + 1; t < nv; s++, t++)
                    V[s] = V[t];
                nv--;
                count = 2 * nv;
            }
        }

        indices.Reverse();
        return indices.ToArray();
    }

    private static float Area(Vector3[] points)
    {
        int n = points.Length;
        float A = 0.0f;
        for (int p = n - 1, q = 0; q < n; p = q++)
        {
            Vector3 pval = points[p];
            Vector3 qval = points[q];
            A += pval.x * qval.y - qval.x * pval.y;
        }
        return (A * 0.5f);
    }

    private static bool Snip(Vector3[] points, int u, int v, int w, int n, int[] V)
    {
        int p;
        Vector3 A = points[V[u]];
        Vector3 B = points[V[v]];
        Vector3 C = points[V[w]];
        if (Mathf.Epsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))))
            return false;
        for (p = 0; p < n; p++)
        {
            if ((p == u) || (p == v) || (p == w))
                continue;
            Vector3 P = points[V[p]];
            if (InsideTriangle(A, B, C, P))
                return false;
        }
        return true;
    }

    private static bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
    {
        float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
        float cCROSSap, bCROSScp, aCROSSbp;

        ax = C.x - B.x; ay = C.y - B.y;
        bx = A.x - C.x; by = A.y - C.y;
        cx = B.x - A.x; cy = B.y - A.y;
        apx = P.x - A.x; apy = P.y - A.y;
        bpx = P.x - B.x; bpy = P.y - B.y;
        cpx = P.x - C.x; cpy = P.y - C.y;

        aCROSSbp = ax * bpy - ay * bpx;
        cCROSSap = cx * apy - cy * apx;
        bCROSScp = bx * cpy - by * cpx;

        return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
    }
}


[System.Serializable]
public class Bezier : System.Object
{
    public Vector3 p0;

    public Vector3 p1;
    public Vector3 p2;

    public Vector3 p3;

    public float ti = 0f;
    private Vector3 b0 = Vector3.zero;
    private Vector3 b1 = Vector3.zero;
    private Vector3 b2 = Vector3.zero;
    private Vector3 b3 = Vector3.zero;
    private float Ax;
    private float Ay;
    private float Bx;
    private float By;
    private float Cx;
    private float Cy;

    public Bezier(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3)
    {
        this.p0 = v0;
        this.p1 = v1;
        this.p2 = v2;
        this.p3 = v3;
    }

    public Bezier(Vector3 v0, Vector3 v1,Vector3 v3)
    {
        this.p0 = v0;
        this.p1 = v1;
        this.p2 = v1;
        this.p3 = v3;
    }
    public void setPoint(Vector3 v0, Vector3 v1, Vector3 v2)
    {
        this.p0 = v0;
        this.p1 = v1; 
        this.p3 = v2;
    }
    /// <summary>
    /// 得到按比例的曲线坐标的点
    /// </summary>
    /// <param name="t">为当前要获取曲线坐标点数 X * (1 / n--曲线坐标点的个数)</param>
    /// <returns></returns>
    public Vector3 GetPointAtTime(float t)
    {
        this.CheckConstant();
        float t2 = t * t;
        float t3 = t * t * t;
        float x = this.Ax * t3 + this.Bx * t2 + this.Cx * t + p0.x;
        float y = this.Ay * t3 + this.By * t2 + this.Cy * t + p0.y;
        return new Vector3(x, y, 0.0f);
    }
    private void SetConstant()
    {
        this.Cx = 3f * ((this.p0.x + this.p1.x) - this.p0.x);
        this.Bx = 3f * ((this.p3.x + this.p2.x) - (this.p0.x + this.p1.x)) - this.Cx;
        this.Ax = this.p3.x - this.p0.x - this.Cx - this.Bx;
        this.Cy = 3f * ((this.p0.y + this.p1.y) - this.p0.y);
        this.By = 3f * ((this.p3.y + this.p2.y) - (this.p0.y + this.p1.y)) - this.Cy;
        this.Ay = this.p3.y - this.p0.y - this.Cy - this.By;
    }
    private void CheckConstant()
    {
        if (this.p0 != this.b0 || this.p1 != this.b1 || this.p2 != this.b2 || this.p3 != this.b3)
            this.SetConstant();
        this.b0 = this.p0;
        this.b1 = this.p1;
        this.b2 = this.p2;
        this.b3 = this.p3;
    }
}

/// <summary>
/// 藤制作工具类
/// 创建网格
/// 调整UV
/// </summary>
public class WireToMesh
{
    /// <summary>
    /// 将传入的藤网格轴坐标传入得到网格顶点坐标
    /// </summary>
    /// <param name="_MeshAxis">藤网格的轴顶点</param>
    /// <returns></returns>
    public static Vector3[] GetMeshVertexs(List<Vector3> _MeshAxis,Vector3 _Point,float _Length)
    {
        if (_MeshAxis == null || _MeshAxis.Count == 0)
        {
            return null;
        }
        int length = _MeshAxis.Count - 1;
        float[] m_Angle = new float[_MeshAxis.Count - 1];
        float[] m_Bisector = new float[_MeshAxis.Count - 2];
        Vector3[] m_VertexLength = new Vector3[length];
        int x = 0;
        for (x = 0; x < length; x++)
        {
            m_Angle[x] = MathfTool.GetVectorDeg(_MeshAxis[x], _MeshAxis[x + 1]);
            m_VertexLength[x] = GetVerticalVector(m_Angle[x], _Length,true);

        }
        Vector3[] meshVertexs = new Vector3[_MeshAxis.Count * 2];

        int m_MeshLength = meshVertexs.Length - 1;
        float angle;
        float included;
        for (x = 0; x < _MeshAxis.Count; x++)
        {
            meshVertexs[x] = _MeshAxis[x] - _Point;
            if (x != 0 && x != length)
            {
                angle = m_VertexLength[x].z + m_VertexLength[x - 1].z;
                if(m_VertexLength[x].z > m_VertexLength[x - 1].z)
                {
                    if (m_VertexLength[x].z - m_VertexLength[x - 1].z >= 180)
                    {
                        angle = (angle + 360) * 0.5f;
                        included = m_VertexLength[x - 1].z + 360 - angle;
                    }
                    else
                    {
                        angle = angle * 0.5f;
                        included = m_VertexLength[x].z - angle;
                    }
                }
                else
                {
                    if (m_VertexLength[x - 1].z - m_VertexLength[x].z >= 180)
                    {
                        angle = (angle + 360) * 0.5f;
                        included = m_VertexLength[x].z + 360 - angle;
                    }
                    else
                    {
                        angle = angle * 0.5f;
                        included = m_VertexLength[x - 1].z - angle;
                    }

                }
                if (included < 0)
                {
                    Debug.Log(included + "  " + x);
                }
                included = _Length / Mathf.Cos(included * Mathf.Deg2Rad);

                meshVertexs[m_MeshLength - x] = _MeshAxis[x] - _Point + GetVerticalVector(angle, included,false);
                meshVertexs[m_MeshLength - x].z = 0;
            }
            else
            {
                meshVertexs[m_MeshLength - x] = meshVertexs[x] + (x == length ? m_VertexLength[x - 1] : m_VertexLength[x]);
                meshVertexs[m_MeshLength - x].z = 0;
            }
        }
        
        return meshVertexs;
    }

    /// <summary>
    /// 根据传入的藤的网格顶点计算相应的网格UV
    /// </summary>
    /// <param name="_MeshVertexs">藤网格顶点</param>
    /// <returns></returns>
    public static Vector2[] CountMeshUV(Vector3[] _MeshVertexs, float _VineWidth)
    {
        Vector2[] UV = new Vector2[_MeshVertexs.Length];
        Vector2[] minMax = GetMinMaxXY(_MeshVertexs);
        int l = _MeshVertexs.Length / 2 - 1;
        float U = minMax[1].x - minMax[0].x;
        float V = minMax[1].y - minMax[0].y;
        for (int x = 0; x < _MeshVertexs.Length; x++)
        {
            //UV[x].x = (_MeshVertexs[x].x - minMax[0].x) / U;
            //UV[x].y = (_MeshVertexs[x].y - minMax[0].y) / V;

            UV[x].x = _MeshVertexs[x].x * _VineWidth;
            if(x == l)
            {
                UV[x].y = 0;
            }
            else if(x == 0)
            {
                UV[x].y = 0;
            }
            else if(x == _MeshVertexs.Length - 1)
            {
                UV[x].y = 1;
            }
            else if(x < l)
            {
                UV[x].y = 0;
            }
            else
            {
                UV[x].y = 1;
            }
            //UV[x].y = UV[x].y < 0 ? (UV[x].y + 360) * Mathf.Deg2Rad : UV[x].y * Mathf.Deg2Rad;
            
        }
        return UV;
    }
    private static Vector2[] GetMinMaxXY(Vector3[] _MeshVertexs)
    {
        Vector2[] minMax = new Vector2[2];
        minMax[0] = _MeshVertexs[0];
        minMax[1] = _MeshVertexs[1];
        for (int x = 0; x < _MeshVertexs.Length; x++)
        {
            minMax[0].x = _MeshVertexs[x].x < minMax[0].x ? _MeshVertexs[x].x : minMax[0].x;
            minMax[0].y = _MeshVertexs[x].y < minMax[0].y ? _MeshVertexs[x].y : minMax[0].y;

            minMax[1].x = _MeshVertexs[x].x > minMax[1].x ? _MeshVertexs[x].x : minMax[1].x;
            minMax[1].y = _MeshVertexs[x].y > minMax[1].y ? _MeshVertexs[x].y : minMax[1].y;
        }
        return minMax;
    }

    /// <summary>
    /// 获取网格三角形索引
    /// </summary>
    /// <param name="_MeshVertexs"></param>
    /// <returns></returns>
    public static int[] GetMeshTriangles(Vector3[] _MeshVertexs)
    {
        int[] m_Triangles = new int[(_MeshVertexs.Length - 2) * 3];
        int length = (int)_MeshVertexs.Length / 2 - 1;
        int counts = _MeshVertexs.Length - 1;
        int x = 0;
        for (int y = 0; y < length; y++)
        {
            m_Triangles[x++] = y;
            m_Triangles[x++] = counts - y;
            m_Triangles[x++] = counts - y - 1;

            m_Triangles[x++] = y;
            m_Triangles[x++] = counts - y - 1;
            m_Triangles[x++] = y + 1;
        }
        return m_Triangles;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="_Angle"></param>
    /// <param name="_Length"></param>
    /// <returns></returns>
    private static Vector3 GetVerticalVector(float _Angle, float _Length,bool _Bool)
    {
        Vector3 m_Vertical = new Vector3(0,0,0);
        
        m_Vertical.z = _Bool ?  _Angle + 90 : _Angle;
        m_Vertical.z = m_Vertical.z > 360 ? m_Vertical.z - 360 : m_Vertical.z;
        if (m_Vertical.z <= 90)
        {
            m_Vertical.x = Mathf.Cos(m_Vertical.z * Mathf.Deg2Rad) * _Length;
            m_Vertical.y = Mathf.Sin(m_Vertical.z * Mathf.Deg2Rad) * _Length;
        }
        else if (m_Vertical.z <= 180)
        {
            m_Vertical.x = -Mathf.Sin((m_Vertical.z - 90) * Mathf.Deg2Rad) * _Length;
            m_Vertical.y = Mathf.Cos((m_Vertical.z - 90) * Mathf.Deg2Rad) * _Length;
        }
        else if (_Angle <= 270)
        {
            m_Vertical.x = -Mathf.Cos((m_Vertical.z - 180) * Mathf.Deg2Rad) * _Length;
            m_Vertical.y = -Mathf.Sin((m_Vertical.z - 180) * Mathf.Deg2Rad) * _Length;
        }
        else
        {
            m_Vertical.x = -Mathf.Cos((m_Vertical.z - 270) * Mathf.Deg2Rad) * _Length;
            m_Vertical.y = Mathf.Sin((m_Vertical.z - 270) * Mathf.Deg2Rad) * _Length;
        }
        return m_Vertical;
    }
}

public class MathfTool
{
    /// <summary>
    /// 计算两个向量和X轴的弧度
    /// </summary>
    /// <param name="_Point1">起点</param>
    /// <param name="_Point2">终点</param>
    /// <returns>返回两坐标和X轴的弧度</returns>
    public static float GetVectorRad(Vector3 _Point1, Vector3 _Point2)
    {
        return GetVectorDeg(_Point1,_Point2) * Mathf.Deg2Rad;
    }

    /// <summary>
    /// 返回两个向量和X轴的角度
    /// </summary>
    /// <param name="_Point1">起点</param>
    /// <param name="_Point2">终点</param>
    /// <returns>返回两坐标和X轴的角度</returns>
    public static float GetVectorDeg(Vector3 _Point1, Vector3 _Point2)
    {
        Vector3 m_Point = _Point2 - _Point1;
        float m_Rad = Mathf.Atan(m_Point.y / m_Point.x) * Mathf.Rad2Deg;
        if (m_Point.y < 0 && m_Point.x < 0)
        {
            m_Rad += 180;
        }
        else if (m_Point.x < 0)
            m_Rad = 180 + m_Rad;
        else
            m_Rad = m_Point.y < 0 ? 360 + m_Rad : m_Rad;
        
        m_Rad = m_Rad > 360 ? m_Rad - 360 : m_Rad;
        return m_Rad;
    }

    /// <summary>
    /// 求平分角
    /// </summary>
    /// <param name="_Angle1">左边顶点</param>
    /// <param name="_Angle2">中点</param>
    /// <param name="_Angle3">右边顶点</param>
    /// <returns></returns>
    public static float GetBisectorDeg(Vector3 _Angle1,Vector3 _Angle2,Vector3 _Angle3)
    {
        float m_Angle = (MathfTool.GetVectorDeg(_Angle2, _Angle1) + MathfTool.GetVectorDeg(_Angle2, _Angle3)) * 0.5f;
        return m_Angle;
    }
}

 

Road.cs

// @khenkel 
// parabox llc

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System.Collections.Generic;
using System.Text;
public class Road : MonoBehaviour 
{
	public bool acceptInput = false;
	public bool connectEnds = false;
	public int insertPoint = -1;
	public List<Vector3> points = new List<Vector3>();
	public float roadWidth = 1f;
	public float groundOffset = .1f;
	public float[] theta;
	public int terrainLayer = 8;

	// uv options 
	public bool swapUV = false;
	public bool flipU = true;
	public bool flipV = true;
	public Vector2 uvScale = Vector2.one;
	public Vector2 uvOffset = Vector2.zero;

	// texture
	public Material mat;

	public void Refresh()
	{
		if(points.Count < 2)
			return;

		transform.localScale = Vector3.one;

		if(!gameObject.GetComponent<MeshFilter>())
			gameObject.AddComponent<MeshFilter>();
		else
		{
			if(gameObject.GetComponent<MeshFilter>().sharedMesh != null)
				DestroyImmediate(gameObject.GetComponent<MeshFilter>().sharedMesh);
		}

		if(!gameObject.GetComponent<MeshRenderer>())
			gameObject.AddComponent<MeshRenderer>();

		List<Vector3> v = new List<Vector3>();
		List<int> t = new List<int>();

		// calculate angles for each line segment, then build out a plane for it
		int tri_index = 0;
		int segments = connectEnds ? points.Count : points.Count-1;
		theta = new float[segments];

		for(int i = 0; i < segments; i++)
		{
			Vector2 a = points[i+0].ToXZVector2();
			Vector2 b = (connectEnds && i == segments-1) ? points[0].ToXZVector2() : points[i+1].ToXZVector2();
			
			bool flip = (a.x > b.x);// ? theta[i] : -theta[i];

			Vector3 rght = flip ? new Vector3(0,0,-1) : new Vector3(0,0,1);
			Vector3 lft = flip ? new Vector3(0,0,1) : new Vector3(0,0,-1);

			theta[i] = MathExtensions.AngleRadian(a, b);

			// seg a
			v.Add(points[i] + rght * roadWidth);		
			v.Add(points[i] + lft * roadWidth);
			// seg b
			int u = (connectEnds && i == segments-1) ? 0 : i+1;
			v.Add(points[u] + rght * roadWidth);		
			v.Add(points[u] + lft * roadWidth);

			// apply angular rotation to points
			int l = v.Count-4;

			v[l+0] = v[l+0].RotateAroundPoint(points[i+0], -theta[i]);
			v[l+1] = v[l+1].RotateAroundPoint(points[i+0], -theta[i]);

			v[l+2] = v[l+2].RotateAroundPoint(points[u], -theta[i]);
			v[l+3] = v[l+3].RotateAroundPoint(points[u], -theta[i]);

			t.AddRange(new int[6]{
				tri_index + 2,
				tri_index + 1,
				tri_index + 0,
				
				tri_index + 2,
				tri_index + 3, 
				tri_index + 1
				});

			tri_index += 4;
		}	

		// join edge vertices
		if(points.Count > 2)
		{
			segments = connectEnds ? v.Count : v.Count - 4;
			for(int i = 0; i < segments; i+=4)
			{
				int p4 = (connectEnds && i == segments-4) ? 0 : i + 4;
				int p5 = (connectEnds && i == segments-4) ? 1 : i + 5;
				int p6 = (connectEnds && i == segments-4) ? 2 : i + 6;
				int p7 = (connectEnds && i == segments-4) ? 3 : i + 7;

				Vector2 leftIntercept = MathExtensions.InterceptPoint(
					v[i+0].ToXZVector2(), v[i+2].ToXZVector2(), 
					v[p4].ToXZVector2(), v[p6].ToXZVector2());

				Vector2 rightIntercept = MathExtensions.InterceptPoint(
					v[i+1].ToXZVector2(), v[i+3].ToXZVector2(), 
					v[p5].ToXZVector2(), v[p7].ToXZVector2());

				v[i+2] = leftIntercept.ToVector3();			
				v[p4] = leftIntercept.ToVector3();

				v[i+3] = rightIntercept.ToVector3();
				v[p5] = rightIntercept.ToVector3();
			}
		}

		transform.position = Vector3.zero;

		// // center pivot point and set height offset
		Vector3 cen = v.Average();
		Vector3 diff = cen - transform.position;
		transform.position = cen;

		for(int i = 0; i < v.Count; i++)
		{
			v[i] = new Vector3(v[i].x, GroundHeight(v[i]) + groundOffset, v[i].z);
			v[i] -= diff;
		}

		Mesh m = new Mesh();
		m.vertices = v.ToArray();
		m.triangles = t.ToArray();
		m.uv = CalculateUV(m.vertices);
		m.RecalculateNormals();
		gameObject.GetComponent<MeshFilter>().sharedMesh = m;
		gameObject.GetComponent<MeshRenderer>().sharedMaterial = mat;
#if UNITY_EDITOR
		Unwrapping.GenerateSecondaryUVSet(gameObject.GetComponent<MeshFilter>().sharedMesh);
#endif
	}

	public Vector2[] CalculateUV(Vector3[] vertices)
	{
		Vector2[] uvs = new Vector2[vertices.Length];

		float scale = (1f / Vector3.Distance(vertices[0], vertices[1]));
		Vector2 topLeft = Vector2.zero;

		int v = 0; // vertex iterator
		int segments = connectEnds ? points.Count : points.Count-1;
		for(int i = 0; i < segments; i++)
		{		
			Vector3 segCenter = (vertices[v+0] + vertices[v+1] + vertices[v+2] + vertices[v+3]) / 4f;

			Vector2 u0 = vertices[v+0].RotateAroundPoint(segCenter, theta[i] + (90f * Mathf.Deg2Rad) ).ToXZVector2();
			Vector2 u1 = vertices[v+1].RotateAroundPoint(segCenter, theta[i] + (90f * Mathf.Deg2Rad) ).ToXZVector2();
			Vector2 u2 = vertices[v+2].RotateAroundPoint(segCenter, theta[i] + (90f * Mathf.Deg2Rad) ).ToXZVector2();
			Vector2 u3 = vertices[v+3].RotateAroundPoint(segCenter, theta[i] + (90f * Mathf.Deg2Rad) ).ToXZVector2();

			// normalizes uv scale
			uvs[v+0] = u0 * scale;
			uvs[v+1] = u1 * scale;
			uvs[v+2] = u2 * scale;
			uvs[v+3] = u3 * scale;

			Vector2 delta = topLeft - uvs[v+0];
			uvs[v+0] += delta;
			uvs[v+1] += delta;
			uvs[v+2] += delta;
			uvs[v+3] += delta;

			topLeft = uvs[v+2];
			v += 4;
		}

		// Normalize X axis, apply to Y
		scale = 1f / uvs[1].x - uvs[0].x;
		for(int i = 0; i < uvs.Length; i++)
		{
			uvs[i] *= scale;
		}

		// optional uv modifications
		if(swapUV)
		{
			for(int i = 0; i < uvs.Length; i++)
				uvs[i] = new Vector2(uvs[i].y, uvs[i].x);
		}
			
		if(flipU)
		{
			for(int i = 0; i < uvs.Length; i++)
				uvs[i] = new Vector2(-uvs[i].x, uvs[i].y);
		}

		if(flipV)
		{
			for(int i = 0; i < uvs.Length; i++)
				uvs[i] = new Vector2(uvs[i].x, -uvs[i].y);
		}

		for(int i = 0; i < uvs.Length; i++)
		{
			uvs[i] += uvOffset;
			uvs[i] = Vector2.Scale(uvs[i], uvScale);
		}
		return uvs;
	}

	public float GroundHeight(Vector3 v)
	{
		RaycastHit ground = new RaycastHit();

		if(Physics.Raycast(v, -Vector3.up, out ground, Mathf.Infinity))//, 1 << terrainLayer))
			return ground.point.y;

		if(Physics.Raycast(v, Vector3.up, out ground, Mathf.Infinity))//, 1 << terrainLayer))
			return ground.point.y;

		// try casting from really high up next
		if(Physics.Raycast(v + Vector3.up*1000f, -Vector3.up, out ground))
			return ground.point.y;

		return v.y;
	}	
}


public static class MathExtensions
{
	public static Vector3 RotateAroundPoint(this Vector3 v, Vector3 origin, float theta)
	{
		// discard y val
		float cx = origin.x, cy = origin.z;	// origin
		float px = v.x, py = v.z;			// point

		float s = Mathf.Sin(theta);
		float c = Mathf.Cos(theta);

		// translate point back to origin:
		px -= cx;
		py -= cy;

		// rotate point
		float xnew = px * c + py * s;
		float ynew = -px * s + py * c;

		// translate point back:
		px = xnew + cx;
		py = ynew + cy;
		
		return new Vector3(px, v.y, py);
	}

	// a-b vector, c-d vector
	public static Vector2 InterceptPoint(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3)
	{
		float a1, b1, c1, a2, b2, c2;

		a1 = p1.y - p0.y;
		b1 = p0.x - p1.x;
		c1 = a1*p0.x + b1*p0.y;

		a2 = p3.y - p2.y;
		b2 = p2.x - p3.x;
		c2 = a2*p2.x + b2*p2.y;

		// Debug.Log(a1 + " + " + b1 + " = " + c1);
		// Debug.Log(a2 + " + " + b2 + " = " + c2);

		float det = a1*b2 - a2*b1;
		if(det == 0)
		{
#if DEBUG
			Debug.LogWarning("Lines are parallel");
#endif
			return Vector2.zero;
		}
		else
		{
			float x = (b2*c1 - b1*c2)/det;
			float y = (a1*c2 - a2*c1)/det;
			// Debug.Log("x " + x + "  y " + y);
			return new Vector2(x, y);
		}
	}

	// ....what have I done....
	public static void SlopeIntercept(Vector2 a, Vector2 c, out float m, out float b, out float x, out float y)
	{
		float y0 = (c.y-a.y);
		float x0 = (c.x-a.x);
		
		if(y0 == 0f)
		{
			x = float.NaN;
			y = a.y;
			m = float.NaN;
			b = float.NaN;
			return;
		}

		if(x0 == 0f)
		{
			x = a.x;
			y = float.NaN;
			m = float.NaN;
			b = float.NaN;
			return;
		}
		
		m = (c.y-a.y)/(c.x-a.x);
		b = (m*a.x)-a.y;

		x = float.NaN;
		y = float.NaN;
	}

	public static Vector2 ToXZVector2(this Vector3 v)
	{
		return new Vector2(v.x, v.z);
	}

	public static Vector3 ToVector3(this Vector2 v)
	{
		return new Vector3(v.x, 0f, v.y);
	}

	public static float AngleRadian(this Vector2 a, Vector2 b)
	{
		float opp = b.y - a.y;
		float adj = b.x - a.x;
		// Debug.Log(opp + " / " + adj);
		if(adj == 0)
			return Mathf.Tan(0f);
		else
			return Mathf.Atan( opp / adj );// * Mathf.Rad2Deg;
	}

	public static Vector3 Average(this List<Vector3> arr)
	{
		if(arr == null || arr.Count < 1)
			return Vector3.zero;

		Vector3 n = arr[0];
		for(int i = 1; i < arr.Count; i++)
			n += arr[i];
		return n/(float)arr.Count;
	}
}

public static class StringExtensions
{
	public static string ToFormattedString<T>(this T[] arr, string delimiter)
	{
		StringBuilder sb = new StringBuilder();
		for(int i = 0; i < arr.Length-1; i++)
			sb.Append(arr[i].ToString() + delimiter);
		sb.Append(arr[arr.Length-1]);
		return sb.ToString();
	}	
}

 

RoadTool.cs

// @khenkel 
// parabox llc

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Text;

[CustomEditor(typeof(Road))]
public class RoadTool : Editor
{
    #region Private

    Road road;
    #endregion

    #region Constant

    const float MIN_ROAD_WIDTH = .3f;
    const float MAX_ROAD_WIDTH = 50f;

    const float MIN_GROUND_OFFSET = .001f;
    const float MAX_GROUND_OFFSET = 1f;
    #endregion

    #region Shortcut

    // big list of things that cause onscenegui to loop endlessly if mixed with our code.
    public bool earlyOut
    {
        get
        {
            return (
                Event.current.alt ||
                Tools.current == Tool.View ||
                GUIUtility.hotControl > 0 ||
                (Event.current.isMouse ? Event.current.button > 1 : false) ||
                Tools.viewTool == ViewTool.FPS ||
                Tools.viewTool == ViewTool.Orbit);
        }
    }
    #endregion

    #region Initalization / Destruction

    [MenuItem("GameObject/Create Other/Road %#r")]
    public static void init()
    {
        GameObject go = new GameObject();
        Road r = go.AddComponent<Road>();
        r.acceptInput = true;
        r.mat = (Material)Resources.LoadAssetAtPath("Assets/RoadTool/Example/Texture/Materials/Road.mat", typeof(Material));
        go.name = "Road " + go.GetInstanceID();
        go.AddComponent<MeshRenderer>();
        Selection.activeObject = go;
    }

    public void OnEnable()
    {
        road = (Road)target;
    }
    #endregion

    #region Window Lifecycle

    bool showUVOptions = false;
    public override void OnInspectorGUI()
    {
        GUI.changed = false;
        road.roadWidth = EditorGUILayout.Slider("Road Width", road.roadWidth, MIN_ROAD_WIDTH, MAX_ROAD_WIDTH);
        road.groundOffset = EditorGUILayout.Slider("Ground Offset", road.groundOffset, MIN_GROUND_OFFSET, MAX_GROUND_OFFSET);

        EditorGUILayout.HelpBox("A negative value inserts new points at the end of the line.", MessageType.Info);
        road.insertPoint = EditorGUILayout.IntField("Insert At Point", road.insertPoint);

        road.connectEnds = EditorGUILayout.Toggle("Connect Ends", road.connectEnds);

        showUVOptions = EditorGUILayout.Foldout(showUVOptions, "UV Options");
        if (showUVOptions)
        {
            road.swapUV = EditorGUILayout.Toggle("Swap UV", road.swapUV);
            road.flipU = EditorGUILayout.Toggle("Flip U", road.flipU);
            road.flipV = EditorGUILayout.Toggle("Flip V", road.flipV);

            road.uvScale = EditorGUILayout.Vector2Field("Scale", road.uvScale);
            road.uvOffset = EditorGUILayout.Vector2Field("Offset", road.uvOffset);
        }

        road.mat = (Material)EditorGUILayout.ObjectField("Material", road.mat, typeof(Material), true);

        if (road.acceptInput)
        {
            GUI.backgroundColor = Color.red;
            if (GUILayout.Button("Lock"))
            {
                road.acceptInput = false;
                SceneView.RepaintAll();
            }
        }
        else
        {
            GUI.backgroundColor = Color.green;
            if (GUILayout.Button("Modify"))
            {
                road.acceptInput = true;
                SceneView.RepaintAll();
            }
        }


        GUI.backgroundColor = Color.white;
        if (GUI.changed)
        {
            EditorUtility.SetDirty(road);
            road.Refresh();
            SceneView.RepaintAll();
        }
    }
    #endregion

    #region Scene

    Vector3 groundPoint = Vector3.zero;
    Vector3 tp;
    public void OnSceneGUI()
    {
        if (road.acceptInput == false)
            return;

        Event e = Event.current;

        if (e.type == EventType.ValidateCommand)
        {
            road.Refresh();
            SceneView.RepaintAll();
        }

        if (e.isKey && (e.keyCode == KeyCode.Escape || e.keyCode == KeyCode.Return))
        {
            road.acceptInput = false;
            SceneView.RepaintAll();
        }

        // Existing point handles
        DrawHandleGUI(road.points);

        // TODO -- figure out why Handles.PositionHandle is sooo slow when panning

        if (!e.alt)
            for (int i = 0; i < road.points.Count; i++)
            {
                tp = road.points[i];
                road.points[i] = Handles.PositionHandle(road.points[i], Quaternion.identity);

                if (tp != road.points[i])
                {
                    Vector3 p = road.points[i];
                    p.y = road.GroundHeight(road.points[i]);
                    road.points[i] = p;
                    road.Refresh();
                }
            }

        if (earlyOut)
            return;

        // New point placement from here down 

        int controlID = GUIUtility.GetControlID(FocusType.Passive);
        HandleUtility.AddDefaultControl(controlID);

        if (e.modifiers != 0 || Tools.current != Tool.Move)
        {
            if (e.type == EventType.mouseUp && e.button == 0 && Tools.current != Tool.Move && e.modifiers == 0)
            {
                FindSceneView().ShowNotification(new GUIContent("Tool must be set to 'Move' to place points!", ""));
                SceneView.RepaintAll();
            }
            return;
        }

        if ((e.type == EventType.mouseDown || e.type == EventType.mouseDrag) && e.button == 0)
        {
            Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
            RaycastHit ground = new RaycastHit();

            if (Physics.Raycast(ray.origin, ray.direction, out ground))
                groundPoint = ground.point;
        }

        // Listen for mouse input
        if (e.type == EventType.mouseUp && e.button == 0)
        {
            Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
            RaycastHit ground = new RaycastHit();

            if (Physics.Raycast(ray.origin, ray.direction, out ground))
            {
                groundPoint = ground.point;
                AddPoint(groundPoint);
            }
        }
    }

    public static SceneView FindSceneView()
    {
        return SceneView.lastActiveSceneView == null ? EditorWindow.GetWindow<SceneView>() : SceneView.lastActiveSceneView;
    }
    #endregion

    #region Handles

    public void DrawHandleGUI(List<Vector3> points)
    {
        if (points == null || points.Count < 1)
            return;

        Handles.BeginGUI();
        GUI.backgroundColor = Color.red;
        for (int i = 0; i < points.Count; i++)
        {

            Vector2 p = HandleUtility.WorldToGUIPoint(points[i]);

            if (GUI.Button(new Rect(p.x + 10, p.y - 50, 25, 25), "x"))
                DeletePoint(i);

            GUI.Label(new Rect(p.x + 45, p.y - 50, 200, 25), "Point: " + i.ToString());
        }
        GUI.backgroundColor = Color.white;
        Handles.EndGUI();
    }
    #endregion

    #region Point Management

    public void AddPoint(Vector3 v)
    {
        Undo.RegisterUndo(new Object[1] { target as Object }, "Set Point");

        if (road.insertPoint < 0 || road.insertPoint > road.points.Count)
            road.points.Add(v);
        else
            road.points.Insert(road.insertPoint, v);

        road.Refresh();
        SceneView.RepaintAll();
    }

    public void DeletePoint(int index)
    {
        Undo.RegisterUndo(new Object[] { target }, "Delete Point");

        road.points.RemoveAt(index);
        road.Refresh();
        SceneView.RepaintAll();
    }
    #endregion
}

 

本文链接:

https://www.bobsong.net/808.html
1 + 7 =
1 评论
    665190Chrome 80Windows 10
    9月16日 回复

    兄弟,少了个类啊,CreationVine。