Unity根据路径创建带有一定宽度的Mesh(面)

Unity根据路径创建带有一定宽度的Mesh(面)

我也要试着写一下,对自己做一个总结,第一次写哦,乱完了!
最近在做一个项目,需要根据路径点生成一定宽度一定厚度的mesh,我在网上找了很多生成mesh的博客,网上的都是mesh的基础生成,一些简单的mesh图形,但是我的需求是生成的mesh可以是任意角度,可能带有弧度,没有办法,只有自己写一个根据点位生成的Mesh,虽然我的数学基础不好,但是最后也勉勉强强能用。效果就是这样的效果
这是我项目中的效果,项目中是一个具有宽度和厚度的Mesh
Demo下载:https://download.csdn.net/download/zhaojunkuan/13153786
最开始的时候我只能绘制一些简单的图形,比如四边形五边形,但是现在需求明显仅仅有规则图形,根本达不到要求,再开始前,需要看一下“九德真君”的一篇博客,我也是看到他的博客受到的启发,https://blog.csdn.net/lzdidiv/article/details/53736068
首先有几种情况(手绘图,做项目的时候画的),需要根据这几种情况计算边上的顶点。
夹角小于90°
180°
两种特殊角度
一、普通情况
如下图,首先看一个通常的情况,两向量夹角小于90°的情况,先根据好理解的图计算几个顶点
此图是0-90堵或者是360°的情况
1、首先计算a,b向量并进行归一化,并对xz面做投影(所以可以用等腰三角形表示)

 Vector3 a =Vector3 .ProjectOnPlane((p1 - p0).normalized,Vector3.up);//计算两向量并进行归一化
 Vector3 b =Vector3 .ProjectOnPlane((p2 - p1).normalized,Vector3.up) ;
将两向量逆时针旋转90°,归一化,方便计算后续的偏移
 Vector3 AVerticle =new Vector3(-a.z,a.y,a.x).normalized;//逆时针旋转90° 与a向量垂直
  Vector3 BVerticle = new Vector3(-b.z,b.y,b.x).normalized;//逆时针旋转90°与b向量垂直

2、获取两向量的和向量dire,并获取逆时针旋转90°垂直向量DireVerticle

Vector3 dire = a + b;//a与b的和向量

Vector3 DireVerticle = new Vector3(-dire.z,dire.y,dire.x).normalized;//逆时针旋转90°,获得垂直向量

3、计算角度,获取a向量与和向量的垂直向量的夹角t,如上图

 float t = Mathf.Asin(Vector3.Dot(a, DireVerticle));//角度
  if (t < 0) t = -t;

4、计算偏移量,以计算p11和p22两个拐点

       float direOffset;
        if (Mathf.Cos(t) == 0)//a,b垂直
        {
            direOffset=offset;
        }
        else
        {
            direOffset = offset / Mathf.Cos(t);
        }
         Vector3 p11 = p1 + DireVerticle * direOffset;
        Vector3 p12=p1- DireVerticle * direOffset;

5、计算p01,p02,p21,p22点

Vector3 p01=Vector3.zero ;
Vector3 p02 = Vector3.zero;
Vector3 p21 = p2 + BVerticle * offset;
Vector3 p22 = p2 - BVerticle * offset;

在这里插入图片描述
根据第一个手绘图,分别做mesh顶点的添加和 triangles的添加
在每段进行绘制时,需要判定,将上一次的第二段作为此次的第一段,所以有顶点的删除,没次进行绘制时都要进行,
在这里插入图片描述
360°和0°做一样的处理
二、180°的时候是两段重叠,进行一次单独处理就好,根据图示和代码很好理解
三、90-180°和180-360°,这两段因为有一个大的拐角,两向量夹角比较大,需要判断一下p12(90-180°)或者p11(180-360°)在不在要绘制的图形内,如果不在直接连接首尾两定点就可以
在处理这两种情况的时候,需要对拐角进行一次贝塞尔处理,以保证顶点不会因为角度太小偏离太远在这里插入图片描述
和0-90°的时候计算原理基本一样,只有顶点位置不一样,一定要注意a和b逆时针旋转获取垂直向量,p01、p02、p11、p12、 p21、p22位置可能在三角形外边,也可能在三角形里边

代码写的也比较乱,黔驴技穷了
下边是三个个脚本的全部代码,地面要设置Layer层为9

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

