问题背景

上次写了动态绘制立方体,这最近又来了新功能,绘制圆柱(风筒),要求是给了很多节点,根据节点去动态绘制风筒,风筒就是圆柱连接而成的,可以理解为管道,还有就是拐角处注意倒角,圆润过度过来。

实现原理

动态绘制圆柱mesh,注意,圆柱的mesh绘制远比立方体复杂得多,上节阐述过基本mesh创建立方体,有兴趣可以去看看https://www.cnblogs.com/answer-yj/p/11231247.html,顶点以及倒角需要你自己去插值出来,其中倒角是使用贝塞尔曲线插值过度出来的。

实现步骤

1.顶点坐标

2.uv坐标

3.构建索引

4.构建实体

具体过程

1.准备顶点数据

构建圆柱应当理解为这是多个圆插值出来的,首先设置圆的顶点坐标,首先说第一种方式,我先前考虑,在世界坐标空间中,创建一个Gamobject通过它的坐标作为圆上顶点坐标,不从本地创建坐标了(因为涉及坐标转换,矩阵变换),所以我用了这种方式

第一种方式:

具体代码:

    /// <summary>
    /// 设置起中间点断面顶点数据
    /// </summary>
    /// <param name="currPos">当前点坐标</param>
    /// <param name="curDirection">朝向</param>
    /// <param name="radius">半径</param>
    private void SetSectionVertexData(Vector3 currPos, Vector3 curDirection, float radius,float u)
    {
        // 首先计算导线点相对场景中心点的偏移坐标
        Vector3 position = currPos - center;

        // 计算direction、right、up向量(注:Unity3d中使用左手坐标系)
        Vector3 direction = curDirection.normalized;
        Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;
        Vector3 up = Vector3.Cross(direction, right).normalized;

        angleStep = 360.0f / (float)devide;
        GameObject gameObject = new GameObject();
        gameObject.transform.position = position + radius * right;for (int i = 0; i < 360.0f; i += (int)angleStep)
        {
            gameObject.transform.RotateAround(currPos, direction, angleStep);
            vertices.Add(gameObject.transform.position);
        }
        Destroy(gameObject);
    }

代码中direction是方向向量(上一点指向下一点坐标),构建顶点也需要顺序,我这是在右侧90度逆时针创建的,gameObject.transform.position = position + radius * right;这是起点顶点,一共十个顶点。

第二种方式:

我已经用第一种方式功能都做完了,创建好了,完事领导说我们风筒可能要在子线程创建,哇,一瞬间透心凉,GameObject就意味着淘汰了,矩阵变换是躲不开了。从本地创建圆,这句话的意思是指模型本地坐标,即左手坐标系(0,0,0)点 找一点为(0,0,0)点去创建。

具体代码:

 1     /// <summary>
 2         /// 设置起点圆顶点数据
 3         /// </summary>
 4         /// <param name="startPos">起点坐标</param>
 5         /// <param name="nextPos">下一点坐标</param>
 6         private void SetStartVertexData(Vector3 startPos, Vector3 nextPos)
 7         {
 8             // 首先计算导线点相对场景中心点的偏移坐标
 9             Vector3 position = startPos - center;
10             Vector3 nextPosition = nextPos - center;
11 
12             // 计算direction、right、up向量(注:Unity3d中使用左手坐标系)
13             Vector3 direction = (nextPosition - position).normalized;//z轴
14             Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;//x轴,右
15             Vector3 up = Vector3.Cross(direction, right).normalized;//x和z得到up
16             // 设起点圆定点数据
17             SetSectionVertexData(position, direction, up, right, uvPosU);
18         }
19 
20 
21   /// <summary>
22         /// 设置断面顶点数据
23         /// </summary>
24         /// <param name="currentPos">当前点坐标</param>
25         /// <param name="direction">朝向</param>
26         private void SetSectionVertexData(Vector3 currentPos, Vector3 direction, Vector3 up, Vector3 right, float u)
27         {
28             // 构建旋转矩阵
29             Matrix4x4 m = Utils.MakeBasis(right, up, direction);
30             for (float i = 0f; i < 360.0f; i += 36.0f)
31             {
32                 // 计算顶点
33                 float rad = Mathf.Deg2Rad * i;
34                 Vector3 targetPos = currentPos + m.MultiplyPoint3x4(new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0.0f));
35                 // 计算V坐标
36                 float v = ((baseValue * 36.0f) / 360.0f);
37                 // 保存顶点坐标&纹理坐标
38                 vertices.Add(targetPos);
39             }
40         }

这里解释下,过程是这样的,在本地坐标构建圆,通过旋转矩阵变换到世界坐标。Vector3 direction = (nextPosition - position).normalized;,一般将指向方向向量为坐标系Z轴,假设y轴(0,1,0)为Up方向,那么Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;Z轴与假设Y轴的叉乘得到的是X轴的方向向量(即right方向),垂直与zy平面,但是接下来求方向向量(Z轴)与X轴的叉乘,指定是我们要的Y方向,因为确定的Z和X(垂直XZ平面)。

Utils.MakeBasis具体逻辑如下:

 1 /// <summary>
 2         /// 通过坐标轴构建矩阵
 3         /// </summary>
 4         /// <param name="xAxis">x轴</param>
 5         /// <param name="yAxis">y轴</param>
 6         /// <param name="zAxis">z轴</param>
 7         /// <returns>4x4矩阵</returns>
 8         public static Matrix4x4 MakeBasis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
 9         {
10             Matrix4x4 mat = Matrix4x4.identity;
11 
12             mat.m00 = xAxis.x;
13             mat.m10 = xAxis.y;
14             mat.m20 = xAxis.z;
15 
16             mat.m01 = yAxis.x;
17             mat.m11 = yAxis.y;
18             mat.m21 = yAxis.z;
19 
20             mat.m02 = zAxis.x;
21             mat.m12 = zAxis.y;
22             mat.m22 = zAxis.z;
23 
24             return mat;
25         }

矩阵就不解释了,自己看吧。Matrix4x4 m = Utils.MakeBasis(right, up, direction);这是在得到所构建节点的坐标系即坐标走向。因为要把构建的圆放到该点坐标下。m.MultiplyPoint3x4是将构建圆的顶点转换到当前点坐标系下,new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0.0f)圆上点坐标XY平面构建元,Z为0,到这里圆心始终是(0,0,0),那么将这个圆放到目标点下需要再加上目标点在当下坐标系的偏移量(即坐标值)。所以加currentPos。

到这就将两种设置顶点坐标方式说完了。还有就是拐角处圆的构建,需要用贝塞尔曲线差值出来。

