Archived
* 将组件加入到菜单中。
* 添加一个任意形状编辑器
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace XericLibrary.Runtime.Renderer
|
||||
{
|
||||
public static class TriangulateHelp
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 添加填充多边形
|
||||
/// </summary>
|
||||
public static void AddFilledPolygon(VertexHelper vh, List<Vector2> vertices, List<int> triangles, Color color)
|
||||
{
|
||||
int vertexStartIndex = vh.currentVertCount;
|
||||
|
||||
// 添加顶点
|
||||
foreach (var vertex in vertices)
|
||||
{
|
||||
vh.AddVert(vertex, color, Vector2.zero);
|
||||
}
|
||||
|
||||
// 添加三角形索引
|
||||
for (int i = 0; i < triangles.Count; i += 3)
|
||||
{
|
||||
vh.AddTriangle(
|
||||
vertexStartIndex + triangles[i],
|
||||
vertexStartIndex + triangles[i + 1],
|
||||
vertexStartIndex + triangles[i + 2]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加描边
|
||||
/// </summary>
|
||||
public static void AddStroke(VertexHelper vh, List<Vector2> vertices, float width, Color color)
|
||||
{
|
||||
if (vertices.Count < 2)
|
||||
return;
|
||||
|
||||
float halfWidth = width * 0.5f;
|
||||
|
||||
// 为每条边生成两个三角形(形成矩形)
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
{
|
||||
int next = (i + 1) % vertices.Count;
|
||||
|
||||
Vector2 p1 = vertices[i];
|
||||
Vector2 p2 = vertices[next];
|
||||
|
||||
// 计算边的法线
|
||||
Vector2 edge = p2 - p1;
|
||||
Vector2 normal = new Vector2(-edge.y, edge.x).normalized;
|
||||
|
||||
// 生成四个顶点(矩形)
|
||||
Vector2 v1 = p1 + normal * halfWidth;
|
||||
Vector2 v2 = p1 - normal * halfWidth;
|
||||
Vector2 v3 = p2 - normal * halfWidth;
|
||||
Vector2 v4 = p2 + normal * halfWidth;
|
||||
|
||||
int startIndex = vh.currentVertCount;
|
||||
|
||||
vh.AddVert(v1, color, Vector2.zero);
|
||||
vh.AddVert(v2, color, Vector2.zero);
|
||||
vh.AddVert(v3, color, Vector2.zero);
|
||||
vh.AddVert(v4, color, Vector2.zero);
|
||||
|
||||
// 两个三角形
|
||||
vh.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
|
||||
vh.AddTriangle(startIndex, startIndex + 2, startIndex + 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 耳切法三角剖分
|
||||
/// </summary>
|
||||
public static List<int> TriangulatePolygon(List<Vector2> polygonVertices)
|
||||
{
|
||||
List<int> triangles = new List<int>();
|
||||
|
||||
if (polygonVertices == null || polygonVertices.Count < 3)
|
||||
return triangles;
|
||||
|
||||
// 复制顶点列表以便修改
|
||||
List<Vector2> verticesCopy = new List<Vector2>(polygonVertices);
|
||||
List<int> indices = Enumerable.Range(0, verticesCopy.Count).ToList();
|
||||
|
||||
// 确保顶点顺序是逆时针(耳切法要求)
|
||||
if (!IsCounterClockwise(verticesCopy))
|
||||
{
|
||||
verticesCopy.Reverse();
|
||||
indices.Reverse();
|
||||
}
|
||||
|
||||
int n = verticesCopy.Count;
|
||||
|
||||
// 当顶点数大于3时继续切耳朵
|
||||
while (n > 3)
|
||||
{
|
||||
bool foundEar = false;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int prev = (i - 1 + n) % n;
|
||||
int curr = i;
|
||||
int next = (i + 1) % n;
|
||||
|
||||
// 检查是否是耳朵
|
||||
if (IsEar(verticesCopy, prev, curr, next, n))
|
||||
{
|
||||
// 添加三角形
|
||||
triangles.Add(indices[prev]);
|
||||
triangles.Add(indices[curr]);
|
||||
triangles.Add(indices[next]);
|
||||
|
||||
// 移除耳朵顶点
|
||||
verticesCopy.RemoveAt(curr);
|
||||
indices.RemoveAt(curr);
|
||||
n--;
|
||||
foundEar = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到耳朵,可能是复杂多边形,退出
|
||||
if (!foundEar)
|
||||
{
|
||||
Debug.LogWarning("无法完成三角剖分,可能是自相交多边形");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一个三角形
|
||||
if (n == 3)
|
||||
{
|
||||
triangles.Add(indices[0]);
|
||||
triangles.Add(indices[1]);
|
||||
triangles.Add(indices[2]);
|
||||
}
|
||||
|
||||
return triangles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查顶点顺序是否为逆时针
|
||||
/// </summary>
|
||||
public static bool IsCounterClockwise(List<Vector2> vertices)
|
||||
{
|
||||
float sum = 0;
|
||||
int n = vertices.Count;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Vector2 v1 = vertices[i];
|
||||
Vector2 v2 = vertices[(i + 1) % n];
|
||||
sum += (v2.x - v1.x) * (v2.y + v1.y);
|
||||
}
|
||||
|
||||
return sum < 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// 检查三个顶点是否构成耳朵
|
||||
/// </summary>
|
||||
public static bool IsEar(List<Vector2> vertices, int prev, int curr, int next, int n)
|
||||
{
|
||||
Vector2 a = vertices[prev];
|
||||
Vector2 b = vertices[curr];
|
||||
Vector2 c = vertices[next];
|
||||
|
||||
// 检查是否是凸角
|
||||
if (!IsConvex(a, b, c))
|
||||
return false;
|
||||
|
||||
// 检查三角形内部是否包含其他顶点
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (i == prev || i == curr || i == next)
|
||||
continue;
|
||||
|
||||
if (PointInTriangle(vertices[i], a, b, c))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查点是否在三角形内部
|
||||
/// </summary>
|
||||
public static bool PointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
|
||||
{
|
||||
float area = Mathf.Abs(CrossProduct(a, b, c));
|
||||
float area1 = Mathf.Abs(CrossProduct(p, a, b));
|
||||
float area2 = Mathf.Abs(CrossProduct(p, b, c));
|
||||
float area3 = Mathf.Abs(CrossProduct(p, c, a));
|
||||
|
||||
return Mathf.Abs(area - (area1 + area2 + area3)) < 0.001f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查三点是否构成凸角(逆时针)
|
||||
/// </summary>
|
||||
public static bool IsConvex(Vector2 a, Vector2 b, Vector2 c)
|
||||
{
|
||||
return CrossProduct(a, b, c) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算叉积
|
||||
/// </summary>
|
||||
public static float CrossProduct(Vector2 a, Vector2 b, Vector2 c)
|
||||
{
|
||||
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45ff8ace448e4691b1db19cb52906ddd
|
||||
timeCreated: 1775549185
|
||||
@@ -0,0 +1,568 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MathNet.Numerics.Distributions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Pool;
|
||||
using UnityEngine.UI;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
|
||||
namespace XericLibrary.Runtime.Renderer
|
||||
{
|
||||
/// <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>
|
||||
/// <returns></returns>
|
||||
private static bool GetNormalizedVector(Vector2 startPoint, Vector2 endPoint,
|
||||
out Vector2 vector, out float distance, out Vector2 normal, out Vector2 subTangent)
|
||||
{
|
||||
vector = 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, float startLength, out float distance, float totalLength, Vector3 offset)
|
||||
{
|
||||
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);
|
||||
|
||||
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="color"></param>
|
||||
/// <param name="offset"></param>
|
||||
public static void PopulateLineByPointsArray_DSL5(this VertexHelper vh, Vector2[] points, float thickness, bool cycleLoop, Color color, Vector3 offset)
|
||||
{
|
||||
// 至少需要 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++)
|
||||
{
|
||||
vh.AddSingleLineSegmentVertsAndTriangle_DSL5(i,
|
||||
GetSingleLineSegmentVerts_DSL5(vertices, points[i], points[(i + 1) % points.Length], thickness, color, currentLength, out var distance, totalLength, offset));
|
||||
currentLength += distance;
|
||||
}
|
||||
ListPool<UIVertex>.Release(vertices);
|
||||
}
|
||||
|
||||
#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>
|
||||
/// 创建线段的顶点数据
|
||||
/// 每个线段由 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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 117633a298088ab4fb9fa0edac2bd141
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user