using System; using Unity.Mathematics; namespace UnityEngine.Splines { /// /// Implement this class to create a customized shape that can be extruded along a using the /// class. /// /// Some default shape implementations are available in the namespace. /// /// generates extruded mesh geometry in the following manner (pseudo-code): /// /// extrudeShape.Setup(spline, numberOfSegments); /// for(int i = 0; i < numberOfSegments; ++i) /// { /// float t = i / (numberOfSegments - 1); /// extrudeShape.SetSegment(i, t, splinePositionAtT, splineTangentAtT, splineUpAtT); /// for(int n = 0; n < extrudeShape.SideCount; ++n) /// vertices.Add(extrudeShape.GetPosition(n / (extrudeShape.SideCount - 1), n)); /// } /// /// This example IExtrudeShape implementation creates a tube. /// /// /// ```lang-csharp /// m_Sides; /// set => m_Sides = value; /// } /// } /// ]]> /// ``` /// public interface IExtrudeShape { /// /// Implement this function to access information about the path being extruded and /// number of segments. invokes this method once prior to extruding the mesh. /// /// The that this template is being extruded along. /// The total number of segments to be created on the extruded mesh. This is /// equivalent to the number of vertex "rings" that make up the mesh positions. public void Setup(ISpline path, int segmentCount) {} /// /// Implement this function to access information about the spline path being extruded for each segment. /// invokes this method once before each ring of vertices is calculated. /// /// The segment index for the current vertex ring. /// The normalized interpolation ratio corresponding to the segment index. Equivalent to index divided by segmentCount - 1. /// The position on the path being extruded along at . /// The tangent on the path being extruded along at . /// The up vector on the path being extruded along at . public void SetSegment(int index, float t, float3 position, float3 tangent, float3 up) {} /// /// How many vertices make up a single ring around the mesh. /// /// How many vertices make up a revolution for each segment of the extruded mesh. public int SideCount { get; } /// /// This method is responsible for returning a 2D position of the template shape for each vertex of a single /// ring around the extruded mesh. /// Note that both interpolation and are provided as a convenience. /// /// The normalized interpolation [0...1] for a vertex around an extruded ring. /// The index of the vertex in the extruded ring. /// A 2D position interpolated along a template shape to be extruded. This value will be converted to /// a 3D point and rotated to align with the spline at the current segment index. public float2 GetPosition(float t, int index); } } namespace UnityEngine.Splines.ExtrusionShapes { // This is intentionally not public. It is used only by the SplineExtrudeEditor class to provide // an enum popup. // when updating this, make sure to also update ShapeTypeUtility.{GetShapeType, CreateShape} enum ShapeType { Circle, Square, Road, [InspectorName("Spline Profile")] Spline } static class ShapeTypeUtility { public static ShapeType GetShapeType(object obj) { return obj switch { Circle => ShapeType.Circle, Square => ShapeType.Square, Road => ShapeType.Road, SplineShape => ShapeType.Spline, _ => throw new ArgumentException($"{nameof(obj)} is not a recognized shape", nameof(obj)) }; } public static IExtrudeShape CreateShape(ShapeType type) { return type switch { ShapeType.Square => new Square(), ShapeType.Road => new Road(), ShapeType.Spline => new SplineShape(), ShapeType.Circle => new Circle(), _ => throw new ArgumentOutOfRangeException(nameof(type)) }; } } /// /// Create a circle shape to be extruded along a spline. /// /// /// /// /// [Serializable] public sealed class Circle : IExtrudeShape { [SerializeField, Min(2)] int m_Sides = 8; float m_Rads; /// public void Setup(ISpline path, int segmentCount) => m_Rads = math.radians(360f / SideCount); /// public float2 GetPosition(float t, int index) { return new float2(math.cos(index * m_Rads), math.sin(index * m_Rads)); } /// public int SideCount { get => m_Sides; set => m_Sides = value; } } /// /// Create a square shape to be extruded along a spline. /// /// /// /// [Serializable] public sealed class Square : IExtrudeShape { /// /// Square is fixed to 4 sides. public int SideCount => 4; static readonly float2[] k_Sides = new[] { new float2(-.5f, -.5f), new float2(.5f, -.5f), new float2(.5f, .5f), new float2(-.5f, .5f), }; /// public float2 GetPosition(float t, int index) => k_Sides[index]; } /// /// A simple plane with skirts at the edges to better blend with uneven terrain. /// /// /// /// /// [Serializable] public sealed class Road : IExtrudeShape { /// /// A road is fixed to 3 sides. public int SideCount => 3; static readonly float2[] k_Sides = new[] { new float2(-.6f, -.1f), new float2(-.5f, 0f), new float2( .5f, 0f), new float2( .6f, -.1f) }; /// public float2 GetPosition(float t, int index) => k_Sides[3-index]; } /// /// Create a shape using a as the template path. The /// referenced Spline is sampled at /// points along the path to form the vertex rings at each segment. /// [Serializable] public class SplineShape : IExtrudeShape { [SerializeField] SplineContainer m_Template; [SerializeField, SplineIndex(nameof(m_Template))] int m_SplineIndex; [SerializeField, Min(2)] int m_SideCount = 12; /// /// Defines the axes used to project the input spline template to 2D coordinates. /// public enum Axis { /// /// Project from the horizontal (X) axis. Uses the {Y, Z} components of /// the position to form the 2D template coordinates. /// X, /// /// Project from the vertical (Y) axis. Uses the {X, Z} components of /// the position to form the 2D template coordinates. /// Y, /// /// Project from the forward (Z) axis. Uses the {X, Y} components of /// the position to form the 2D template coordinates. /// Z } /// /// Defines the axes used to project the input spline template to 2D coordinates. /// [SerializeField, Tooltip("The axis of the template spline to be used when winding the vertices along the " + "extruded mesh.")] public Axis m_Axis = Axis.Y; /// public int SideCount { get => m_SideCount; set => m_SideCount = value; } /// /// The which contains the /// to use as the shape template. /// public SplineContainer SplineContainer { get => m_Template; set => m_Template = value; } /// /// The index of the in the /// to use as the shape template. This value must be greater than or /// equal to 0. If the index is out of bounds of the container spline /// count, the modulo of SplineIndex and Container.Splines.Count is used /// as the index. /// public int SplineIndex { get => m_SplineIndex; set => m_SplineIndex = math.max(0, value); } /// /// Returns the referenced by the /// and . /// public Spline Spline => m_Template != null ? m_Template[m_SplineIndex % m_Template.Splines.Count] : null; /// public float2 GetPosition(float t, int index) { if (Spline == null) return 0f; if (t == 1) t = 0.9999f; else if (t == 0) t = 0.0001f; return m_Axis switch { Axis.X => Spline.EvaluatePosition(1 - t).zy, Axis.Y => Spline.EvaluatePosition(1 - t).xz, Axis.Z => Spline.EvaluatePosition(1 - t).xy, _ => throw new ArgumentOutOfRangeException() }; } } }