318 lines
7.5 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|