459 lines
13 KiB
C#
459 lines
13 KiB
C#
using UnityEngine;
|
|
|
|
namespace BezierSolution
|
|
{
|
|
[AddComponentMenu( "Bezier Solution/Bezier Point" )]
|
|
[HelpURL( "https://github.com/yasirkula/UnityBezierSolution" )]
|
|
public partial class BezierPoint : MonoBehaviour
|
|
{
|
|
public Vector3 localPosition
|
|
{
|
|
get { return transform.localPosition; }
|
|
set
|
|
{
|
|
if( transform.localPosition == value )
|
|
return;
|
|
|
|
transform.localPosition = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange | InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
#pragma warning disable 0649
|
|
[SerializeField, HideInInspector]
|
|
private Vector3 m_position;
|
|
public Vector3 position
|
|
{
|
|
get { return m_position; }
|
|
set
|
|
{
|
|
if( transform.position == value )
|
|
return;
|
|
|
|
transform.position = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange | InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
public Quaternion localRotation
|
|
{
|
|
get { return transform.localRotation; }
|
|
set
|
|
{
|
|
if( transform.localRotation == value )
|
|
return;
|
|
|
|
transform.localRotation = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange | InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
public Quaternion rotation
|
|
{
|
|
get { return transform.rotation; }
|
|
set
|
|
{
|
|
if( transform.rotation == value )
|
|
return;
|
|
|
|
transform.rotation = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange | InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
public Vector3 localEulerAngles
|
|
{
|
|
get { return transform.localEulerAngles; }
|
|
set
|
|
{
|
|
if( transform.localEulerAngles == value )
|
|
return;
|
|
|
|
transform.localEulerAngles = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange | InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
public Vector3 eulerAngles
|
|
{
|
|
get { return transform.eulerAngles; }
|
|
set
|
|
{
|
|
if( transform.eulerAngles == value )
|
|
return;
|
|
|
|
transform.eulerAngles = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange | InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
public Vector3 localScale
|
|
{
|
|
get { return transform.localScale; }
|
|
set
|
|
{
|
|
if( transform.localScale == value )
|
|
return;
|
|
|
|
transform.localScale = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange | InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
private Vector3 m_precedingControlPointLocalPosition = Vector3.left;
|
|
public Vector3 precedingControlPointLocalPosition
|
|
{
|
|
get { return m_precedingControlPointLocalPosition; }
|
|
set
|
|
{
|
|
if( m_precedingControlPointLocalPosition == value )
|
|
return;
|
|
|
|
m_precedingControlPointLocalPosition = value;
|
|
m_precedingControlPointPosition = transform.TransformPoint( value );
|
|
|
|
if( m_handleMode == HandleMode.Aligned )
|
|
{
|
|
m_followingControlPointLocalPosition = -m_precedingControlPointLocalPosition.normalized * m_followingControlPointLocalPosition.magnitude;
|
|
m_followingControlPointPosition = transform.TransformPoint( m_followingControlPointLocalPosition );
|
|
}
|
|
else if( m_handleMode == HandleMode.Mirrored )
|
|
{
|
|
m_followingControlPointLocalPosition = -m_precedingControlPointLocalPosition;
|
|
m_followingControlPointPosition = transform.TransformPoint( m_followingControlPointLocalPosition );
|
|
}
|
|
|
|
spline.dirtyFlags |= InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
private Vector3 m_precedingControlPointPosition;
|
|
public Vector3 precedingControlPointPosition
|
|
{
|
|
get { return m_precedingControlPointPosition; }
|
|
set
|
|
{
|
|
if( m_precedingControlPointPosition == value )
|
|
return;
|
|
|
|
m_precedingControlPointPosition = value;
|
|
m_precedingControlPointLocalPosition = transform.InverseTransformPoint( value );
|
|
|
|
if( transform.hasChanged )
|
|
{
|
|
m_position = transform.position;
|
|
m_followingControlPointPosition = transform.TransformPoint( m_followingControlPointLocalPosition );
|
|
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange;
|
|
transform.hasChanged = false;
|
|
}
|
|
|
|
if( m_handleMode == HandleMode.Aligned )
|
|
{
|
|
m_followingControlPointPosition = m_position - ( m_precedingControlPointPosition - m_position ).normalized *
|
|
( m_followingControlPointPosition - m_position ).magnitude;
|
|
m_followingControlPointLocalPosition = transform.InverseTransformPoint( m_followingControlPointPosition );
|
|
}
|
|
else if( m_handleMode == HandleMode.Mirrored )
|
|
{
|
|
m_followingControlPointPosition = 2f * m_position - m_precedingControlPointPosition;
|
|
m_followingControlPointLocalPosition = transform.InverseTransformPoint( m_followingControlPointPosition );
|
|
}
|
|
|
|
spline.dirtyFlags |= InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
private Vector3 m_followingControlPointLocalPosition = Vector3.right;
|
|
public Vector3 followingControlPointLocalPosition
|
|
{
|
|
get { return m_followingControlPointLocalPosition; }
|
|
set
|
|
{
|
|
if( m_followingControlPointLocalPosition == value )
|
|
return;
|
|
|
|
m_followingControlPointLocalPosition = value;
|
|
m_followingControlPointPosition = transform.TransformPoint( value );
|
|
|
|
if( m_handleMode == HandleMode.Aligned )
|
|
{
|
|
m_precedingControlPointLocalPosition = -m_followingControlPointLocalPosition.normalized * m_precedingControlPointLocalPosition.magnitude;
|
|
m_precedingControlPointPosition = transform.TransformPoint( m_precedingControlPointLocalPosition );
|
|
}
|
|
else if( m_handleMode == HandleMode.Mirrored )
|
|
{
|
|
m_precedingControlPointLocalPosition = -m_followingControlPointLocalPosition;
|
|
m_precedingControlPointPosition = transform.TransformPoint( m_precedingControlPointLocalPosition );
|
|
}
|
|
|
|
spline.dirtyFlags |= InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
private Vector3 m_followingControlPointPosition;
|
|
public Vector3 followingControlPointPosition
|
|
{
|
|
get { return m_followingControlPointPosition; }
|
|
set
|
|
{
|
|
if( m_followingControlPointPosition == value )
|
|
return;
|
|
|
|
m_followingControlPointPosition = value;
|
|
m_followingControlPointLocalPosition = transform.InverseTransformPoint( value );
|
|
|
|
if( transform.hasChanged )
|
|
{
|
|
m_position = transform.position;
|
|
m_precedingControlPointPosition = transform.TransformPoint( m_precedingControlPointLocalPosition );
|
|
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange;
|
|
transform.hasChanged = false;
|
|
}
|
|
|
|
if( m_handleMode == HandleMode.Aligned )
|
|
{
|
|
m_precedingControlPointPosition = m_position - ( m_followingControlPointPosition - m_position ).normalized *
|
|
( m_precedingControlPointPosition - m_position ).magnitude;
|
|
m_precedingControlPointLocalPosition = transform.InverseTransformPoint( m_precedingControlPointPosition );
|
|
}
|
|
else if( m_handleMode == HandleMode.Mirrored )
|
|
{
|
|
m_precedingControlPointPosition = 2f * m_position - m_followingControlPointPosition;
|
|
m_precedingControlPointLocalPosition = transform.InverseTransformPoint( m_precedingControlPointPosition );
|
|
}
|
|
|
|
spline.dirtyFlags |= InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
private HandleMode m_handleMode = HandleMode.Mirrored;
|
|
public HandleMode handleMode
|
|
{
|
|
get { return m_handleMode; }
|
|
set
|
|
{
|
|
if( m_handleMode == value )
|
|
return;
|
|
|
|
m_handleMode = value;
|
|
|
|
if( value == HandleMode.Aligned || value == HandleMode.Mirrored )
|
|
{
|
|
// Temporarily change the value of m_precedingControlPointLocalPosition so that it will be different from precedingControlPointLocalPosition
|
|
// and precedingControlPointLocalPosition's setter will run
|
|
Vector3 _precedingControlPointLocalPosition = m_precedingControlPointLocalPosition;
|
|
m_precedingControlPointLocalPosition -= Vector3.one;
|
|
|
|
precedingControlPointLocalPosition = _precedingControlPointLocalPosition;
|
|
}
|
|
|
|
spline.dirtyFlags |= InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
[UnityEngine.Serialization.FormerlySerializedAs( "normal" )]
|
|
private Vector3 m_normal = Vector3.up;
|
|
public Vector3 normal
|
|
{
|
|
get { return m_normal; }
|
|
set
|
|
{
|
|
if( m_normal == value )
|
|
return;
|
|
|
|
m_normal = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.NormalChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
[UnityEngine.Serialization.FormerlySerializedAs( "autoCalculatedNormalAngleOffset" )]
|
|
private float m_autoCalculatedNormalAngleOffset = 0f;
|
|
public float autoCalculatedNormalAngleOffset
|
|
{
|
|
get { return m_autoCalculatedNormalAngleOffset; }
|
|
set
|
|
{
|
|
if( m_autoCalculatedNormalAngleOffset == value )
|
|
return;
|
|
|
|
m_autoCalculatedNormalAngleOffset = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.NormalOffsetChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
private Vector3[] m_intermediateNormals;
|
|
public Vector3[] intermediateNormals
|
|
{
|
|
get { return m_intermediateNormals; }
|
|
set
|
|
{
|
|
// In this special case, don't early exit if the assigned array is the same because one of its elements might have changed.
|
|
// We can safely early exit if the assigned value was null or empty, though
|
|
if( ( m_intermediateNormals == null || m_intermediateNormals.Length == 0 ) && ( value == null || value.Length == 0 ) )
|
|
return;
|
|
|
|
m_intermediateNormals = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.NormalChange;
|
|
}
|
|
}
|
|
|
|
[SerializeField, HideInInspector]
|
|
[UnityEngine.Serialization.FormerlySerializedAs( "extraData" )]
|
|
private ExtraData m_extraData;
|
|
public ExtraData extraData
|
|
{
|
|
get { return m_extraData; }
|
|
set
|
|
{
|
|
if( m_extraData == value )
|
|
return;
|
|
|
|
m_extraData = value;
|
|
spline.dirtyFlags |= InternalDirtyFlags.ExtraDataChange;
|
|
}
|
|
}
|
|
#pragma warning restore 0649
|
|
|
|
public BezierSpline spline { get; internal set; }
|
|
public int index { get; internal set; }
|
|
|
|
public BezierPoint previousPoint
|
|
{
|
|
get
|
|
{
|
|
if( spline )
|
|
{
|
|
if( index > 0 )
|
|
return spline.endPoints[index - 1];
|
|
else if( spline.loop )
|
|
return spline.endPoints[spline.endPoints.Count - 1];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public BezierPoint nextPoint
|
|
{
|
|
get
|
|
{
|
|
if( spline )
|
|
{
|
|
if( index < spline.endPoints.Count - 1 )
|
|
return spline.endPoints[index + 1];
|
|
else if( spline.loop )
|
|
return spline.endPoints[0];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
transform.hasChanged = true;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if( spline )
|
|
spline.dirtyFlags |= InternalDirtyFlags.All;
|
|
}
|
|
|
|
public void CopyTo( BezierPoint other )
|
|
{
|
|
other.transform.localPosition = transform.localPosition;
|
|
other.transform.localRotation = transform.localRotation;
|
|
other.transform.localScale = transform.localScale;
|
|
|
|
other.m_handleMode = m_handleMode;
|
|
|
|
other.m_precedingControlPointLocalPosition = m_precedingControlPointLocalPosition;
|
|
other.m_followingControlPointLocalPosition = m_followingControlPointLocalPosition;
|
|
|
|
other.m_normal = m_normal;
|
|
other.m_autoCalculatedNormalAngleOffset = m_autoCalculatedNormalAngleOffset;
|
|
|
|
other.m_extraData = m_extraData;
|
|
}
|
|
|
|
public void Refresh()
|
|
{
|
|
m_position = transform.position;
|
|
m_precedingControlPointPosition = transform.TransformPoint( m_precedingControlPointLocalPosition );
|
|
m_followingControlPointPosition = transform.TransformPoint( m_followingControlPointLocalPosition );
|
|
|
|
transform.hasChanged = false;
|
|
}
|
|
|
|
internal void RefreshIfChanged()
|
|
{
|
|
if( transform.hasChanged )
|
|
{
|
|
Refresh();
|
|
spline.dirtyFlags |= InternalDirtyFlags.EndPointTransformChange | InternalDirtyFlags.ControlPointPositionChange;
|
|
}
|
|
}
|
|
|
|
internal void SetNormalAndResetIntermediateNormals( Vector3 normal, string undo )
|
|
{
|
|
if( spline && spline.autoCalculateNormals )
|
|
return;
|
|
|
|
#if UNITY_EDITOR
|
|
if( !string.IsNullOrEmpty( undo ) )
|
|
UnityEditor.Undo.RecordObject( this, undo );
|
|
#endif
|
|
|
|
this.normal = normal;
|
|
intermediateNormals = null;
|
|
|
|
BezierPoint previousPoint = this.previousPoint;
|
|
if( previousPoint && previousPoint.m_intermediateNormals != null && previousPoint.m_intermediateNormals.Length > 0 )
|
|
{
|
|
#if UNITY_EDITOR
|
|
if( !string.IsNullOrEmpty( undo ) )
|
|
UnityEditor.Undo.RecordObject( previousPoint, undo );
|
|
#endif
|
|
|
|
previousPoint.intermediateNormals = null;
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
localPosition = Vector3.zero;
|
|
localRotation = Quaternion.identity;
|
|
localScale = Vector3.one;
|
|
|
|
precedingControlPointLocalPosition = Vector3.left;
|
|
followingControlPointLocalPosition = Vector3.right;
|
|
|
|
m_normal = Vector3.up;
|
|
m_autoCalculatedNormalAngleOffset = 0f;
|
|
|
|
m_extraData = new ExtraData();
|
|
|
|
transform.hasChanged = true;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[ContextMenu( "Invert Spline" )]
|
|
private void InvertSplineContextMenu()
|
|
{
|
|
if( spline )
|
|
spline.InvertSpline( "Invert spline" );
|
|
}
|
|
#endif
|
|
}
|
|
} |