Unity空间画任意管道

发表于2017-10-18
评论12 7.6k浏览
三维空间画管道主要是用到空间圆参数方程来实现。
在拐角的处理方面,暂时只是前后管道预留出一个半径的长度,用贝塞尔曲线进行十均分然后画mesh连接,由于圆的空间参数方程的单位向量的a与b的值是圆的随机值,是通过坐标向量求得,所以可能会出现画圆的时候起点不统一的情况。
在前后管道重新生成的过程中,直管道部分暂时是两个重叠,尾部缺一个半径的距离,是为了避免管道有裂缝。(这不是重点,重点是参考空间圆的参数方程,代码中有必要注释)

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class drawPipe : MonoBehaviour {
    //第一个点和下一个点
    Vector3 firstPoint = Vector3.zero;
    Vector3 nextPoint = Vector3.zero;
    //管道半径
    public float circleR = 0.5f;
    //管道材质球
    public Material lineMaterial;
    //管道方向法线向量
    Vector3 n,n1,n2;
    //法向量单位向量
    Vector3 n0,n10, n20;
    //坐标向量
    Vector3 i1 = new Vector3(0, 1, 0);
    Vector3 i2 = new Vector3(1, 0, 0);
    //单位向量
    Vector3 a, al;
    Vector3 b, bl;
    //第一个与下一个圆的坐标
    Vector3 circleXYZFirst;
    Vector3 circleXYZNext;
    //123三点
    Vector3[] points = new Vector3[3];
    //拐角三点的前后两点
    Vector3 cornerPointF;
    Vector3 cornerPointB;
    //第一次画管道的最后点的预留点
    Vector3 secondPF;
    //拐点后用来重复续接的靠近尾部的点。
    Vector3 cornerPointB_B;
    //用于下次接着画的起点
    Vector3 cornerPointBStart = Vector3.zero;
    //拐点密度
    float density = 0.1f;//绘制的0-1的间隙 越小曲线越接近曲线
    //贝塞尔曲线生成拐点坐标列表
    List<Vector3> cornerPoints = new List<Vector3>();
    Vector3[] _Vertices = new Vector3[16];
    Vector3[] _VerticesAll = new Vector3[96];
    List<Vector3> _VerticesAllList = new List<Vector3>();
    GameObject pointItem;
    // Use this for initialization
    void Start () {
        points = new Vector3[3]
        {
            Vector3.zero,
            Vector3.zero,
            Vector3.zero
        };
    }
	// Update is called once per frame
	void Update () {
        DrawPipeA();
    }
    private void DrawPipeA()
    {
        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit hit;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if(Physics.Raycast(ray,out hit) && hit.collider.tag == "plane")
            {
                if(firstPoint == Vector3.zero)
                {
                    firstPoint = hit.point;
                    points[0] = firstPoint;
                    pointItem = (GameObject)Instantiate(Resources.Load("Prefabs/point"), points[0], transform.rotation);
                }
                else if(nextPoint == Vector3.zero)
                {
                    nextPoint = hit.point;
                    points[1] = nextPoint;
                    //画管道
                    DrawPipeB(points[0], points[1]);
                    pointItem = (GameObject)Instantiate(Resources.Load("Prefabs/point"), points[1], transform.rotation);
                }
                else
                {
                    if(points[2] == Vector3.zero)
                    {
                        nextPoint = hit.point;
                        points[2] = nextPoint;
                        //画带拐角的管道
                        DrawPipeB(points[0], points[1], points[2]);
                        pointItem = (GameObject)Instantiate(Resources.Load("Prefabs/point"), points[2], transform.rotation);
                    }
                    else
                    {
                        points[0] = points[1];
                        points[1] = points[2];
                        points[2] = hit.point;
                        //画带拐角的管道
                        DrawPipeB(points[0], points[1], points[2]);
                        pointItem = (GameObject)Instantiate(Resources.Load("Prefabs/point"), points[2], transform.rotation);
                    }
                }
            }
        }
    }
    //画管道,创建mesh
    private void DrawPipeB(Vector3 firstP,Vector3 secondP)
    {
        n = secondP - firstP;
        n0 = n / Mathf.Sqrt(n.x * n.x   n.y * n.y   n.z * n.z);
        secondPF = secondP - n0 * circleR;
        GameObject _Line = new GameObject("MeshLine");
        _Line.AddComponent<MeshFilter>();
        _Line.AddComponent<MeshRenderer>();
        if (lineMaterial == null) Debug.Log("The line material is empty! 线段未添加材质球!");
        else _Line.GetComponent<MeshRenderer>().material = lineMaterial;
        al = Vector3.Cross(n, i1);//叉乘
        if (al == Vector3.zero)
        {
            al = Vector3.Cross(n, i2);
        }
        a = Vector3.Normalize(al);//单位化向量
        bl = Vector3.Cross(n, a);
        b = Vector3.Normalize(bl);
        for (float angle = 0, x = 0; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle   (Mathf.PI / 4), x  )
        {
            circleXYZFirst.x = firstP.x   circleR * Mathf.Cos(angle) * a.x   circleR * Mathf.Sin(angle) * b.x;//参数方程公式
            circleXYZFirst.y = firstP.y   circleR * Mathf.Cos(angle) * a.y   circleR * Mathf.Sin(angle) * b.y;
            circleXYZFirst.z = firstP.z   circleR * Mathf.Cos(angle) * a.z   circleR * Mathf.Sin(angle) * b.z;
            _Vertices[(int)x] = circleXYZFirst;
        }
        for (float angle = 0, y = 8; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle   (Mathf.PI / 4), y  )
        {
            circleXYZNext.x = secondPF.x   circleR * Mathf.Cos(angle) * a.x   circleR * Mathf.Sin(angle) * b.x;//参数方程公式
            circleXYZNext.y = secondPF.y   circleR * Mathf.Cos(angle) * a.y   circleR * Mathf.Sin(angle) * b.y;
            circleXYZNext.z = secondPF.z   circleR * Mathf.Cos(angle) * a.z   circleR * Mathf.Sin(angle) * b.z;
            _Vertices[(int)y] = circleXYZNext;
        }
        //生成所有面
        int[] _Triangles = new int[48]
        {
            //顶点0,1,9按顺序生成一个面,面朝向顺时针方向
            0,1,8,
            1,9,8,
            1,2,9,
            2,10,9,
            2,3,10,
            3,11,10,
            3,4,11,
            4,12,11,
            4,5,12,
            5,13,12,
            5,6,13,
            6,14,13,
            6,7,14,
            7,15,14,
            7,0,15,
            0,8,15
        };
        Mesh _mesh = new Mesh();
        _mesh.Clear();
        _mesh.vertices = _Vertices;
        _mesh.triangles = _Triangles;
        _mesh.RecalculateNormals();
        _Line.GetComponent<MeshFilter>().mesh = _mesh;
    }
    //画带拐角的管道,创建mesh
    private void DrawPipeB(Vector3 firstP, Vector3 secondP,Vector3 thirdP)
    {
        cornerPoints.Clear();
        _VerticesAllList.Clear();
        GameObject _Line = new GameObject("MeshLine");
        _Line.AddComponent<MeshFilter>();
        _Line.AddComponent<MeshRenderer>();
        if (lineMaterial == null) Debug.Log("The line material is empty! 线段未添加材质球!");
        else _Line.GetComponent<MeshRenderer>().material = lineMaterial;
        n1 = secondP - firstP;
        n2 = thirdP - secondP;
        n10 = n1 / Mathf.Sqrt(n1.x * n1.x   n1.y * n1.y   n1.z * n1.z);
        cornerPointF = secondP - n10 * circleR;
        n20 = n2 / Mathf.Sqrt(n2.x * n2.x   n2.y * n2.y   n2.z * n2.z);
        cornerPointB = secondP   n20 * circleR;
        cornerPointB_B = thirdP - n20 * circleR;
        //处理第一个点
        if (cornerPointBStart == Vector3.zero)
        {
            cornerPoints.Add(firstP);
        }
        else
        {
            cornerPoints.Add(cornerPointBStart);
        }
        //给第一个点赋值,留作下次用
        cornerPointBStart = cornerPointB;
        for (float i = 0; i < 1; i  = density)
        {
            Vector3 cornerPot = CornerPoint(i   density, cornerPointF, cornerPointB, secondP);
            cornerPoints.Add(cornerPot);
            //Instantiate(Resources.Load("Prefabs/point11"), cornerPot, transform.rotation);
        }
        cornerPoints.Add(cornerPointB_B);
        for (int i = 1; i < cornerPoints.Count; i  )
        {
            n = cornerPoints[i] - cornerPoints[i - 1];
            //GameObject _Line = new GameObject("MeshLine"   i);
            //_Line.AddComponent<MeshFilter>();
            //_Line.AddComponent<MeshRenderer>();
            //if (lineMaterial == null) Debug.Log("The line material is empty! 线段未添加材质球!");
            //else _Line.GetComponent<MeshRenderer>().material = lineMaterial;
            al = Vector3.Cross(n, i1);//叉乘
            if (al == Vector3.zero)
            {
                al = Vector3.Cross(n, i2);
            }
            a = Vector3.Normalize(al);//单位化向量
            bl = Vector3.Cross(n, a);
            b = Vector3.Normalize(bl);
            if(i == 1)
            {
                for (float angle = 0, x = 0; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle   (Mathf.PI / 4), x  )
                {
                    circleXYZFirst.x = cornerPoints[i - 1].x   circleR * Mathf.Cos(angle) * a.x   circleR * Mathf.Sin(angle) * b.x;//参数方程公式
                    circleXYZFirst.y = cornerPoints[i - 1].y   circleR * Mathf.Cos(angle) * a.y   circleR * Mathf.Sin(angle) * b.y;
                    circleXYZFirst.z = cornerPoints[i - 1].z   circleR * Mathf.Cos(angle) * a.z   circleR * Mathf.Sin(angle) * b.z;
                    _Vertices[(int)x] = circleXYZFirst;
                    _VerticesAllList.Add(_Vertices[(int)x]);//第一个圈点存入list
                }
            }
            else
            {
                for (float angle = 0, x = 0; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle   (Mathf.PI / 4), x  )
                {
                    _Vertices[(int)x] = _Vertices[(int)x   8];
                    //_VerticesAllList.Add(_Vertices[(int)x]);//拐角点存入list
                }
            }
            for (float angle = 0, y = 8; angle < (2 * Mathf.PI - Mathf.PI / 4); angle = angle   (Mathf.PI / 4), y  )
            {
                circleXYZNext.x = cornerPoints[i].x   circleR * Mathf.Cos(angle) * a.x   circleR * Mathf.Sin(angle) * b.x;//参数方程公式
                circleXYZNext.y = cornerPoints[i].y   circleR * Mathf.Cos(angle) * a.y   circleR * Mathf.Sin(angle) * b.y;
                circleXYZNext.z = cornerPoints[i].z   circleR * Mathf.Cos(angle) * a.z   circleR * Mathf.Sin(angle) * b.z;
                _Vertices[(int)y] = circleXYZNext;
                _VerticesAllList.Add(_Vertices[(int)y]);//除了第一圈点的剩下点存入list
            }
        }
        for(int i = 0;i < _VerticesAllList.Count; i  )
        {
            _VerticesAll[i] = _VerticesAllList[i];
        }
        //生成所有面
        int[] _Triangles = new int[48]
        {
        //顶点0,1,9按顺序生成一个面,面朝向顺时针方向
        0,1,8,
        1,9,8,
        1,2,9,
        2,10,9,
        2,3,10,
        3,11,10,
        3,4,11,
        4,12,11,
        4,5,12,
        5,13,12,
        5,6,13,
        6,14,13,
        6,7,14,
        7,15,14,
        7,0,15,
        0,8,15,
        };
        int[] _TrianglesAll = new int[528];
        for(int i = 0;i < 48; i  )
        {
            _TrianglesAll[i] = _Triangles[i];
            _TrianglesAll[i   48] = _Triangles[i]   8;
            _TrianglesAll[i   48 * 2] = _Triangles[i]   8 * 2;
            _TrianglesAll[i   48 * 3] = _Triangles[i]   8 * 3;
            _TrianglesAll[i   48 * 4] = _Triangles[i]   8 * 4;
            _TrianglesAll[i   48 * 5] = _Triangles[i]   8 * 5;
            _TrianglesAll[i   48 * 6] = _Triangles[i]   8 * 6;
            _TrianglesAll[i   48 * 7] = _Triangles[i]   8 * 7;
            _TrianglesAll[i   48 * 8] = _Triangles[i]   8 * 8;
            _TrianglesAll[i   48 * 9] = _Triangles[i]   8 * 9;
            _TrianglesAll[i   48 * 10] = _Triangles[i]   8 * 10;
        }
        Mesh _mesh = new Mesh();
        _mesh.Clear();
        _mesh.vertices = _VerticesAll;
        _mesh.triangles = _TrianglesAll;
        _mesh.RecalculateNormals();
        _Line.GetComponent<MeshFilter>().mesh = _mesh;
    }
    //贝塞尔曲线计算拐角
    private Vector3 CornerPoint(float k, Vector3 firstPot, Vector3 endPot, Vector3 midPot)
    {
        Vector3 a;
        a.x = k * k * (endPot.x - 2 * midPot.x   firstPot.x)   firstPot.x   2 * k * (midPot.x - firstPot.x);//公式为B(t)=(1-t)^2*v0 2*t*(1-t)*a0 t*t*v1 其中v0为起点 v1为终点 a为中间点 
        a.y = k * k * (endPot.y - 2 * midPot.y   firstPot.y)   firstPot.y   2 * k * (midPot.y - firstPot.y);
        a.z = k * k * (endPot.z - 2 * midPot.z   firstPot.z)   firstPot.z   2 * k * (midPot.z - firstPot.z);
        return a;
    }
}

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

标签: