| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- using UnityEngine;
- using System;
- using Cinemachine.Utility;
- namespace Cinemachine
- {
- /// <summary>Defines a world-space path, consisting of an array of waypoints,
- /// each of which has position and roll settings. Bezier interpolation
- /// is performed between the waypoints, to get a smooth and continuous path.
- /// The path will pass through all waypoints, and (unlike CinemachinePath) first
- /// and second order continuity is guaranteed</summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
- [AddComponentMenu("Cinemachine/CinemachineSmoothPath")]
- [SaveDuringPlay]
- [DisallowMultipleComponent]
- [HelpURL(Documentation.BaseURL + "manual/CinemachineSmoothPath.html")]
- public class CinemachineSmoothPath : CinemachinePathBase
- {
- /// <summary>If checked, then the path ends are joined to form a continuous loop</summary>
- [Tooltip("If checked, then the path ends are joined to form a continuous loop.")]
- public bool m_Looped;
- /// <summary>A waypoint along the path</summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
- [Serializable] public struct Waypoint
- {
- /// <summary>Position in path-local space</summary>
- [Tooltip("Position in path-local space")]
- public Vector3 position;
- /// <summary>Defines the roll of the path at this waypoint.
- /// The other orientation axes are inferred from the tangent and world up.</summary>
- [Tooltip("Defines the roll of the path at this waypoint. The other orientation axes are inferred from the tangent and world up.")]
- public float roll;
- /// <summary>Representation as Vector4</summary>
- internal Vector4 AsVector4
- {
- get { return new Vector4(position.x, position.y, position.z, roll); }
- }
- internal static Waypoint FromVector4(Vector4 v)
- {
- Waypoint wp = new Waypoint();
- wp.position = new Vector3(v[0], v[1], v[2]);
- wp.roll = v[3];
- return wp;
- }
- }
- /// <summary>The waypoints that define the path.
- /// They will be interpolated using a bezier curve</summary>
- [Tooltip("The waypoints that define the path. They will be interpolated using a bezier curve.")]
- public Waypoint[] m_Waypoints = new Waypoint[0];
- /// <summary>The minimum value for the path position</summary>
- public override float MinPos { get { return 0; } }
- /// <summary>The maximum value for the path position</summary>
- public override float MaxPos
- {
- get
- {
- int count = m_Waypoints.Length - 1;
- if (count < 1)
- return 0;
- return m_Looped ? count + 1 : count;
- }
- }
- /// <summary>True if the path ends are joined to form a continuous loop</summary>
- public override bool Looped { get { return m_Looped; } }
- /// <summary>When calculating the distance cache, sample the path this many
- /// times between points</summary>
- public override int DistanceCacheSampleStepsPerSegment { get { return m_Resolution; } }
- private void OnValidate() { InvalidateDistanceCache(); }
- private void Reset()
- {
- m_Looped = false;
- m_Waypoints = new Waypoint[2]
- {
- new Waypoint { position = new Vector3(0, 0, -5) },
- new Waypoint { position = new Vector3(0, 0, 5) }
- };
- m_Appearance = new Appearance();
- InvalidateDistanceCache();
- }
- /// <summary>Call this if the path changes in such a way as to affect distances
- /// or other cached path elements</summary>
- public override void InvalidateDistanceCache()
- {
- base.InvalidateDistanceCache();
- m_ControlPoints1 = null;
- m_ControlPoints2 = null;
- }
- Waypoint[] m_ControlPoints1;
- Waypoint[] m_ControlPoints2;
- bool m_IsLoopedCache;
- void UpdateControlPoints()
- {
- int numPoints = (m_Waypoints == null) ? 0 : m_Waypoints.Length;
- if (numPoints > 1
- && (Looped != m_IsLoopedCache
- || m_ControlPoints1 == null || m_ControlPoints1.Length != numPoints
- || m_ControlPoints2 == null || m_ControlPoints2.Length != numPoints))
- {
- Vector4[] p1 = new Vector4[numPoints];
- Vector4[] p2 = new Vector4[numPoints];
- Vector4[] K = new Vector4[numPoints];
- for (int i = 0; i < numPoints; ++i)
- K[i] = m_Waypoints[i].AsVector4;
- if (Looped)
- SplineHelpers.ComputeSmoothControlPointsLooped(ref K, ref p1, ref p2);
- else
- SplineHelpers.ComputeSmoothControlPoints(ref K, ref p1, ref p2);
- m_ControlPoints1 = new Waypoint[numPoints];
- m_ControlPoints2 = new Waypoint[numPoints];
- for (int i = 0; i < numPoints; ++i)
- {
- m_ControlPoints1[i] = Waypoint.FromVector4(p1[i]);
- m_ControlPoints2[i] = Waypoint.FromVector4(p2[i]);
- }
- m_IsLoopedCache = Looped;
- }
- }
- /// <summary>Returns standardized position</summary>
- float GetBoundingIndices(float pos, out int indexA, out int indexB)
- {
- pos = StandardizePos(pos);
- int numWaypoints = m_Waypoints.Length;
- if (numWaypoints < 2)
- indexA = indexB = 0;
- else
- {
- indexA = Mathf.FloorToInt(pos);
- if (indexA >= numWaypoints)
- {
- // Only true if looped
- pos -= MaxPos;
- indexA = 0;
- }
- indexB = indexA + 1;
- if (indexB == numWaypoints)
- {
- if (Looped)
- indexB = 0;
- else
- {
- --indexB;
- --indexA;
- }
- }
- }
- return pos;
- }
- /// <summary>Get a worldspace position of a point along the path</summary>
- /// <param name="pos">Position along the path. Need not be normalized.</param>
- /// <returns>World-space position of the point along at path at pos</returns>
- public override Vector3 EvaluatePosition(float pos)
- {
- Vector3 result = Vector3.zero;
- if (m_Waypoints.Length > 0)
- {
- UpdateControlPoints();
- int indexA, indexB;
- pos = GetBoundingIndices(pos, out indexA, out indexB);
- if (indexA == indexB)
- result = m_Waypoints[indexA].position;
- else
- result = SplineHelpers.Bezier3(pos - indexA,
- m_Waypoints[indexA].position, m_ControlPoints1[indexA].position,
- m_ControlPoints2[indexA].position, m_Waypoints[indexB].position);
- }
- return transform.TransformPoint(result);
- }
- /// <summary>Get the tangent of the curve at a point along the path.</summary>
- /// <param name="pos">Position along the path. Need not be normalized.</param>
- /// <returns>World-space direction of the path tangent.
- /// Length of the vector represents the tangent strength</returns>
- public override Vector3 EvaluateTangent(float pos)
- {
- Vector3 result = transform.rotation * Vector3.forward;
- if (m_Waypoints.Length > 1)
- {
- UpdateControlPoints();
- int indexA, indexB;
- pos = GetBoundingIndices(pos, out indexA, out indexB);
- if (!Looped && indexA == m_Waypoints.Length - 1)
- --indexA;
- result = SplineHelpers.BezierTangent3(pos - indexA,
- m_Waypoints[indexA].position, m_ControlPoints1[indexA].position,
- m_ControlPoints2[indexA].position, m_Waypoints[indexB].position);
- }
- return transform.TransformDirection(result);
- }
- /// <summary>Get the orientation the curve at a point along the path.</summary>
- /// <param name="pos">Position along the path. Need not be normalized.</param>
- /// <returns>World-space orientation of the path, as defined by tangent, up, and roll.</returns>
- public override Quaternion EvaluateOrientation(float pos)
- {
- Quaternion transformRot = transform.rotation;
- Vector3 transformUp = transformRot * Vector3.up;
- Quaternion result = transformRot;
- if (m_Waypoints.Length > 0)
- {
- float roll = 0;
- int indexA, indexB;
- pos = GetBoundingIndices(pos, out indexA, out indexB);
- if (indexA == indexB)
- roll = m_Waypoints[indexA].roll;
- else
- {
- UpdateControlPoints();
- roll = SplineHelpers.Bezier1(pos - indexA,
- m_Waypoints[indexA].roll, m_ControlPoints1[indexA].roll,
- m_ControlPoints2[indexA].roll, m_Waypoints[indexB].roll);
- }
- Vector3 fwd = EvaluateTangent(pos);
- if (!fwd.AlmostZero())
- {
- Quaternion q = Quaternion.LookRotation(fwd, transformUp);
- result = q * RollAroundForward(roll);
- }
- }
- return result;
- }
-
- // same as Quaternion.AngleAxis(roll, Vector3.forward), just simplified
- Quaternion RollAroundForward(float angle)
- {
- float halfAngle = angle * 0.5F * Mathf.Deg2Rad;
- return new Quaternion(
- 0,
- 0,
- Mathf.Sin(halfAngle),
- Mathf.Cos(halfAngle));
- }
-
- }
- }
|