Advertisement
Staggart

Spline Looping

Sep 30th, 2024
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 6.54 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using Unity.Mathematics;
  4. using UnityEngine;
  5. using UnityEngine.Splines;
  6. #if UNITY_EDITOR
  7. using UnityEditor;
  8. #endif
  9.  
  10. namespace sc.modeling.splines.runtime.auxiliary
  11. {
  12.     public class SplineLooping : MonoBehaviour
  13.     {
  14.         public SplineContainer splineContainer;
  15.         [Min(1f)]
  16.         public float knotDistance = 2f;
  17.        
  18.         public enum Direction
  19.         {
  20.             Right,
  21.             Left
  22.         }
  23.  
  24.         [Space]
  25.         public Direction direction;
  26.         public TextAlignment xAlignment = TextAlignment.Center;
  27.         public TextAlignment zAlignment = TextAlignment.Center;
  28.        
  29.         [Header("Shape")]
  30.         [Min(5f)]
  31.         public float radius = 10f;
  32.         [Min(0f)]
  33.         public float width = 15f;
  34.         [Range(1, 3)]
  35.         public int loops = 1;
  36.        
  37.         [Header("Runway")]
  38.         [Min(1f)]
  39.         public float runwayLength = 15f;
  40.         [Min(0f)]
  41.         public float runwayHeight = 0f;
  42.        
  43.         [Header("Curvature")]
  44.         [Range(0f,1f)]
  45.         public float flattening = 1f;
  46.  
  47.         private void Reset()
  48.         {
  49.             splineContainer = GetComponent<SplineContainer>();
  50.             if(!splineContainer) splineContainer = gameObject.AddComponent<SplineContainer>();
  51.         }
  52.  
  53.         [Serializable]
  54.         private class Point
  55.         {
  56.             public Point(float3 position, float3 forward, float3 up, float3 right)
  57.             {
  58.                 this.position = position;
  59.                 this.forward = forward;
  60.                 this.up = up;
  61.                 this.right = right;
  62.             }
  63.             public float3 position;
  64.             public float3 forward;
  65.             public float3 up;
  66.             public float3 right;
  67.         }
  68.         private Point[] points = Array.Empty<Point>();
  69.  
  70.         public void Rebuild()
  71.         {
  72.             if (!splineContainer) return;
  73.            
  74.             int pointCount = (int)(math.ceil((radius + width) / knotDistance) * loops);
  75.             pointCount = Mathf.Max(8, pointCount);
  76.            
  77.             if (pointCount <= 1) return;
  78.            
  79.             for (int s = 0; s < splineContainer.Splines.Count; s++)
  80.             {
  81.                 splineContainer.RemoveSpline(splineContainer.Splines[s]);
  82.             }
  83.  
  84.             Spline spline = new Spline();
  85.            
  86.             float curAngle = -90f;
  87.             float angleLength = 360 * loops;
  88.             float angleIncrement = (angleLength / (float)pointCount);
  89.  
  90.             points = new Point[pointCount+1];
  91.  
  92.             float3 prevPosition = new float3(0f);
  93.             for (int i = 0; i <= pointCount; i++)
  94.             {
  95.                 float t = (float)i / (pointCount);
  96.  
  97.                 t = math.clamp(t, 0.00001f, 0.999999f);
  98.  
  99.                
  100.                 float r = Mathf.Deg2Rad * curAngle;
  101.                 float3 dir = new float3(
  102.                     math.sin(t * 2f - 1f) * 0.5f,
  103.                     math.sin(r),
  104.                     math.cos(r));
  105.                 if (direction == Direction.Left) dir.x = -dir.x;
  106.  
  107.                 //Negate x-component so that the direction stays level
  108.                 float3 up = -math.normalize(new float3(dir.x * (1f-flattening), dir.y, dir.z));
  109.                 float3 center = new float3(0f, dir.y, 0f);
  110.                 float3 right = math.normalize(dir - center);
  111.  
  112.                 right = math.lerp(right, math.right(), flattening);
  113.  
  114.                 float3 position = new Vector3(
  115.                     dir.x * width,
  116.                     dir.y * radius,
  117.                     dir.z * radius
  118.                     );
  119.  
  120.                 //Uncenter
  121.                 position.y += radius;
  122.  
  123.                 if (xAlignment == TextAlignment.Left) position.x += (width * 0.5f);
  124.                 if (xAlignment == TextAlignment.Right) position.x -= (width * 0.5f);
  125.                
  126.                 //First
  127.                 if (i == 0)
  128.                 {
  129.                     position.z = -runwayLength;
  130.                     right = Vector3.right;
  131.                     up = Vector3.up;
  132.                    
  133.                     //Unknown, so place it before the first point
  134.                     prevPosition = position + (-math.forward() * 2f);
  135.                 }
  136.                 //Last
  137.                 else if (i == pointCount)
  138.                 {
  139.                     position.z += runwayLength;
  140.                     right = Vector3.right;
  141.                     up = Vector3.up;
  142.                 }
  143.                 else
  144.                 {
  145.                     position.y += runwayHeight;
  146.                 }
  147.  
  148.                 if (zAlignment == TextAlignment.Left) position.z += (runwayLength);
  149.                 if (zAlignment == TextAlignment.Right) position.z -= (runwayLength);
  150.  
  151.                 float3 forward = math.normalize(position - prevPosition);
  152.                 prevPosition = position;
  153.                
  154.                 BezierKnot knot = new BezierKnot
  155.                 {
  156.                     Position = position,
  157.                     Rotation = Quaternion.LookRotation(-forward, up),
  158.                 };
  159.                 spline.Add(knot, TangentMode.AutoSmooth);
  160.  
  161.                 points[i] = new Point(position, forward, up, right);
  162.                
  163.                 curAngle += angleIncrement;
  164.             }
  165.    
  166.             //Adding a spline automatically rebuilds the mesh
  167.             splineContainer.AddSpline(spline);
  168.         }
  169.  
  170.         private void OnDrawGizmosSelected()
  171.         {
  172.             if (!splineContainer) return;
  173.            
  174.             Gizmos.matrix = splineContainer.transform.localToWorldMatrix;
  175.            
  176.             foreach (Point p in points)
  177.             {
  178.                 Gizmos.color = Color.blue;
  179.                 Gizmos.DrawLine(p.position, p.position + p.forward);
  180.  
  181.                 Gizmos.color = Color.green;
  182.                 Gizmos.DrawLine(p.position, p.position + p.up);
  183.  
  184.                 Gizmos.color = Color.red;
  185.                 Gizmos.DrawLine(p.position, p.position + p.right);
  186.             }
  187.         }
  188.     }
  189.    
  190.     #if UNITY_EDITOR
  191.     [CustomEditor(typeof(SplineLooping))]
  192.     [CanEditMultipleObjects]
  193.     public class SplineLoopingEditor : Editor
  194.     {
  195.         public override void OnInspectorGUI()
  196.         {
  197.             EditorGUI.BeginChangeCheck();
  198.  
  199.             base.OnInspectorGUI();
  200.  
  201.             if (EditorGUI.EndChangeCheck())
  202.             {
  203.                 foreach (var m_target in targets)
  204.                     ((SplineLooping)m_target).Rebuild();
  205.             }
  206.         }
  207.     }
  208.     #endif
  209. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement