This repository has been archived on 2026-07-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
2026-07-06 17:04:54 +08:00

691 lines
29 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UI;
using XericLibrary.Runtime.MacroLibrary;
namespace XericLibrary.Runtime.UIGraph
{
/// <summary>
/// UILineRenderer 的工具类,提供静态方法用于线条渲染相关的操作
/// </summary>
internal static class XericRendererUtils
{
#region 数学计算
/// <summary>
/// 计算线路的总长度
/// </summary>
/// <param name="points">线路的顶点数组</param>
/// <param name="cycleLoop">线路是循环</param>
/// <returns>线路的总长度</returns>
public static float CalculateTotalLength(this Vector2[] points, bool cycleLoop = false)
{
if (points.Length < 2)
return 0f;
var totalLength = 0f;
for (int i = 0; i < points.Length - 1; i++)
totalLength += Vector2.Distance(points[i], points[i + 1]);
if (cycleLoop)
totalLength += Vector2.Distance(points[0], points[^1]);
return totalLength;
}
/// <summary>
/// 计算一个点指向另一个点的角度(以度为单位)
/// </summary>
/// <param name="vertex">起始点</param>
/// <param name="target">目标点</param>
/// <returns>旋转角度,范围为 -180 到 180 度</returns>
public static float RotatePointTowards(Vector2 vertex, Vector2 target)
{
// 使用反正切函数计算两点之间的角度
// 将弧度转换为角度返回
return (Mathf.Atan2(target.y - vertex.y, target.x - vertex.x) * Mathf.Rad2Deg);
}
/// <summary>
/// 如果需要居中显示,则偏移到组件中心
/// </summary>
/// <param name="center">是否要居中显示</param>
/// <param name="rectSize">整个绘制矩形范围的尺寸</param>
/// <returns></returns>
public static Vector2 SwitchCenterOffset(bool center, Vector2 rectSize) =>
center ? Vector2.zero : -(rectSize / 2);
/// <summary>
/// 获取两个点之间的法线,副法线
/// </summary>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <param name="vector">起点到终点的矢量</param>
/// <param name="distance">两点间距离</param>
/// <param name="normal">起点到终点的法线</param>
/// <param name="subTangent">垂直与起点到终点的法线(法线左转90</param>
/// <param name="reverseVector">反转矢量</param>
/// <returns></returns>
private static bool GetNormalizedVector(Vector2 startPoint, Vector2 endPoint,
out Vector2 vector, out float distance, out Vector2 normal, out Vector2 subTangent,
bool reverseVector = false)
{
vector = reverseVector ? startPoint - endPoint : endPoint - startPoint;
distance = vector.magnitude;
normal = Vector2.zero;
subTangent = Vector2.zero;
// 距离过小时不绘制
if (distance <= float.Epsilon)
return false;
// 线段的法线和副法线
normal = vector / distance;
subTangent = Vector2.Perpendicular(normal);
return true;
}
#endregion
#region uv计算
/// <summary>
/// 计算起点和终点的UV坐标Y值
/// </summary>
/// <param name="startLength">这段线段的起始线路长度</param>
/// <param name="currentLength">这段线段的长度</param>
/// <param name="totalLength">总线路的长度</param>
/// <param name="startUVY">起点处的uv y</param>
/// <param name="endUVY">终点处的uv y</param>
private static void GetCurrentLengthUVY(float startLength, float currentLength, float totalLength,
out float startUVY, out float endUVY)
{
startUVY = totalLength > 0 ? startLength / totalLength : 0f;
endUVY = totalLength > 0 ? (startLength + currentLength) / totalLength : 0f;
}
#endregion
#region 简易线路绘制
// 在点与点之间绘制给定宽度(thickness)的方片,转角处用两个三角形链接
// 这样会造成一根线会出现5个顶点,所以叫做 DSL5 (直接简单的5点直线 directly simple line 5)
/// <summary>
/// 创建线段的顶点数据
/// 每个线段由 5 个顶点组成:
/// - 起点的左右两个顶点
/// - 终点的左右两个顶点
/// - 终点的中心点(用于线段连接)
/// </summary>
/// <param name="vertices">顶点容器</param>
/// <param name="startPoint">线段的起点</param>
/// <param name="distance">线段的距离</param>
/// <param name="endPoint">线段的终点</param>
/// <param name="thickness">线条的厚度</param>
/// <param name="color">线条的颜色</param>
/// <param name="startLength">当前线段起点在线路中的累积长度</param>
/// <param name="totalLength">线路的总长度</param>
public static List<UIVertex> GetSingleLineSegmentVerts_DSL5(List<UIVertex> vertices, Vector2 startPoint,
Vector2 endPoint,
float thickness, Color color, Vector2 normal, Vector2 subTangent, float startUVY, float endUVY,
Vector3 offset)
{
if (vertices == null)
throw new Exception("没有指定线段片段顶点容器列表");
vertices.Clear();
// 创建顶点模板,设置颜色为组件的当前颜色
UIVertex vertex = UIVertex.simpleVert;
vertex.color = color;
// 线段单侧宽度
var width = thickness / 2;
vertex.position = (startPoint +
subTangent * width);
vertex.position += offset;
vertex.uv0 = new Vector2(0f, startUVY); // 左侧顶点的UV
vertices.Add(vertex);
vertex.position = (startPoint +
subTangent * -width);
vertex.position += offset;
vertex.uv0 = new Vector2(1f, startUVY); // 右侧顶点的UV
vertices.Add(vertex);
vertex.position = (endPoint +
subTangent * width);
vertex.position += offset;
vertex.uv0 = new Vector2(0f, endUVY); // 左侧顶点的UV
vertices.Add(vertex);
vertex.position = (endPoint +
subTangent * -width);
vertex.position += offset;
vertex.uv0 = new Vector2(1f, endUVY); // 右侧顶点的UV
vertices.Add(vertex);
// 添加终点的中心点,用于连接后续线段
vertex.position = endPoint;
vertex.position += offset;
vertex.uv0 = new Vector2(0.5f, endUVY); // 中心点的UV
vertices.Add(vertex);
return vertices;
}
/// <summary>
/// 在网格构建对象上添加这个直线片段
/// </summary>
/// <param name="vh">构建网格对象</param>
/// <param name="segmentPartIndex">该线路片段索引(比如第一段线应该是0)</param>
/// <param name="verts">该片段顶点</param>
public static void AddSingleLineSegmentVertsAndTriangle_DSL5(this VertexHelper vh, int segmentPartIndex,
IEnumerable<UIVertex> verts)
{
foreach (var vert in verts)
vh.AddVert(vert);
int index = segmentPartIndex * 5;
// 添加线段的两个三角形(构成矩形线段)
vh.AddTriangle(index, index + 1, index + 3);
vh.AddTriangle(index + 3, index + 2, index);
// 为线段之间添加斜角边缘
// 使用上一个线段的终点和当前线段的起点创建过渡三角形
if (segmentPartIndex != 0)
{
vh.AddTriangle(index, index - 1, index - 3);
vh.AddTriangle(index + 1, index - 1, index - 2);
}
}
/// <summary>
/// 推送顶点绘制线路
/// </summary>
/// <param name="vh"></param>
/// <param name="points"></param>
/// <param name="thickness"></param>
/// <param name="cycleLoop"></param>
/// <param name="lineColor"></param>
/// <param name="offset"></param>
/// <param name="enableArrow"></param>
/// <param name="arrowColor"></param>
/// <param name="arrowSize">宽x长y</param>
/// <param name="pointProgress"></param>
/// <param name="arrowPointProgress"></param>
/// <param name="absProgressPoint"></param>
/// <param name="reverseDir"></param>
public static void PopulateLineByPointsArray_DSL5(this VertexHelper vh, Vector2[] points,
float thickness, bool cycleLoop, Color lineColor, Vector3 offset,
bool enableArrow,
Color arrowColor, Vector2 arrowSize, float pointProgress, float arrowPointProgress,
bool absProgressPoint = true, bool reverseDir = false)
{
// 至少需要 2 个点才能绘制线条
if (points.Length < 2)
return;
// 计算线路总长度
var totalLength = points.CalculateTotalLength(cycleLoop);
var currentLength = 0f;
var lineVertices = ListPool<UIVertex>.Get();
var arrowVertices = ListPool<UIVertex>.Get();
var arrowindices = ListPool<int>.Get();
for (int i = 0; i < points.Length - (cycleLoop ? 0 : 1); i++)
{
var thisPoint = points[i];
var nextPoint = points[(i + 1) % points.Length];
// 先计算线的矢量参数,以及uv。
if (!GetNormalizedVector(thisPoint, nextPoint,
out var vector, out var distance, out var normal, out var subTangent))
continue;
GetCurrentLengthUVY(currentLength, distance, totalLength, out var startUvy, out var endUvy);
vh.AddSingleLineSegmentVertsAndTriangle_DSL5(i,
GetSingleLineSegmentVerts_DSL5(lineVertices, thisPoint, nextPoint, thickness, lineColor, normal,
subTangent, startUvy, endUvy, offset));
if (enableArrow)
{
PopulateTriangleArrowByPointArray(arrowVertices, thisPoint, nextPoint, arrowColor, arrowSize.y,
arrowSize.x, pointProgress, arrowPointProgress, absProgressPoint, reverseDir);
arrowindices.AddFaceByConsecutiveVertices(arrowVertices, i * 3, 3, i * 3);
}
currentLength += distance;
}
if (enableArrow)
{
vh.AddUIVertexStream(arrowVertices, arrowindices);
}
ListPool<UIVertex>.Release(lineVertices);
ListPool<UIVertex>.Release(arrowVertices);
ListPool<int>.Release(arrowindices);
}
#endregion
#region 圆弧过度的线路绘制
// 在简易线路绘制的基础上,修改了转角处的处理
// 线的顶点还是5,但是转角圆弧处理会更细节,叫它 CFAL (中心对齐的圆弧转角直线,center fanshaped arc Line)
/// <summary>
/// 在网格构建对象上添加这个直线片段
/// </summary>
/// <param name="vh">构建网格对象</param>
/// <param name="segmentPartIndex">该线路片段索引(比如第一段线应该是0)</param>
/// <param name="verts">该片段顶点</param>
public static void AddSingleLineSegmentVertsAndTriangle_CFAL(this VertexHelper vh, int segmentPartIndex,
IEnumerable<UIVertex> verts)
{
foreach (var vert in verts)
vh.AddVert(vert);
int index = segmentPartIndex * 5;
// 添加线段的两个三角形(构成矩形线段)
vh.AddTriangle(index, index + 1, index + 3);
vh.AddTriangle(index + 3, index + 2, index);
// 为线段之间添加斜角边缘
// 使用上一个线段的终点和当前线段的起点创建过渡三角形
if (segmentPartIndex != 0)
{
vh.AddTriangle(index, index - 1, index - 3);
vh.AddTriangle(index + 1, index - 1, index - 2);
}
}
/// <summary>
/// 创建带有倒角的线段顶点数据
/// 支持设置圆弧细分数量和内圆弧半径
/// </summary>
/// <param name="vertices">顶点容器</param>
/// <param name="startPoint">线段的起点</param>
/// <param name="endPoint">线段的终点</param>
/// <param name="nextPoint">下一个线段的终点(用于计算夹角)</param>
/// <param name="prevPoint">上一个线段的起点(用于计算夹角)</param>
/// <param name="thickness">线条的厚度</param>
/// <param name="color">线条的颜色</param>
/// <param name="startLength">当前线段起点在线路中的累积长度</param>
/// <param name="totalLength">线路的总长度</param>
/// <param name="offset">偏移量</param>
/// <param name="chamferSegments">倒角细分数量</param>
/// <param name="innerRadius">内圆弧半径</param>
/// <param name="distance">返回线段的距离</param>
/// <returns>包含线段顶点的列表</returns>
public static List<UIVertex> GetSingleLineSegmentVerts_CFAL(List<UIVertex> vertices, Vector2 startPoint,
Vector2 endPoint, Vector2 nextPoint, Vector2 prevPoint,
float thickness, Color color, float startLength, out float distance, float totalLength, Vector3 offset,
int chamferSegments = 0, float innerRadius = 0f)
{
if (vertices == null)
throw new Exception("没有指定线段片段顶点容器列表");
vertices.Clear();
// 创建顶点模板,设置颜色为组件的当前颜色
UIVertex vertex = UIVertex.simpleVert;
vertex.color = color;
if (!GetNormalizedVector(startPoint, endPoint,
out var vector, out distance, out var normal, out var subTangent))
return vertices;
// 线段单侧宽度
var width = thickness / 2;
GetCurrentLengthUVY(startLength, distance, totalLength, out var startUVY, out var endUVY);
// 计算起点和终点的夹角
float startAngle = 0f;
float endAngle = 0f;
if (prevPoint != startPoint)
{
var prevVector = startPoint - prevPoint;
var prevNormal = prevVector.normalized;
startAngle = Vector2.Angle(normal, -prevNormal);
}
if (nextPoint != endPoint)
{
var nextVector = nextPoint - endPoint;
var nextNormal = nextVector.normalized;
endAngle = Vector2.Angle(-normal, nextNormal);
}
// 计算顶点偏移量
float startOffset = 0f;
float endOffset = 0f;
if (Mathf.Abs(startAngle) > 0.1f)
{
if (innerRadius > 0f)
{
startOffset = (width + innerRadius) / Mathf.Sin(startAngle * Mathf.Deg2Rad / 2) - innerRadius;
}
else
{
startOffset = width / Mathf.Sin(startAngle * Mathf.Deg2Rad / 2);
}
}
if (Mathf.Abs(endAngle) > 0.1f)
{
if (innerRadius > 0f)
{
endOffset = (width + innerRadius) / Mathf.Sin(endAngle * Mathf.Deg2Rad / 2) - innerRadius;
}
else
{
endOffset = width / Mathf.Sin(endAngle * Mathf.Deg2Rad / 2);
}
}
// 添加起点的左侧顶点
vertex.position = (startPoint +
subTangent * width +
normal * startOffset);
vertex.position += offset;
vertex.uv0 = new Vector2(0f, startUVY);
vertices.Add(vertex);
// 添加起点的右侧顶点
vertex.position = (startPoint +
subTangent * -width +
normal * startOffset);
vertex.position += offset;
vertex.uv0 = new Vector2(1f, startUVY);
vertices.Add(vertex);
// 添加终点的左侧顶点
vertex.position = (endPoint +
subTangent * width +
normal * -endOffset);
vertex.position += offset;
vertex.uv0 = new Vector2(0f, endUVY);
vertices.Add(vertex);
// 添加终点的右侧顶点
vertex.position = (endPoint +
subTangent * -width +
normal * -endOffset);
vertex.position += offset;
vertex.uv0 = new Vector2(1f, endUVY);
vertices.Add(vertex);
// 添加终点的中心点
vertex.position = (endPoint +
normal * -endOffset);
vertex.position += offset;
vertex.uv0 = new Vector2(0.5f, endUVY);
vertices.Add(vertex);
return vertices;
}
/// <summary>
/// 推送顶点绘制带有倒角的线路
/// </summary>
/// <param name="vh">网格构建对象</param>
/// <param name="points">线路顶点数组</param>
/// <param name="thickness">线条厚度</param>
/// <param name="cycleLoop">是否循环</param>
/// <param name="color">线条颜色</param>
/// <param name="offset">偏移量</param>
/// <param name="chamferSegments">倒角细分数量</param>
/// <param name="innerRadius">内圆弧半径</param>
public static void PopulateLineByPointsArray_CFAL(this VertexHelper vh, Vector2[] points, float thickness,
bool cycleLoop, Color color, Vector3 offset,
int chamferSegments = 0, float innerRadius = 0f)
{
// 至少需要 2 个点才能绘制线条
if (points.Length < 2)
return;
// 计算线路总长度
var totalLength = points.CalculateTotalLength(cycleLoop);
var currentLength = 0f;
var vertices = ListPool<UIVertex>.Get();
for (int i = 0; i < points.Length - (cycleLoop ? 0 : 1); i++)
{
var startPoint = points[i];
var endPoint = points[(i + 1) % points.Length];
var prevPoint = i > 0 ? points[i - 1] : cycleLoop ? points[^1] : startPoint;
var nextPoint = points[(i + 2) % points.Length];
vh.AddSingleLineSegmentVertsAndTriangle_CFAL(i,
GetSingleLineSegmentVerts_CFAL(vertices, startPoint, endPoint, nextPoint, prevPoint,
thickness, color, currentLength, out var distance, totalLength, offset,
chamferSegments, innerRadius));
currentLength += distance;
}
ListPool<UIVertex>.Release(vertices);
}
#endregion
#region 箭头绘制工具
/// <summary>
/// 绘制一个三角形箭头
/// </summary>
/// <param name="start">起点</param>
/// <param name="end">终点</param>
/// <param name="color">颜色</param>
/// <param name="sizeL">箭头的长度</param>
/// <param name="sizeW">箭头的宽度</param>
/// <param name="pointProgress">箭头所处坐标相对线的进度,使用绝对距离时,进度为世界单位;使用相对距离时,进度为单位距离</param>
/// <param name="arrowPointProgress">箭头的哪部分与所处坐标对齐,0为头部对齐,1为尾部对齐</param>
/// <param name="absProgressPoint">箭头做出坐标是否为绝对距离</param>
/// <param name="reverseDir">反转方向</param>
/// <remarks>
/// 箭头默认在
/// </remarks>
public static List<UIVertex> PopulateTriangleArrowByPointArray(List<UIVertex> vertices, Vector2 start,
Vector2 end, Color color, float sizeL, float sizeW, float pointProgress, float arrowPointProgress,
bool absProgressPoint = true, bool reverseDir = false)
{
GetNormalizedVector(start, end, out var vector, out var length, out var normal, out var tangent,
reverseDir);
var progressPosition = absProgressPoint
? pointProgress * normal
: pointProgress * vector;
// 加上坐标
progressPosition += reverseDir ? end : start;
// 加上箭头偏移量
progressPosition += arrowPointProgress * sizeL * normal;
// 绘制箭头
UIVertex vertex = UIVertex.simpleVert;
vertex.color = color;
vertex.position = progressPosition;
vertices.Add(vertex);
vertex.position = progressPosition + -sizeL * normal + sizeW * 0.5f * tangent;
vertices.Add(vertex);
vertex.position += (Vector3)(-sizeW * tangent);
vertices.Add(vertex);
return vertices;
}
#endregion
#region 带有顶合并的线路绘制方法
/// <summary>
/// 创建线段的顶点数据
/// 每个线段由 5 个顶点组成:
/// - 起点的左右两个顶点
/// - 终点的左右两个顶点
/// - 终点的中心点(用于线段连接)
/// </summary>
/// <param name="startPoint">线段的起点</param>
/// <param name="endPoint">线段的终点</param>
/// <param name="angle0">起点到终点与终点到下一点</param>
/// <param name="angle3">起点到终点与终点到下一点</param>
/// <param name="thickness">线条的厚度</param>
/// <param name="center">是否居中显示</param>
/// <param name="rectSize">矩形变换的尺寸</param>
/// <param name="color">线条的颜色</param>
/// <param name="vh">用于添加顶点的 VertexHelper 对象</param>
/// <param name="startLength">当前线段起点在线路中的累积长度</param>
/// <param name="totalLength">线路的总长度</param>
public static void CreateLineSegment_CFAL(this VertexHelper vh, Vector2 startPoint, Vector2 endPoint,
float angle0, float angle3, float thickness, bool center, Vector2 rectSize, Color color, float startLength,
float totalLength)
{
// 计算偏移量:如果需要居中显示,则偏移到组件中心
Vector2 offset = center ? (rectSize / 2) : Vector2.zero;
// 创建顶点模板,设置颜色为组件的当前颜色
UIVertex vertex = UIVertex.simpleVert;
vertex.color = color;
if (!GetNormalizedVector(startPoint, endPoint,
out var vector, out var distance, out var normal, out var subTangent))
return;
// 线段单侧宽度
var width = thickness / 2;
//顶点偏移量,根据夹角计算内侧交点相对坐标点应该退后的距离
var pointOffset1 = Mathf.Abs(angle0) <= 0.1f
? 0
: width / Mathf.Sin(angle0 * Mathf.Deg2Rad) + (angle0 > 90 ? width : 0);
var pointOffset2 = Mathf.Abs(angle3) <= 0.1f
? 0
: width / Mathf.Sin(angle3 * Mathf.Deg2Rad) + (angle3 > 90 ? width : 0);
Debug.Log(
$"{angle0} = {Mathf.Sin(angle0 * Mathf.Deg2Rad)} , {angle3} = {Mathf.Sin(angle3 * Mathf.Deg2Rad)}");
// 计算起点和终点的UV坐标Y值
float startUVY = totalLength > 0 ? startLength / totalLength : 0f;
float endUVY = totalLength > 0 ? (startLength + distance) / totalLength : 0f;
vertex.position = (Vector3)(startPoint +
subTangent * width +
normal * pointOffset1) - (Vector3)offset;
vertex.uv0 = new Vector2(0f, startUVY); // 左侧顶点的UV
vh.AddVert(vertex);
vertex.position = (Vector3)(startPoint +
subTangent * -width +
normal * pointOffset1) - (Vector3)offset;
vertex.uv0 = new Vector2(1f, startUVY); // 右侧顶点的UV
vh.AddVert(vertex);
vertex.position = (Vector3)(endPoint +
subTangent * width +
normal * -pointOffset2) - (Vector3)offset;
vertex.uv0 = new Vector2(0f, endUVY); // 左侧顶点的UV
vh.AddVert(vertex);
vertex.position = (Vector3)(endPoint +
subTangent * -width +
normal * -pointOffset2) - (Vector3)offset;
vertex.uv0 = new Vector2(1f, endUVY); // 右侧顶点的UV
vh.AddVert(vertex);
// 添加终点的中心点,用于连接后续线段
vertex.position = (Vector3)(endPoint +
normal * -pointOffset2) - (Vector3)offset;
vertex.uv0 = new Vector2(0.5f, endUVY); // 中心点的UV
vh.AddVert(vertex);
}
#endregion
#region 顶点工具集
public static void GetUVAtRect(this Vector2 pos, Rect rect, out float u, out float v)
{
u = (pos.x - rect.xMin) / rect.width;
v = (pos.y - rect.yMin) / rect.height;
}
public static void Get(this IList<UIVertex> vertexList, Vector4 uv2)
{
}
#endregion
#region 顶点列表工具
private static readonly Color32 s_DefaultColor =
new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
private static readonly Vector4 s_DefaultTangent = new Vector4(1f, 0.0f, 0.0f, -1f);
/// <summary>
/// 添加一个顶点信息
/// </summary>
/// <param name="vertexList"></param>
/// <param name="position"></param>
/// <param name="uv"></param>
/// <param name="color"></param>
public static void AddVert(this ICollection<UIVertex> vertexList, Vector3 position, Vector4 uv, Color32 color)
{
vertexList.Add(new UIVertex()
{
position = position,
normal = Vector3.back,
tangent = s_DefaultTangent,
color = color,
uv0 = uv,
uv1 = uv,
uv2 = uv,
uv3 = uv
});
}
/// <summary>
/// 添加一个三角面索引
/// </summary>
/// <param name="indices"></param>
/// <param name="index0"></param>
/// <param name="index1"></param>
/// <param name="index2"></param>
public static void AddTriangle(this ICollection<int> indices, int index0, int index1, int index2)
{
indices.Add(index0);
indices.Add(index1);
indices.Add(index2);
}
/// <summary>
/// 向顶点管理器添加顶点列表中的顶点构成的面
/// </summary>
/// <param name="vh"></param>
/// <param name="vertexList"></param>
/// <param name="startVertexIndex">开始连成面的顶点</param>
/// <param name="faceVertexRange">连成面的顶点范围</param>
/// <param name="centerVertexIndex">作为连接面中心的顶点</param>
public static void AddFaceByConsecutiveVertices(this ICollection<int> indices, IList<UIVertex> vertexList,
int startVertexIndex, int faceVertexRange, int centerVertexIndex)
{
if (faceVertexRange < 3)
throw new IndexOutOfRangeException("无法创建小于3个点的面");
var length = startVertexIndex + faceVertexRange - 1;
for (int i = startVertexIndex + 1; i < length; i++)
{
if (i < 0 || i > vertexList.Count)
throw new IndexOutOfRangeException(
$"在向顶点集添加顶点时,从{startVertexIndex}开始的{faceVertexRange}个顶点(以及{centerVertexIndex})可能超出{vertexList.Count}的范围(预定范围{length}),导致构建超出预期");
var next = startVertexIndex + 1;
if (next >= vertexList.Count)
return;
indices.AddTriangle(centerVertexIndex, startVertexIndex, startVertexIndex + 1);
}
}
#endregion
}
}