增加B样条曲线脚本
parent
cf7d3b3b75
commit
83763d8168
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5dcf5a38e38ac3a40b688e333002f841
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace GameWrapper.BSplineCurve
|
||||
{
|
||||
/// <summary>
|
||||
/// B样条曲线
|
||||
/// </summary>
|
||||
public sealed class BSplineCurve : MonoBehaviour
|
||||
{
|
||||
[Header("是否本地坐标")]
|
||||
public bool isLocalPos;
|
||||
|
||||
[Header("控制点设置")]
|
||||
public List<Vector3> listControlPoint = new();
|
||||
/// <summary>
|
||||
/// 实际点位
|
||||
/// </summary>
|
||||
private List<Vector3> curvePoints = new();
|
||||
|
||||
[Range(2, 7)]
|
||||
private int degree = 2; // 曲线阶数(次数+1)
|
||||
[Range(0.001f, 0.1f)]
|
||||
private float stepSize = 0.01f;
|
||||
|
||||
#region API
|
||||
/// <summary>
|
||||
/// 获取曲线上特定参数值的点
|
||||
/// </summary>
|
||||
public Vector3 GetPointOnCurve(float t)
|
||||
{
|
||||
if (listControlPoint.Count <= degree || curvePoints.Count == 0)
|
||||
return Vector3.zero;
|
||||
|
||||
int n = listControlPoint.Count - 1;
|
||||
float normalizedT = Mathf.Lerp(degree, n + 1, Mathf.Clamp01(t));
|
||||
|
||||
Vector3 point = Vector3.zero;
|
||||
float[] knots = GenerateKnotVector();
|
||||
|
||||
for (int i = 0; i <= n; i++)
|
||||
{
|
||||
float basis = BasisFunction(i, degree, normalizedT, knots);
|
||||
point += GetPos(i) * basis;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成B样条曲线
|
||||
/// </summary>
|
||||
public void GenerateCurve()
|
||||
{
|
||||
curvePoints.Clear();
|
||||
|
||||
if (listControlPoint.Count < 2)
|
||||
return;
|
||||
|
||||
int n = listControlPoint.Count - 1; // 控制点索引最大值
|
||||
int m = n + degree + 1; // 节点向量最大值
|
||||
|
||||
// 生成均匀节点向量
|
||||
float[] knots = new float[m + 1];
|
||||
for (int i = 0; i <= m; i++)
|
||||
{
|
||||
knots[i] = i;
|
||||
}
|
||||
|
||||
curvePoints.Add(GetPos(0));
|
||||
// 计算曲线点
|
||||
for (float t = degree; t <= n + 1; t += stepSize)
|
||||
{
|
||||
Vector3 point = Vector3.zero;
|
||||
|
||||
for (int i = 0; i <= n; i++)
|
||||
{
|
||||
float basis = BasisFunction(i, degree, t, knots);
|
||||
point += GetPos(i) * basis;
|
||||
}
|
||||
|
||||
curvePoints.Add(point);
|
||||
}
|
||||
curvePoints.Add(GetPos(listControlPoint.Count - 1));
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// B样条基函数计算(De Boor递归算法)
|
||||
/// </summary>
|
||||
private float BasisFunction(int i, int k, float t, float[] knots)
|
||||
{
|
||||
if (0 == k)
|
||||
return (t >= knots[i] && t < knots[i + 1]) ? 1f : 0f;
|
||||
else
|
||||
{
|
||||
float denominator1 = knots[i + k] - knots[i];
|
||||
float denominator2 = knots[i + k + 1] - knots[i + 1];
|
||||
|
||||
float term1 = 0f;
|
||||
float term2 = 0f;
|
||||
|
||||
if (0 != denominator1)
|
||||
term1 = (t - knots[i]) / denominator1 * BasisFunction(i, k - 1, t, knots);
|
||||
|
||||
if (0 != denominator2)
|
||||
term2 = (knots[i + k + 1] - t) / denominator2 * BasisFunction(i + 1, k - 1, t, knots);
|
||||
|
||||
return term1 + term2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成节点向量
|
||||
/// </summary>
|
||||
private float[] GenerateKnotVector()
|
||||
{
|
||||
int n = listControlPoint.Count - 1;
|
||||
int m = n + degree + 1;
|
||||
float[] knots = new float[m + 1];
|
||||
|
||||
for (int i = 0; i <= m; i++)
|
||||
{
|
||||
knots[i] = i;
|
||||
}
|
||||
|
||||
return knots;
|
||||
}
|
||||
|
||||
private Vector3 GetPos(int index)
|
||||
{
|
||||
if (isLocalPos)
|
||||
return listControlPoint[index] + gameObject.transform.position;
|
||||
else
|
||||
return listControlPoint[index];
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[Header("是否显示控制点")]
|
||||
public bool isShowControlPoints = true;
|
||||
[Header("是否显示曲线")]
|
||||
public bool isShowCurve = true;
|
||||
[Header("控制点颜色")]
|
||||
public Color controlPointColor = new(1f, 0f, 0f, 0.8f);
|
||||
[Header("曲线颜色")]
|
||||
public Color curveColor = Color.blue;
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
if (Selection.activeGameObject != gameObject)
|
||||
return;
|
||||
|
||||
GenerateCurve();
|
||||
|
||||
// 绘制曲线
|
||||
if (isShowCurve && curvePoints.Count > 1)
|
||||
{
|
||||
Gizmos.color = curveColor;
|
||||
for (int i = 0; i < curvePoints.Count - 1; ++i)
|
||||
{
|
||||
Gizmos.DrawLine(curvePoints[i], curvePoints[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制控制点
|
||||
if (isShowControlPoints)
|
||||
{
|
||||
Gizmos.color = controlPointColor;
|
||||
for (int i = 0; i < listControlPoint.Count; ++i)
|
||||
{
|
||||
Vector3 point = GetPos(i);
|
||||
Gizmos.DrawSphere(point, 0.2f);
|
||||
Gizmos.DrawIcon(point, i.ToString());
|
||||
}
|
||||
|
||||
// 绘制控制多边形
|
||||
for (int i = 0; i < listControlPoint.Count - 1; ++i)
|
||||
{
|
||||
Gizmos.DrawLine(GetPos(i), GetPos(i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7da60780d216efa4f97b7bd37895bf08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in New Issue