Files
2026-03-04 10:03:45 +08:00

318 lines
7.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace SplineMesh
{
[DisallowMultipleComponent]
[RequireComponent(typeof(MeshFilter))]
[ExecuteInEditMode]
public class MeshBender : MonoBehaviour
{
public enum FillingMode
{
Once = 0,
Repeat = 1,
StretchToInterval = 2
}
private bool isDirty;
private Mesh result;
private bool useSpline;
private Spline spline;
private float intervalStart;
private float intervalEnd;
private CubicBezierCurve curve;
private Dictionary<float, CurveSample> sampleCache = new Dictionary<float, CurveSample>();
private SourceMesh source;
private FillingMode mode = FillingMode.StretchToInterval;
public SourceMesh Source
{
get
{
return source;
}
set
{
if (!(value == source))
{
SetDirty();
source = value;
}
}
}
public FillingMode Mode
{
get
{
return mode;
}
set
{
if (value != mode)
{
SetDirty();
mode = value;
}
}
}
public void SetInterval(CubicBezierCurve curve)
{
if (this.curve != curve)
{
if (curve == null)
{
throw new ArgumentNullException("curve");
}
if (this.curve != null)
{
this.curve.Changed.RemoveListener(SetDirty);
}
this.curve = curve;
spline = null;
curve.Changed.AddListener(SetDirty);
useSpline = false;
SetDirty();
}
}
public void SetInterval(Spline spline, float intervalStart, float intervalEnd = 0f)
{
if (!(this.spline == spline) || this.intervalStart != intervalStart || this.intervalEnd != intervalEnd)
{
if (spline == null)
{
throw new ArgumentNullException("spline");
}
if (intervalStart < 0f || intervalStart >= spline.Length)
{
throw new ArgumentOutOfRangeException("interval start must be 0 or greater and lesser than spline length (was " + intervalStart + ")");
}
if ((intervalEnd != 0f && intervalEnd <= intervalStart) || intervalEnd > spline.Length)
{
throw new ArgumentOutOfRangeException("interval end must be 0 or greater than interval start, and lesser than spline length (was " + intervalEnd + ")");
}
if (this.spline != null)
{
this.spline.CurveChanged.RemoveListener(SetDirty);
}
this.spline = spline;
spline.CurveChanged.AddListener(SetDirty);
curve = null;
this.intervalStart = intervalStart;
this.intervalEnd = intervalEnd;
useSpline = true;
SetDirty();
}
}
private void OnEnable()
{
if (GetComponent<MeshFilter>().sharedMesh != null)
{
result = GetComponent<MeshFilter>().sharedMesh;
return;
}
GetComponent<MeshFilter>().sharedMesh = (result = new Mesh());
result.name = "Generated by " + GetType().Name;
}
private void LateUpdate()
{
ComputeIfNeeded();
}
public void ComputeIfNeeded()
{
if (isDirty)
{
Compute();
}
}
private void SetDirty()
{
isDirty = true;
}
private void Compute()
{
isDirty = false;
switch (Mode)
{
case FillingMode.Once:
FillOnce();
break;
case FillingMode.Repeat:
FillRepeat();
break;
case FillingMode.StretchToInterval:
FillStretch();
break;
}
}
private void OnDestroy()
{
if (curve != null)
{
curve.Changed.RemoveListener(Compute);
}
}
private void FillOnce()
{
sampleCache.Clear();
List<MeshVertex> list = new List<MeshVertex>(source.Vertices.Count);
foreach (MeshVertex vertex in source.Vertices)
{
float num = vertex.position.x - source.MinX;
if (!sampleCache.TryGetValue(num, out var value))
{
if (!useSpline)
{
if (num > curve.Length)
{
num = curve.Length;
}
value = curve.GetSampleAtDistance(num);
}
else
{
float num2 = intervalStart + num;
if (num2 > spline.Length)
{
if (spline.IsLoop)
{
while (num2 > spline.Length)
{
num2 -= spline.Length;
}
}
else
{
num2 = spline.Length;
}
}
value = spline.GetSampleAtDistance(num2);
}
sampleCache[num] = value;
}
list.Add(value.GetBent(vertex));
}
MeshUtility.Update(result, source.Mesh, source.Triangles, list.Select((MeshVertex b) => b.position), list.Select((MeshVertex b) => b.normal));
}
private void FillRepeat()
{
int num = Mathf.FloorToInt((useSpline ? (((intervalEnd == 0f) ? spline.Length : intervalEnd) - intervalStart) : curve.Length) / source.Length);
List<int> list = new List<int>();
List<Vector2> list2 = new List<Vector2>();
List<Vector2> list3 = new List<Vector2>();
List<Vector2> list4 = new List<Vector2>();
List<Vector2> list5 = new List<Vector2>();
List<Vector2> list6 = new List<Vector2>();
List<Vector2> list7 = new List<Vector2>();
List<Vector2> list8 = new List<Vector2>();
List<Vector2> list9 = new List<Vector2>();
for (int i = 0; i < num; i++)
{
int[] triangles = source.Triangles;
foreach (int num2 in triangles)
{
list.Add(num2 + source.Vertices.Count * i);
}
list2.AddRange(source.Mesh.uv);
list3.AddRange(source.Mesh.uv2);
list4.AddRange(source.Mesh.uv3);
list5.AddRange(source.Mesh.uv4);
list6.AddRange(source.Mesh.uv5);
list7.AddRange(source.Mesh.uv6);
list8.AddRange(source.Mesh.uv7);
list9.AddRange(source.Mesh.uv8);
}
List<MeshVertex> list10 = new List<MeshVertex>(source.Vertices.Count);
float num3 = 0f;
for (int k = 0; k < num; k++)
{
sampleCache.Clear();
foreach (MeshVertex vertex in source.Vertices)
{
float num4 = vertex.position.x - source.MinX + num3;
if (!sampleCache.TryGetValue(num4, out var value))
{
if (!useSpline)
{
if (num4 > curve.Length)
{
continue;
}
value = curve.GetSampleAtDistance(num4);
}
else
{
float num5;
for (num5 = intervalStart + num4; num5 > spline.Length; num5 -= spline.Length)
{
}
value = spline.GetSampleAtDistance(num5);
}
sampleCache[num4] = value;
}
list10.Add(value.GetBent(vertex));
}
num3 += source.Length;
}
MeshUtility.Update(result, source.Mesh, list, list10.Select((MeshVertex b) => b.position), list10.Select((MeshVertex b) => b.normal), list2, list3, list4, list5, list6, list7, list8, list9);
}
private void FillStretch()
{
List<MeshVertex> list = new List<MeshVertex>(source.Vertices.Count);
sampleCache.Clear();
foreach (MeshVertex vertex in source.Vertices)
{
float num = ((source.Length == 0f) ? 0f : (Math.Abs(vertex.position.x - source.MinX) / source.Length));
if (!sampleCache.TryGetValue(num, out var value))
{
if (!useSpline)
{
value = curve.GetSampleAtDistance(curve.Length * num);
}
else
{
float num2 = ((intervalEnd == 0f) ? (spline.Length - intervalStart) : (intervalEnd - intervalStart));
float num3 = intervalStart + num2 * num;
if (num3 > spline.Length)
{
num3 = spline.Length;
Debug.Log("dist " + num3 + " spline length " + spline.Length + " start " + intervalStart);
}
value = spline.GetSampleAtDistance(num3);
}
sampleCache[num] = value;
}
list.Add(value.GetBent(vertex));
}
MeshUtility.Update(result, source.Mesh, source.Triangles, list.Select((MeshVertex b) => b.position), list.Select((MeshVertex b) => b.normal));
if (TryGetComponent<MeshCollider>(out var component))
{
component.sharedMesh = result;
}
}
}
}