public class Bezier
{
    //贝塞尔曲线公式:B(t)=P0*(1-t)^3 + 3*P1*t(1-t)^2 + 3*P2*t^2*(1-t) + P3*t^3 ,t属于[0,1].
    public static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
    {
        float u = 1 - t;
        float tt = t * t;
        float uu = u * u;
        float uuu = uu * u;
        float ttt = tt * t;

        Vector3 p = uuu * p0;
        p += 3 * uu * t * p1;
        p += 3 * u * tt * p2;
        p += ttt * p3;

        return p;
    }
    // B(t) = (1-t)^2P0+ 2 (1-t) tP1 + t^2P2
    public static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
    {
        float u = 1 - t;
        float tt = t * t;
        float uu = u * u;
        float ttt = tt * t;

        Vector3 p = uu * p0;
        p += 2 * u * t * p1;
        p += tt * p2;

        return p;
    }
}

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

/// <summary>
/// 在xz平面内
/// </summary>
public class CreatePathMeshData
{

    public List<Vector3> verticles;
    public List<int> triangles;
    public List<Vector2> uv;
    int beizierT ;
    public MeshCollider meshCollider;
    bool LastIsSpecialAngle;
    public CreatePathMeshData()
    {
        verticles = new List<Vector3>();
        triangles = new List<int>();
        uv = new List<Vector2>();
        beizierT = 60;
    }
   
    public void GetVeriticles(Vector3 p0, Vector3 p1, Vector3 p2, float Linewidth)
    {
        RemoveUnwantFace();
        float offset = Linewidth / 2;
        Vector3 a =Vector3 .ProjectOnPlane((p1 - p0).normalized,Vector3.up);
        Vector3 b =Vector3 .ProjectOnPlane((p2 - p1).normalized,Vector3.up) ;
        float angle = Angle_360(a, b);//计算从a顺时针到b的角度 范围360°
        Vector3 AVerticle =new Vector3(-a.z,a.y,a.x).normalized;//逆时针旋转90° 与a向量垂直
        Vector3 BVerticle = new Vector3(-b.z,b.y,b.x).normalized;//逆时针旋转90°与b向量垂直
        Vector3 dire = a + b;//a与b的和向量
        Vector3 p01=Vector3.zero ;
        Vector3 p02 = Vector3.zero;
        Vector3 p21 = p2 + BVerticle * offset;
        Vector3 p22 = p2 - BVerticle * offset;
        Vector3 DireVerticle = new Vector3(-dire.z,dire.y,dire.x).normalized;//逆时针旋转90°,获得垂直向量
        float t = Mathf.Asin(Vector3.Dot(a, DireVerticle));//角度
        if (t < 0) t = -t;
        float direOffset;
        if (Mathf.Cos(t) == 0)//a,b垂直
        {
            direOffset=offset;
        }
        else
        {
            direOffset = offset / Mathf.Cos(t);
        }
         Vector3 p11 = p1 + DireVerticle * direOffset;
        Vector3 p12=p1- DireVerticle * direOffset;
        #region 特殊角度
        if (angle >= 90 && angle < 180)
        {
            if (!Isinside(p12, p0, p1, p2))
            {
                LastIsSpecialAngle = true;
                Vector3 p1n = p1 + AVerticle * offset;
                Vector3 p1m = p1 + BVerticle * offset;
                Vector3 p1np1m = p1m - p1n;
                Vector3 middle = p1n + p1np1m / 2;
                Vector3 p1p1m = p1m - p1;
                float anglep1p1m = Vector3.Angle(p1p1m, p1np1m);
                float DisMiddlep1 = offset * Mathf.Sin(anglep1p1m * Mathf.PI / 180);
                p11 = p1 + (middle - p1).normalized * DisMiddlep1 * 2;
                Vector3[] ttt = new Vector3[beizierT + 1];
                for (int i = 0; i <= beizierT; i++)
                {
                    float t1 = i / (float)beizierT;
                    ttt[i] = Bezier.CalculateCubicBezierPoint(t1, p1n, p11, p1m);
                    Vector3 pos = ttt[i];
                }
                //上
                if (CreateLineMesh.FirstCreate)
                {
                    p01 = p0 + AVerticle * offset;
                    p02 = p0 - AVerticle * offset;
                    CreateLineMesh.FirstCreate = false;
                }
                else
                {
                    p01 = verticles[verticles.Count - 1];
                }
                Addverticles(p01);
                Addverticles(p1n);
                Addverticles(p0);
                Addverticles(p1);
                Addverticles(p2);
                AddTriangle(verticles.Count-5);
                AddTriangle(verticles.Count - 4);
                AddTriangle(verticles.Count - 2);
                AddTriangle(verticles.Count - 5);
                AddTriangle(verticles.Count - 2);
                AddTriangle(verticles.Count - 3);
                AddTriangle(verticles.Count - 3);
                AddTriangle(verticles.Count - 2);
                AddTriangle(verticles.Count - 1);
               
                int IndexOfP1 = verticles.Count - 2;//记录P1的位置
                int countTemp = verticles.Count;//记录贝塞尔前的顶点数量
                int tttNum = ttt.Length;
                for (int i = 0; i < tttNum; i++)
                {
                    Addverticles(ttt[i]);
                }
                for (int i = 0; i < tttNum-1; i++)
                {
                    AddTriangle(IndexOfP1);
                    AddTriangle(i+ countTemp);
                    AddTriangle(i+1+ countTemp);
                }
                Addverticles(p1);
                Addverticles(p1m);
                Addverticles(p2);
                Addverticles(p21);
                AddTriangle(verticles.Count-4);
                AddTriangle(verticles.Count - 3);
                AddTriangle(verticles.Count - 1);
                AddTriangle(verticles.Count - 4);
                AddTriangle(verticles.Count - 1);
                AddTriangle(verticles.Count - 2);
                return;
            }

        }
        else if (angle > 180 && angle <= 270)
        {
            if (!Isinside(p11, p0, p1, p2))
            {
                LastIsSpecialAngle = true;
                Vector3 p1n = p1 - AVerticle * offset;
                Vector3 p1m = p1 - BVerticle * offset;
                Vector3 p1np1m = p1m - p1n;
                Vector3 middle = p1n + p1np1m / 2;
                Vector3 p1p1m = p1m - p1;
                float anglep1p1m = Vector3.Angle(p1p1m, p1np1m);
                float DisMiddlep1 = offset * Mathf.Sin(anglep1p1m * Mathf.PI / 180);
                p12 = p1 + (middle - p1).normalized * DisMiddlep1 * 2;
                Vector3[] ttt = new Vector3[beizierT + 1];
                for (int i = 0; i <= beizierT; i++)
                {
                    float t1 = i / (float)beizierT;
                    ttt[i] = Bezier.CalculateCubicBezierPoint(t1, p1m, p12, p1n);
                    Vector3 pos = ttt[i];
                  
                }
                //上
                if (CreateLineMesh.FirstCreate)
                {
                    p01 = p0 + AVerticle * offset;
                    p02 = p0 - AVerticle * offset;
                    CreateLineMesh.FirstCreate = false;
                }
                else
                {
                    p02 = verticles[verticles.Count - 1];
                }
                Addverticles(p0);
                Addverticles(p1);
                Addverticles(p02);
                Addverticles(p1n);
                Addverticles(p2);
                AddTriangle(verticles.Count - 5);
                AddTriangle(verticles.Count - 4);
                AddTriangle(verticles.Count - 2);
                AddTriangle(verticles.Count - 5);
                AddTriangle(verticles.Count - 2);
                AddTriangle(verticles.Count - 3);
                AddTriangle(verticles.Count - 5);
                AddTriangle(verticles.Count - 1);
                AddTriangle(verticles.Count - 4);
                int IndexOfP1 = verticles.Count - 4;//记录P1的位置
                int countTemp = verticles.Count;//记录贝塞尔前的顶点数量
                int tttNum = ttt.Length;
                for (int i = 0; i < ttt.Length; i++)
                {
                    Addverticles(ttt[i]);
                }
                for (int i = 0; i < tttNum - 1; i++)
                {
                    AddTriangle(IndexOfP1);
                    AddTriangle(i + countTemp);
                    AddTriangle(i + 1 + countTemp);
                }
                Addverticles(p1);
                Addverticles(p1m);
                Addverticles(p2);
                Addverticles(p22);
                AddTriangle(verticles.Count - 4);
                AddTriangle(verticles.Count - 2);
                AddTriangle(verticles.Count - 1);
                AddTriangle(verticles.Count - 4);
                AddTriangle(verticles.Count - 1);
                AddTriangle(verticles.Count - 3);
                return;
            }
        }
        else if (angle == 180)
        {
            if (CreateLineMesh.FirstCreate)
            {
                p01 = p0 + AVerticle * offset;
                p02 = p0 - AVerticle * offset;
                CreateLineMesh.FirstCreate = false;
            }
            else
            {
                p01 = verticles[verticles.Count - 3];
                if (LastIsSpecialAngle)
                {
                    p01 = verticles[verticles.Count - 2];
                    LastIsSpecialAngle = false;
                }
                p02 = verticles[verticles.Count - 1];
            }
            //上
            Addverticles(p01);
            Addverticles(p11);
            Addverticles(p02);
            Addverticles(p11);
            Addverticles(p22);
            Addverticles(p21);
            int verticleCount = verticles.Count;
            AddTriangle(verticleCount - 6);
            AddTriangle(verticleCount - 5);
            AddTriangle(verticleCount - 3);
            AddTriangle(verticleCount - 6);
            AddTriangle(verticleCount - 3);
            AddTriangle(verticleCount - 4);
            AddTriangle(verticleCount - 2);
            AddTriangle(verticleCount - 5);
            AddTriangle(verticleCount - 3);
            AddTriangle(verticleCount - 2);
            AddTriangle(verticleCount - 3);
            AddTriangle(verticleCount - 1);
            return;
        }
        #endregion
        #region 小于90度
        if (CreateLineMesh.FirstCreate)
        {
            p01 = p0 + AVerticle * offset;//根据宽度计算周围平移顶点
            p02 = p0 - AVerticle * offset;
            CreateLineMesh.FirstCreate = false;
        }
        else
        {
            p01 = verticles[verticles.Count - 3];//同一个的Mesh的情况下,起点要取上一次的中间两端点
            if (LastIsSpecialAngle)
            {
                p01 = verticles[verticles.Count - 2];
                LastIsSpecialAngle = false;
            }
            p02 = verticles[verticles.Count - 1];
            
        }
        //上表面
        Addverticles(p01);
        Addverticles(p11);
        Addverticles(p02);
        Addverticles(p12);
        Addverticles(p21);
        Addverticles(p22);
        int count = verticles.Count;
        AddTriangle(count-6);
        AddTriangle(count - 5);
        AddTriangle(count - 3);
        AddTriangle(count - 6);
        AddTriangle(count - 3);
        AddTriangle(count - 4);
        AddTriangle(count - 5);
        AddTriangle(count - 2);
        AddTriangle(count - 1);
        AddTriangle(count - 5);
        AddTriangle(count - 1);
        AddTriangle(count - 3);
        #endregion

    }
    void Addverticles(Vector3 p)
    {
        verticles.Add(p); 
    }
    void AddTriangle(int index)
    {
        triangles.Add(index);
    }
    void RemoveUnwantFace()
    {
        if (verticles.Count < 6) return;
        verticles.RemoveAt(verticles.Count-1);
        verticles.RemoveAt(verticles.Count - 1);
        for (int i = 0; i < 6; i++)
        {
            triangles.Remove(triangles.Count-1);
        }
    }
    //判断一个点在三角形内还是外
    bool Isinside(Vector3 point, Vector3 a, Vector3 b, Vector3 c)
    {
        Vector3 pa = a - point;
        Vector3 pb = b - point;
        Vector3 pc = c - point;
        Vector3 pab = Vector3.Cross(pa, pb);
        Vector3 pbc = Vector3.Cross(pb, pc);
        Vector3 pca = Vector3.Cross(pc, pa);

        float d1 = Vector3.Dot(pab, pbc);
        float d2 = Vector3.Dot(pab, pca);
        float d3 = Vector3.Dot(pbc, pca);

        if (d1 > 0 && d2 > 0 && d3 > 0) return true;
        return false;
    }
    //判断逆时针旋转两向量夹角0-360度
    public float Angle_360(Vector3 from_, Vector3 to_)
    {
        Vector3 v3 = Vector3.Cross(from_, to_);
        if (v3.y > 0)
            return Vector3.Angle(from_, to_);
        else
            return 360 - Vector3.Angle(from_, to_);
    }
    /// <summary>
    /// 获取两向量的法线,左手坐标系,从a-b
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <returns></returns>
    public static Vector3 GetNormalVector3(Vector3 a, Vector3 b)
    {
        return Vector3.Cross(a, b).normalized;
    }
    /// <summary>
    /// 绕轴旋转
    /// </summary>
    /// <param name="position">点</param>
    /// <param name="center">绕的中心点</param>
    /// <param name="axis"></param>
    /// <param name="angle">angle>0顺时针</param>
    /// <returns></returns>
    public static Vector3 RotateRound(Vector3 position, Vector3 center, Vector3 axis, float angle)
    {
        Vector3 point = Quaternion.AngleAxis(angle, axis) * (position - center);
        Vector3 resultVec3 = center + point;
        return resultVec3;
    }
    #region 暂时不用
    / <summary>
    / 判断一个点在三角形内
    / </summary>
    / <param name="point"></param>
    / <param name="a"></param>
    / <param name="b"></param>
    / <param name="c"></param>
    / <returns></returns>
    //bool Isinside2(Vector3 point, Vector3 a, Vector3 b, Vector3 c)
    //{
    //    float s1 = Area(a, b, c);
    //    float s2 = Area(point, a, b);
    //    float s3 = Area(point, a, c);
    //    float s4 = Area(point, b, c);
    //    //需考虑边界情况
    //    if (s2 == 0 || s3 == 0 || s4 == 0) return false;
    //    //不能用“==”判断两个浮点类型的值是否相等,可使用如下,差小于等于某个精度值即可。
    //    if (s1 - (s2 + s3 + s4) <= 0.00001f) return true;
    //    return false;
    //}
    //float Area(Vector3 a, Vector3 b, Vector3 c)//计算三角形面积
    //{
    //    //海伦公式:p=(a+b+c)/2; S = √[p(p-a)(p-b)(p-c)] //这里a,b,c代表边长
    //    float dab = Vector3.Distance(a, b);
    //    float dac = Vector3.Distance(a, c);
    //    float dbc = Vector3.Distance(b, c);
    //    float half = (dab + dac + dbc) / 2;
    //    return Mathf.Sqrt(half * (half - dab) * (half - dac) * (half - dbc));
    //}
    #endregion
}

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

public class CreateLineMesh : MonoBehaviour {
    public static bool FirstCreate;
    public Material material;
    GameObject mesh;
    MeshCollider meshCollider;
    MeshFilter meshFilter;
    MeshRenderer meshRenderer;
    CreatePathMeshData meshData;
    bool down;
    Vector3 startPos;
    Vector3 lastPOs;
    public float Width=0.5f;
    public float Thickness=0.2f;
    public void CreateUpdateMeshPos()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Create();
            FirstCreate = true;
            Vector3 _pos = SendRay();
            startPos = lastPOs = _pos;
            down = true;
        }
        if (down)
        {
            Vector3 pos = SendRay();
            if (pos != lastPOs)
            {
                float dis = Vector3.Distance(pos, lastPOs);
                if (dis >= 0.1f)
                {
                    Vector3 p0 = startPos;
                    Vector3 p1 = lastPOs;
                    Vector3 p2 = pos;
                    if (p1 == p0 || p1 == p2)
                    {
                        p1 = (p0 + p2) / 2;
                    }
                    if (meshData.verticles.Count > 60000)//一个Mesh顶点数量限制在65000
                        Create();
                    meshData.GetVeriticles(p0, p1, p2, Width);
                    GetMesh();
                    startPos = lastPOs;
                    lastPOs = pos;
                }
            }
        }
        if (Input.GetMouseButtonUp(0))
        {
            down = false;
        }
       
    }
    void GetMesh()
    {
        meshFilter.mesh.vertices = meshData.verticles.ToArray();
        meshFilter.mesh.triangles = meshData.triangles.ToArray();
        if (meshData.meshCollider == null)
            meshData.meshCollider = mesh.AddComponent<MeshCollider>();
        meshData.meshCollider.enabled = false;
        meshData.meshCollider.enabled = true;
    }
    void Create()
    {
        mesh = new GameObject("Mesh", typeof(MeshFilter), typeof(MeshRenderer));
        meshFilter = mesh.GetComponent<MeshFilter>();
        meshRenderer = mesh.GetComponent<MeshRenderer>();
        meshRenderer.material = material;
        meshData = new CreatePathMeshData();
    }
    Vector3 SendRay()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        int layer = 1 << 9;
        if (Physics.Raycast(ray, out hit,1000, layer))
        {
            Vector3 pos = hit.point;
            pos.y += 1;
            return pos;
        }
        return Vector3.zero;

    }
   
	// Update is called once per frame
	void Update ()
    {
        CreateUpdateMeshPos();

    }
}

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值