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 sampleCache = new Dictionary(); 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().sharedMesh != null) { result = GetComponent().sharedMesh; return; } GetComponent().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 list = new List(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 list = new List(); List list2 = new List(); List list3 = new List(); List list4 = new List(); List list5 = new List(); List list6 = new List(); List list7 = new List(); List list8 = new List(); List list9 = new List(); 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 list10 = new List(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 list = new List(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(out var component)) { component.sharedMesh = result; } } } }