using System; using Unity.Mathematics; namespace UnityEngine.Splines { /// /// This struct contains position and tangent data for a knot. The position is a scalar point and the tangents are vectors. /// The class stores a collection of BezierKnot that form a series of connected /// . Each knot contains a Position, Tangent In, and Tangent Out. When a spline is not /// closed, the first and last knots will contain an extraneous tangent (in and out, respectively). /// [Serializable] public struct BezierKnot : ISerializationCallbackReceiver, IEquatable { /// /// The position of the knot. On a cubic Bezier curve, this is equivalent to or /// , depending on whether this knot is forming the first or second control point /// of the curve. /// public float3 Position; /// /// The tangent vector that leads into this knot. On a cubic Bezier curve, this value is used to calculate /// when used as the second knot in a curve. /// public float3 TangentIn; /// /// The tangent vector that follows this knot. On a cubic Bezier curve, this value is used to calculate /// when used as the first knot in a curve. /// public float3 TangentOut; /// /// Rotation of the knot. /// public quaternion Rotation; /// /// Create a new BezierKnot struct. /// /// The position of the knot relative to the spline. public BezierKnot(float3 position): this(position, 0f, 0f, quaternion.identity) { } /// /// Creates a new struct. /// /// The position of the knot relative to the spline. /// The leading tangent to this knot. /// The following tangent to this knot. public BezierKnot(float3 position, float3 tangentIn, float3 tangentOut) : this(position, tangentIn, tangentOut, quaternion.identity) { } /// /// Create a new BezierKnot struct. /// /// The position of the knot relative to the spline. /// The leading tangent to this knot. /// The following tangent to this knot. /// The rotation of the knot relative to the spline. public BezierKnot(float3 position, float3 tangentIn, float3 tangentOut, quaternion rotation) { Position = position; TangentIn = tangentIn; TangentOut = tangentOut; Rotation = rotation; } /// /// Multiply the position and tangents by a matrix. /// /// The matrix to multiply. /// A new BezierKnot multiplied by matrix. public BezierKnot Transform(float4x4 matrix) { var rotation = math.mul(new quaternion(matrix), Rotation); var invRotation = math.inverse(rotation); // Tangents need to be scaled, so rotation should be applied to them. // No need however to use the translation as this is only a direction. return new BezierKnot( math.transform(matrix, Position), math.rotate(invRotation, math.rotate(matrix, math.rotate(Rotation,TangentIn))), math.rotate(invRotation, math.rotate(matrix, math.rotate(Rotation,TangentOut))), rotation); } /// /// Knot position addition. This operation only applies to the position, tangents and rotation are unmodified. /// /// The target knot. /// The value to add. /// A new BezierKnot where position is the sum of knot.position and rhs. public static BezierKnot operator +(BezierKnot knot, float3 rhs) { return new BezierKnot(knot.Position + rhs, knot.TangentIn, knot.TangentOut, knot.Rotation); } /// /// Knot position subtraction. This operation only applies to the position, tangents and rotation are unmodified. /// /// The target knot. /// The value to subtract. /// A new BezierKnot where position is the sum of knot.position minus rhs. public static BezierKnot operator -(BezierKnot knot, float3 rhs) { return new BezierKnot(knot.Position - rhs, knot.TangentIn, knot.TangentOut, knot.Rotation); } internal BezierKnot BakeTangentDirectionToRotation(bool mirrored, BezierTangent main = BezierTangent.Out) { if (mirrored) { float lead = math.length(main == BezierTangent.In ? TangentIn : TangentOut); return new BezierKnot(Position, new float3(0f, 0f, -lead), new float3(0f, 0f, lead), SplineUtility.GetKnotRotation( math.mul(Rotation, main == BezierTangent.In ? -TangentIn : TangentOut), math.mul(Rotation, math.up()))); } return new BezierKnot(Position, new float3(0, 0, -math.length(TangentIn)), new float3(0, 0, math.length(TangentOut)), Rotation = SplineUtility.GetKnotRotation( math.mul(Rotation, main == BezierTangent.In ? -TangentIn : TangentOut), math.mul(Rotation, math.up()))); } /// /// See ISerializationCallbackReceiver. /// public void OnBeforeSerialize() {} /// /// See ISerializationCallbackReceiver. /// public void OnAfterDeserialize() { // Ensures that when adding the first knot via Unity inspector // or when deserializing knot that did not have the rotation field prior, // rotation is deserialized to identity instead of (0, 0, 0, 0) which does not represent a valid rotation. if (math.lengthsq(Rotation) == 0f) Rotation = quaternion.identity; } /// /// Create a string with the values of this knot. /// /// A summary of the values contained by this knot. public override string ToString() => $"{{{Position}, {TangentIn}, {TangentOut}, {Rotation}}}"; /// /// Compare two knots for equality. /// /// The knot to compare against. /// Returns true when the position, tangents, and rotation of each knot are identical. public bool Equals(BezierKnot other) { return Position.Equals(other.Position) && TangentIn.Equals(other.TangentIn) && TangentOut.Equals(other.TangentOut) && Rotation.Equals(other.Rotation); } /// /// Compare against an object for equality. /// /// The object to compare against. /// /// Returns true when is a and the values of each knot are /// identical. /// public override bool Equals(object obj) { return obj is BezierKnot other && Equals(other); } /// /// Calculate a hash code for this knot. /// /// /// A hash code for the knot. /// public override int GetHashCode() { return HashCode.Combine(Position, TangentIn, TangentOut, Rotation); } } }