ExtrusionSegment.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEngine;
  6. namespace SplineMesh {
  7. [ExecuteInEditMode]
  8. [DisallowMultipleComponent]
  9. [RequireComponent(typeof(MeshFilter))]
  10. [RequireComponent(typeof(MeshRenderer))]
  11. public class ExtrusionSegment : MonoBehaviour {
  12. private bool isDirty = false;
  13. private MeshFilter mf;
  14. private Mesh result;
  15. private bool useSpline = false;
  16. private CubicBezierCurve curve;
  17. private Spline spline;
  18. private float intervalStart, intervalEnd;
  19. private List<Vertex> shapeVertices = new List<Vertex>();
  20. /// <summary>
  21. ///
  22. /// </summary>
  23. public List<Vertex> ShapeVertices {
  24. get { return shapeVertices; }
  25. set {
  26. if (value == shapeVertices) return;
  27. SetDirty();
  28. shapeVertices = value;
  29. }
  30. }
  31. private float textureScale = 1;
  32. /// <summary>
  33. ///
  34. /// </summary>
  35. public float TextureScale {
  36. get { return textureScale; }
  37. set {
  38. if (value == textureScale) return;
  39. SetDirty();
  40. textureScale = value;
  41. }
  42. }
  43. private float textureOffset = 0;
  44. /// <summary>
  45. ///
  46. /// </summary>
  47. public float TextureOffset {
  48. get { return textureOffset; }
  49. set {
  50. if (value == textureOffset) return;
  51. SetDirty();
  52. textureOffset = value;
  53. }
  54. }
  55. private float sampleSpacing = 0.1f;
  56. /// <summary>
  57. ///
  58. /// </summary>
  59. public float SampleSpacing {
  60. get { return sampleSpacing; }
  61. set {
  62. if (value == sampleSpacing) return;
  63. if (value <= 0) throw new ArgumentOutOfRangeException("SampleSpacing", "Must be greater than 0");
  64. SetDirty();
  65. sampleSpacing = value;
  66. }
  67. }
  68. private void OnEnable() {
  69. mf = GetComponent<MeshFilter>();
  70. if (mf.sharedMesh == null) {
  71. mf.sharedMesh = new Mesh();
  72. }
  73. }
  74. /// <summary>
  75. /// Set the cubic Bézier curve to use to bend the source mesh, and begin to listen to curve control points for changes.
  76. /// </summary>
  77. /// <param name="curve"></param>
  78. /// <param name="update">If let to true, update the resulting mesh immediatly.</param>
  79. public void SetInterval(CubicBezierCurve curve) {
  80. if (this.curve == curve) return;
  81. if (curve == null) throw new ArgumentNullException("curve");
  82. if (this.curve != null) {
  83. this.curve.Changed.RemoveListener(SetDirty);
  84. }
  85. this.curve = curve;
  86. spline = null;
  87. curve.Changed.AddListener(SetDirty);
  88. useSpline = false;
  89. SetDirty();
  90. }
  91. public void SetInterval(Spline spline, float intervalStart, float intervalEnd = 0) {
  92. if (this.spline == spline && this.intervalStart == intervalStart && this.intervalEnd == intervalEnd) return;
  93. if (spline == null) throw new ArgumentNullException("spline");
  94. if (intervalStart < 0 || intervalStart >= spline.Length) {
  95. throw new ArgumentOutOfRangeException("interval start must be 0 or greater and lesser than spline length (was " + intervalStart + ")");
  96. }
  97. if (intervalEnd != 0 && intervalEnd <= intervalStart || intervalEnd > spline.Length) {
  98. throw new ArgumentOutOfRangeException("interval end must be 0 or greater than interval start, and lesser than spline length (was " + intervalEnd + ")");
  99. }
  100. if (this.spline != null) {
  101. // unlistening previous spline
  102. this.spline.CurveChanged.RemoveListener(SetDirty);
  103. }
  104. this.spline = spline;
  105. // listening new spline
  106. spline.CurveChanged.AddListener(SetDirty);
  107. curve = null;
  108. this.intervalStart = intervalStart;
  109. this.intervalEnd = intervalEnd;
  110. useSpline = true;
  111. SetDirty();
  112. }
  113. private void SetDirty() {
  114. isDirty = true;
  115. }
  116. private void LateUpdate() {
  117. ComputeIfNeeded();
  118. }
  119. public void ComputeIfNeeded() {
  120. if (isDirty) {
  121. Compute();
  122. isDirty = false;
  123. }
  124. }
  125. private List<CurveSample> GetPath() {
  126. var path = new List<CurveSample>();
  127. if (useSpline) {
  128. // calculate path from spline interval
  129. float d = intervalStart;
  130. while (d < intervalEnd) {
  131. path.Add(spline.GetSampleAtDistance(d));
  132. d += sampleSpacing;
  133. }
  134. path.Add(spline.GetSampleAtDistance(intervalEnd));
  135. } else {
  136. // calculate path in a curve
  137. float d = 0;
  138. while (d < curve.Length) {
  139. path.Add(curve.GetSampleAtDistance(d));
  140. d += sampleSpacing;
  141. }
  142. path.Add(curve.GetSampleAtDistance(curve.Length));
  143. }
  144. return path;
  145. }
  146. public void Compute() {
  147. List<CurveSample> path = GetPath();
  148. int vertsInShape = shapeVertices.Count;
  149. int segmentCount = path.Count - 1;
  150. var triangleIndices = new List<int>(vertsInShape * 2 * segmentCount * 3);
  151. var bentVertices = new List<MeshVertex>(vertsInShape * 2 * segmentCount * 3);
  152. foreach (var sample in path) {
  153. foreach (Vertex v in shapeVertices) {
  154. bentVertices.Add(sample.GetBent(new MeshVertex(
  155. new Vector3(0, v.point.y, -v.point.x),
  156. new Vector3(0, v.normal.y, -v.normal.x),
  157. new Vector2(v.uCoord, textureScale * (sample.distanceInCurve + textureOffset)))));
  158. }
  159. }
  160. var index = 0;
  161. for (int i = 0; i < segmentCount; i++) {
  162. for (int j = 0; j < shapeVertices.Count; j++) {
  163. int offset = j == shapeVertices.Count - 1 ? -(shapeVertices.Count - 1) : 1;
  164. int a = index + shapeVertices.Count;
  165. int b = index;
  166. int c = index + offset;
  167. int d = index + offset + shapeVertices.Count;
  168. triangleIndices.Add(c);
  169. triangleIndices.Add(b);
  170. triangleIndices.Add(a);
  171. triangleIndices.Add(a);
  172. triangleIndices.Add(d);
  173. triangleIndices.Add(c);
  174. index++;
  175. }
  176. }
  177. MeshUtility.Update(mf.sharedMesh,
  178. mf.sharedMesh,
  179. triangleIndices,
  180. bentVertices.Select(b => b.position),
  181. bentVertices.Select(b => b.normal),
  182. bentVertices.Select(b => b.uv));
  183. var mc = GetComponent<MeshCollider>();
  184. if(mc != null) {
  185. mc.sharedMesh = mf.sharedMesh;
  186. }
  187. }
  188. [Serializable]
  189. public class Vertex {
  190. public Vector2 point;
  191. public Vector2 normal;
  192. public float uCoord;
  193. public Vertex(Vector2 point, Vector2 normal, float uCoord) {
  194. this.point = point;
  195. this.normal = normal;
  196. this.uCoord = uCoord;
  197. }
  198. public Vertex(Vertex other) {
  199. this.point = other.point;
  200. this.normal = other.normal;
  201. this.uCoord = other.uCoord;
  202. }
  203. }
  204. }
  205. }