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()
};
}
}
}