代码:

 1         /// <summary>
 2         /// 设置中间点顶点数据
 3         /// </summary>
 4         /// <param name="currentPos"></param>
 5         /// <param name="prevPos"></param>
 6         /// <param name="nextPos"></param>
 7         private void SetPrepareVertexData(Vector3 currentPos, Vector3 prevPos, Vector3 nextPos)
 8         {
 9             // 首先计算导线点相对场景中心点的偏移坐标
10             Vector3 prevPosition = prevPos - center;
11             Vector3 position = currentPos - center;
12             Vector3 anitherposition = nextPos - center;
13 
14             // 计算前一段巷道的direction、right、up向量(注:Unity3d中使用左手坐标系)
15             Vector3 prevDirection = (position - prevPosition).normalized;
16             Vector3 prevRight = Vector3.Cross(Vector3.up, prevDirection).normalized;
17             Vector3 prevUp = Vector3.Cross(prevDirection, prevRight).normalized;
18 
19             // 计算后一段巷道的direction、right、up向量(注:Unity3d中使用左手坐标系)
20             Vector3 anitherDirection = (anitherposition - position).normalized;
21             Vector3 anitherlRight = Vector3.Cross(Vector3.up, anitherDirection).normalized;
22             Vector3 anitherUp = Vector3.Cross(anitherDirection, anitherlRight).normalized;
23 
24             float angle = Vector3.Angle(-prevDirection, anitherDirection);
25 
26             if (angle >= 179.0)
27             {
28                 //生成断面数据不倒角处理
29                 uvPosU += (GetPointDistance(position, prevPosition) / textureSizeL);
30                 SetSectionVertexData(position, prevDirection, prevUp, prevRight, uvPosU);
31                 return;
32             }
33             //倒角处理
34 
35             //前后两段风筒长度
36             float PrevLength = Vector3.Distance(position, prevPosition);
37             float anithorLength = Vector3.Distance(position, anitherposition);
38 
39             indentationValue = PrevLength > anithorLength ? (anithorLength * 0.25f) : (PrevLength * 0.25f);//缩进为短风筒的1/4
40 
41             // 计算缩进后的位置
42             Vector3 prevEnd = position - prevDirection * indentationValue;//
43             Vector3 behindStart = position + anitherDirection * indentationValue;//
44 
45             uvPosU += (GetPointDistance(prevPosition, prevEnd) / textureSizeL);
46             // 生成前段结束断面顶点数据
47             SetSectionVertexData(prevEnd, prevDirection, prevUp, prevRight, uvPosU);
48 
49             // 生成中间倒角断面顶点数据
50             //插值0-1
51             float timer = 0.1f;
52             Vector3 prevpos = prevEnd;
53             for (float i = 1.0f; i <= 9.0f; i++)
54             {
55                 Vector3 pos = Utils.CalculateCubicBezierPoint(timer, prevEnd, position, behindStart);
56                 // 计算断面方向
57                 Vector3 direction = (pos - prevpos).normalized;
58                 Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;
59                 Vector3 up = Vector3.Cross(direction, right).normalized;
60                 uvPosU += (GetPointDistance(pos, prevpos) / textureSizeL);
61                 // 生成断面顶点数据
62                 SetSectionVertexData(pos, direction, up, right, uvPosU);
63                 //递增插值时间
64                 timer += 0.1f;
65                 //更新前一个点坐标
66                 prevpos = pos;
67             }
68             // 生成后段起始断面顶点数据
69             SetSectionVertexData(behindStart, anitherDirection, anitherUp, anitherlRight, ++uvPosU);
70         }

1到9插10个圆。需要找拐角处前后两个点分别为开始和结束插值点,Utils.CalculateCubicBezierPoint(timer, prevEnd, position, behindStart);timer为每次插值(可理解为时刻)。此函数(三阶贝塞尔曲线)具体逻辑:

 1 /// <summary>
 2         /// 计算三阶贝塞尔曲线点坐标
 3         /// </summary>
 4         /// <param name="t">时刻(0.0~1.0)</param>
 5         /// <param name="p0">起点坐标</param>
 6         /// <param name="p1">中间点坐标</param>
 7         /// <param name="p2">终点坐标</param>
 8         /// <returns>坐标点</returns>
 9         public static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
10         {
11             float u = 1.0f - t;
12             float tt = t * t;
13             float uu = u * u;
14 
15             return (p0 * uu + p1 * 2 * u * t + p2 * tt);
16         }

这样准备顶点坐标就结束了。

2.uv坐标

UV坐标是与顶点一一对应的,所以在创建顶点时最好一起计算出来,我这里的要求是,上下面贴想同纹理,而且是重复帖,所以V向基本就贴1次上下两面贴,就要从圆上10个点分成两半,或者按角度角度分,一般从0-1后一半从1-0这样就可以实现,重点是U方向坐标值的计算,每个横向重复帖很多次纹理,需要根据贴图比例以及长度去设置,但是U的值应该是累加的。一次从起点到终点贴。知道这一点就好办了。

代码:

//横向代码,U是累加的,根据圆柱长度去设置计算。
 uvPosU += (GetPointDistance(pos, prevpos) / textureSizeL);

//V方向按上述规律贴
   /// <summary>
        /// 设置断面顶点数据
        /// </summary>
        /// <param name="currentPos">当前点坐标</param>
        /// <param name="direction">朝向</param>
        private void SetSectionVertexData(Vector3 currentPos, Vector3 direction, Vector3 up, Vector3 right, float u)
        {
            // 构建旋转矩阵
            Matrix4x4 m = Utils.MakeBasis(right, up, direction);
            int baseValue = 2;
            for (float i = 0f; i < 360.0f; i += 36.0f)
            {
                // 计算顶点
                float rad = Mathf.Deg2Rad * i;
                Vector3 targetPos = currentPos + m.MultiplyPoint3x4(new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0.0f));
                // 计算V坐标
                float v = ((baseValue * 36.0f) / 360.0f);
                // 保存顶点坐标&纹理坐标
                vertices.Add(targetPos);
                uvs.Add(new Vector2(u, v));
                if (i > 108)
                {
                    baseValue -= 2;
                }
                else
                {
                    baseValue += 2;
                }
            }
        }

这float v = ((baseValue * 36.0f) / 360.0f);算法需要按要求设置。

3.构建索引

构建索引就要按照定点顺序去构建,连接圆与圆之间的曲面,索引值也是累加的比如这里十个点,第一个圆是0-9/第二个10-19/以此类推。

 1   /// <summary>
 2         /// 设置索引数据
 3         /// </summary>
 4         private void SetIndexData()
 5         {
 6             int faceCount = (vertices.Count / devide) - 1;
 7             int baseValue;
 8             for (int i = 0; i < faceCount; i++)
 9             {
10                 baseValue = 0;
11                 for (int j = 1; j <= 10; j++)
12                 {
13                     if (j < 10)
14                     {
15                         triangles.Add(i * 10 + baseValue);
16                         triangles.Add(i * 10 + 11 + baseValue);
17                         triangles.Add(i * 10 + baseValue + 10);
18 
19                         triangles.Add(i * 10 + baseValue);
20                         triangles.Add(i * 10 + baseValue + 1);
21                         triangles.Add(i * 10 + 11 + baseValue);
22                     }
23                     else
24                     {
25                         triangles.Add(i * 10 + baseValue);
26                         triangles.Add(i * 10 + 10);
27                         triangles.Add(i * 10 + baseValue + 10);
28 
29                         triangles.Add(i * 10 + baseValue);
30                         triangles.Add(i * 10);
31                         triangles.Add(i * 10 + 10);
32                     }
33                     baseValue++;
34                 }
35             }
36         }

注意构建顺序不然顺逆时针容易反了。

4.构建实体

这里解释最后一步了,也是最简单的一步了,不解释上代码:

 1   /// <summary>
 2         /// 绘制风筒实体
 3         /// </summary>
 4         /// <param name="posList">风筒节点数据集合</param>
 5         /// <param name="radius">风筒半径</param>
 6         public void DrawAirDuct(List<Vector3> posList, float radius)
 7         {
 8             this.radius = radius;
 9             PrepareVertexData(posList);
10             SetIndexData();
11             Mesh mesh = new Mesh
12             {
13                 vertices = vertices.ToArray(),
14                 triangles = triangles.ToArray(),
15                 uv = uvs.ToArray(),
16             };
17             mesh.RecalculateNormals();
18             GameObject airDuctObj = new GameObject(name);
19             airDuctObj.AddComponent<MeshFilter>().mesh = mesh;
20             airDuctObj.AddComponent<MeshRenderer>().material = new Material(Resources.Load<Material>("Materials/Mine/LanewayColor"));
21 
22             // 添加碰撞器MeshCollider
23             airDuctObj.AddComponent<MeshCollider>();
24         }

切记别忘了重新计算法线mesh.RecalculateNormals();这才出预期效果。

完整代码:

  1 using System;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 
  5 namespace Tx3d.Framework
  6 {
  7     /// <summary>
  8     /// 绘制风筒实体
  9     /// </summary>
 10     public class AirDuct : Entity
 11     {
 12         #region Fields
 13 
 14         //计算圆角的缩进值
 15         private float indentationValue;
 16 
 17         //圆划分为多少等份
 18         private int devide = 10;
 19 
 20         //中心点坐标
 21         private Vector3 center = Vector3.zero;
 22 
 23         //顶点坐标集合
 24         private List<Vector3> vertices = new List<Vector3>();
 25 
 26         //uv坐标集合
 27         private List<Vector2> uvs = new List<Vector2>();
 28 
 29         //索引集合
 30         private List<int> triangles = new List<int>();
 31 
 32         //半径
 33         private float radius;
 34 
 35         //贴图长度缩放尺寸
 36         private float textureSizeL = 10.24f;
 37 
 38         //uv坐标的u坐标值
 39         private float uvPosU = 0;
 40 
 41         #endregion
 42 
 43         #region Public Methods
 44 
 45         /// <summary>
 46         /// 风筒实体
 47         /// </summary>
 48         /// <param name="name">实体名</param>
 49         public AirDuct(string name) : base(name)
 50         {
 51 
 52         }
 53 
 54         /// <summary>
 55         /// 绘制风筒实体
 56         /// </summary>
 57         /// <param name="posList">风筒节点数据集合</param>
 58         /// <param name="radius">风筒半径</param>
 59         public void DrawAirDuct(List<Vector3> posList, float radius)
 60         {
 61             this.radius = radius;
 62             PrepareVertexData(posList);
 63             SetIndexData();
 64             Mesh mesh = new Mesh
 65             {
 66                 vertices = vertices.ToArray(),
 67                 triangles = triangles.ToArray(),
 68                 uv = uvs.ToArray(),
 69             };
 70             mesh.RecalculateNormals();
 71             GameObject airDuctObj = new GameObject(name);
 72             airDuctObj.AddComponent<MeshFilter>().mesh = mesh;
 73             airDuctObj.AddComponent<MeshRenderer>().material = new Material(Resources.Load<Material>("Materials/Mine/LanewayColor"));
 74 
 75             // 添加碰撞器MeshCollider
 76             airDuctObj.AddComponent<MeshCollider>();
 77         }
 78 
 79         /// <summary>
 80         /// 释放风筒实体
 81         /// </summary>
 82         public override void Dispose()
 83         {
 84 
 85         }
 86 
 87         /// <summary>
 88         /// 射线查询
 89         /// </summary>
 90         /// <param name="ray">射线</param>
 91         /// <param name="hit">拾取结果</param>
 92         /// <param name="maxDistance">最大拾取距离</param>
 93         /// <returns>拾取成功返回true,否则返回false</returns>
 94         public override bool Raycast(Ray ray, out RaycastHit hit, float maxDistance)
 95         {
 96             var collider = gameObject.GetComponent<MeshCollider>();
 97             return collider.Raycast(ray, out hit, maxDistance);
 98         }
 99 
100         /// <summary>
101         /// 体积查询 <see cref="Entity.VolumeQuery(PlaneBoundedVolume)"/>
102         /// </summary>
103         /// <param name="volume">查询体</param>
104         /// <returns>查询成功返回true,否则返回false</returns>
105         public override bool VolumeQuery(PlaneBoundedVolume volume)
106         {
107             throw new NotImplementedException();
108         }
109 
110         #endregion
111 
112         #region Private Methods
113 
114         /// <summary>
115         /// 准备顶点数据
116         /// </summary>
117         private void PrepareVertexData(List<Vector3> posList)
118         {
119             for (int i = 0; i < posList.Count; i++)
120             {   
121                 //起点圆面
122                 if (i == 0)
123                 {
124                     SetStartVertexData(posList[i], posList[i + 1]);
125                 }
126                 else if (i != posList.Count - 1)
127                 {
128                     ////中间点(求缩进点设置圆角)
129                     SetPrepareVertexData(posList[i], posList[i - 1], posList[i + 1]);
130                 }
131                 else
132                 {
133                     //终点圆面
134                     SetEndVertexData(posList[i], posList[i - 1]);
135                 }
136             }
137         }
138 
139         /// <summary>
140         /// 设置起点圆顶点数据
141         /// </summary>
142         /// <param name="startPos">起点坐标</param>
143         /// <param name="nextPos">下一点坐标</param>
144         private void SetStartVertexData(Vector3 startPos, Vector3 nextPos)
145         {
146             // 首先计算导线点相对场景中心点的偏移坐标
147             Vector3 position = startPos - center;
148             Vector3 nextPosition = nextPos - center;
149 
150             // 计算direction、right、up向量(注:Unity3d中使用左手坐标系)
151             Vector3 direction = (nextPosition - position).normalized;//z轴
152             Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;//x轴,右
153             Vector3 up = Vector3.Cross(direction, right).normalized;//x和z得到up
154             // 设起点圆定点数据
155             SetSectionVertexData(position, direction, up, right, uvPosU);
156         }
157 
158         /// <summary>
159         /// 设置终点圆顶点数据
160         /// </summary>
161         /// <param name="endPos">终点坐标</param>
162         /// <param name="previousPos">上一点坐标</param>
163         private void SetEndVertexData(Vector3 endPos, Vector3 previousPos)
164         {
165             // 首先计算导线点相对场景中心点的偏移坐标
166             Vector3 position = endPos - center;
167             Vector3 PreviousPosition = previousPos - center;
168 
169             // 计算direction、right、up向量(注:Unity3d中使用左手坐标系)
170             Vector3 direction = (position - PreviousPosition).normalized;//指向下一点(结束)方向向量
171             Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;
172             Vector3 up = Vector3.Cross(direction, right).normalized;
173             //计算U值
174             uvPosU += (GetPointDistance(position, PreviousPosition) / textureSizeL);
175             SetSectionVertexData(position, direction, up, right, uvPosU);
176         }
177 
178         /// <summary>
179         /// 设置断面顶点数据
180         /// </summary>
181         /// <param name="currentPos">当前点坐标</param>
182         /// <param name="direction">朝向</param>
183         private void SetSectionVertexData(Vector3 currentPos, Vector3 direction, Vector3 up, Vector3 right, float u)
184         {
185             // 构建旋转矩阵
186             Matrix4x4 m = Utils.MakeBasis(right, up, direction);
187             int baseValue = 2;
188             for (float i = 0f; i < 360.0f; i += 36.0f)
189             {
190                 // 计算顶点
191                 float rad = Mathf.Deg2Rad * i;
192                 Vector3 targetPos = currentPos + m.MultiplyPoint3x4(new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0.0f));
193                 // 计算V坐标
194                 float v = ((baseValue * 36.0f) / 360.0f);
195                 // 保存顶点坐标&纹理坐标
196                 vertices.Add(targetPos);
197                 uvs.Add(new Vector2(u, v));
198                 if (i > 108)
199                 {
200                     baseValue -= 2;
201                 }
202                 else
203                 {
204                     baseValue += 2;
205                 }
206             }
207         }
208 
209         /// <summary>
210         /// 设置中间点顶点数据
211         /// </summary>
212         /// <param name="currentPos"></param>
213         /// <param name="prevPos"></param>
214         /// <param name="nextPos"></param>
215         private void SetPrepareVertexData(Vector3 currentPos, Vector3 prevPos, Vector3 nextPos)
216         {
217             // 首先计算导线点相对场景中心点的偏移坐标
218             Vector3 prevPosition = prevPos - center;
219             Vector3 position = currentPos - center;
220             Vector3 anitherposition = nextPos - center;
221 
222             // 计算前一段巷道的direction、right、up向量(注:Unity3d中使用左手坐标系)
223             Vector3 prevDirection = (position - prevPosition).normalized;
224             Vector3 prevRight = Vector3.Cross(Vector3.up, prevDirection).normalized;
225             Vector3 prevUp = Vector3.Cross(prevDirection, prevRight).normalized;
226 
227             // 计算后一段巷道的direction、right、up向量(注:Unity3d中使用左手坐标系)
228             Vector3 anitherDirection = (anitherposition - position).normalized;
229             Vector3 anitherlRight = Vector3.Cross(Vector3.up, anitherDirection).normalized;
230             Vector3 anitherUp = Vector3.Cross(anitherDirection, anitherlRight).normalized;
231 
232             float angle = Vector3.Angle(-prevDirection, anitherDirection);
233 
234             if (angle >= 179.0)
235             {
236                 //生成断面数据不倒角处理
237                 uvPosU += (GetPointDistance(position, prevPosition) / textureSizeL);
238                 SetSectionVertexData(position, prevDirection, prevUp, prevRight, uvPosU);
239                 return;
240             }
241             //倒角处理
242 
243             //前后两段风筒长度
244             float PrevLength = Vector3.Distance(position, prevPosition);
245             float anithorLength = Vector3.Distance(position, anitherposition);
246 
247             indentationValue = PrevLength > anithorLength ? (anithorLength * 0.25f) : (PrevLength * 0.25f);//缩进为短风筒的1/4
248 
249             // 计算缩进后的位置
250             Vector3 prevEnd = position - prevDirection * indentationValue;//
251             Vector3 behindStart = position + anitherDirection * indentationValue;//
252 
253             uvPosU += (GetPointDistance(prevPosition, prevEnd) / textureSizeL);
254             // 生成前段结束断面顶点数据
255             SetSectionVertexData(prevEnd, prevDirection, prevUp, prevRight, uvPosU);
256 
257             // 生成中间倒角断面顶点数据
258             //插值0-1
259             float timer = 0.1f;
260             Vector3 prevpos = prevEnd;
261             for (float i = 1.0f; i <= 9.0f; i++)
262             {
263                 Vector3 pos = Utils.CalculateCubicBezierPoint(timer, prevEnd, position, behindStart);
264                 // 计算断面方向
265                 Vector3 direction = (pos - prevpos).normalized;
266                 Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;
267                 Vector3 up = Vector3.Cross(direction, right).normalized;
268                 uvPosU += (GetPointDistance(pos, prevpos) / textureSizeL);
269                 // 生成断面顶点数据
270                 SetSectionVertexData(pos, direction, up, right, uvPosU);
271                 //递增插值时间
272                 timer += 0.1f;
273                 //更新前一个点坐标
274                 prevpos = pos;
275             }
276             // 生成后段起始断面顶点数据
277             SetSectionVertexData(behindStart, anitherDirection, anitherUp, anitherlRight, ++uvPosU);
278         }
279 
280         /// <summary>
281         /// 设置索引数据
282         /// </summary>
283         private void SetIndexData()
284         {
285             int faceCount = (vertices.Count / devide) - 1;
286             int baseValue;
287             for (int i = 0; i < faceCount; i++)
288             {
289                 baseValue = 0;
290                 for (int j = 1; j <= 10; j++)
291                 {
292                     if (j < 10)
293                     {
294                         triangles.Add(i * 10 + baseValue);
295                         triangles.Add(i * 10 + 11 + baseValue);
296                         triangles.Add(i * 10 + baseValue + 10);
297 
298                         triangles.Add(i * 10 + baseValue);
299                         triangles.Add(i * 10 + baseValue + 1);
300                         triangles.Add(i * 10 + 11 + baseValue);
301                     }
302                     else
303                     {
304                         triangles.Add(i * 10 + baseValue);
305                         triangles.Add(i * 10 + 10);
306                         triangles.Add(i * 10 + baseValue + 10);
307 
308                         triangles.Add(i * 10 + baseValue);
309                         triangles.Add(i * 10);
310                         triangles.Add(i * 10 + 10);
311                     }
312                     baseValue++;
313                 }
314             }
315         }
316 
317         /// <summary>
318         /// 求两点距离
319         /// </summary>
320         /// <param name="prevPos">前一点坐标</param>
321         /// <param name="nextPos">后一点坐标</param>
322         /// <returns></returns>
323         private float GetPointDistance(Vector3 prevPos, Vector3 nextPos)
324         {
325             return Vector3.Distance(prevPos, nextPos);
326         }
327 
328         #endregion
329     }
330 }
View Code

相关文章: