using UnityEngine; using System.Collections; namespace Dreamteck.Splines { [ExecuteInEditMode] [AddComponentMenu("Dreamteck/Splines/Node Connector")] public class Node : MonoBehaviour { [System.Serializable] public class Connection { public SplineComputer spline { get { return _computer; } } public int pointIndex { get { return _pointIndex; } } public bool invertTangents = false; [SerializeField] private int _pointIndex = 0; [SerializeField] private SplineComputer _computer = null; [SerializeField] [HideInInspector] internal SplinePoint point; internal bool isValid { get { if (_computer == null) return false; if (_pointIndex >= _computer.pointCount) return false; return true; } } internal Connection(SplineComputer comp, int index, SplinePoint inputPoint) { _pointIndex = index; _computer = comp; point = inputPoint; } } public enum Type { Smooth, Free } [HideInInspector] public Type type = Type.Smooth; public bool transformNormals { get { return _transformNormals; } set { if (value != _transformNormals) { _transformNormals = value; UpdatePoints(); } } } public bool transformSize { get { return _transformSize; } set { if (value != _transformSize) { _transformSize = value; UpdatePoints(); } } } public bool transformTangents { get { return _transformTangents; } set { if (value != _transformTangents) { _transformTangents = value; UpdatePoints(); } } } [SerializeField] [HideInInspector] protected Connection[] connections = new Connection[0]; [SerializeField] [HideInInspector] private bool _transformSize = true; [SerializeField] [HideInInspector] private bool _transformNormals = true; [SerializeField] [HideInInspector] private bool _transformTangents = true; private Vector3 _lastPosition, _lastScale; private Quaternion _lastRotation; private Transform _trs; private void Awake() { _trs = transform; SampleTransform(); } void LateUpdate() { Run(); } void Update() { Run(); } bool TransformChanged() { #if UNITY_EDITOR if(_trs == null) return _lastPosition != transform.position || _lastRotation != transform.rotation || _lastScale != transform.lossyScale; #endif return _lastPosition != _trs.position || _lastRotation != _trs.rotation || _lastScale != _trs.lossyScale; } void SampleTransform() { #if UNITY_EDITOR if (!Application.isPlaying) { _lastPosition = transform.position; _lastScale = transform.lossyScale; _lastRotation = transform.rotation; } else { _lastPosition = _trs.position; _lastScale = _trs.lossyScale; _lastRotation = _trs.rotation; } return; #else _lastPosition = _trs.position; _lastScale = _trs.lossyScale; _lastRotation = _trs.rotation; #endif } private void Run() { if (TransformChanged()) { #if UNITY_EDITOR if (!Application.isPlaying) { UnityEditor.EditorUtility.SetDirty(this); for (int i = 0; i < connections.Length; i++) { UnityEditor.EditorUtility.SetDirty(connections[i].spline); } } #endif UpdateConnectedComputers(); SampleTransform(); } } public SplinePoint GetPoint(int connectionIndex, bool swapTangents) { SplinePoint point = PointToWorld(connections[connectionIndex].point); if (connections[connectionIndex].invertTangents && swapTangents) { Vector3 tempTan = point.tangent; point.tangent = point.tangent2; point.tangent2 = tempTan; } return point; } public void SetPoint(int connectionIndex, SplinePoint worldPoint, bool swappedTangents) { Connection connection = connections[connectionIndex]; connection.point = PointToLocal(worldPoint); if (connection.invertTangents && swappedTangents) { Vector3 tempTan = connection.point.tangent; connection.point.tangent = connection.point.tangent2; connection.point.tangent2 = tempTan; } if (type == Type.Smooth) { if (connection.point.type == SplinePoint.Type.SmoothFree) { for (int i = 0; i < connections.Length; i++) { if (i == connectionIndex) continue; Vector3 tanDir = (connection.point.tangent - connection.point.position).normalized; if (tanDir == Vector3.zero) tanDir = -(connection.point.tangent2 - connection.point.position).normalized; float tan1Length = (connections[i].point.tangent - connections[i].point.position).magnitude; float tan2Length = (connections[i].point.tangent2 - connections[i].point.position).magnitude; connections[i].point = connection.point; connections[i].point.tangent = connections[i].point.position + tanDir * tan1Length; connections[i].point.tangent2 = connections[i].point.position - tanDir * tan2Length; } } else { for (int i = 0; i < connections.Length; i++) { if (i == connectionIndex) continue; connections[i].point = connection.point; } } } } void OnDestroy() { ClearConnections(); } public void ClearConnections() { for (int i = connections.Length-1; i >= 0; i--) { if (connections[i].spline != null) connections[i].spline.DisconnectNode(connections[i].pointIndex); } connections = new Connection[0]; } public void UpdateConnectedComputers(SplineComputer excludeComputer = null) { for (int i = connections.Length - 1; i >= 0; i--) { if (!connections[i].isValid) { RemoveConnection(i); continue; } if (connections[i].spline == excludeComputer) continue; if (type == Type.Smooth && i != 0) { SetPoint(i, GetPoint(0, false), false); } SplinePoint point = GetPoint(i, true); if (!transformNormals) { point.normal = connections[i].spline.GetPointNormal(connections[i].pointIndex); } if (!transformTangents) { point.tangent = connections[i].spline.GetPointTangent(connections[i].pointIndex); point.tangent2 = connections[i].spline.GetPointTangent2(connections[i].pointIndex); } if (!transformSize) { point.size = connections[i].spline.GetPointSize(connections[i].pointIndex); } connections[i].spline.SetPoint(connections[i].pointIndex, point); } } public void UpdatePoint(SplineComputer computer, int pointIndex, SplinePoint point, bool updatePosition = true) { #if UNITY_EDITOR if (!Application.isPlaying) { transform.position = point.position; } else { _trs.position = point.position; } #else _trs.position = point.position; #endif for (int i = 0; i < connections.Length; i++) { if (connections[i].spline == computer && connections[i].pointIndex == pointIndex) { SetPoint(i, point, true); } } } public void UpdatePoints() { for (int i = connections.Length - 1; i >= 0; i--) { if (!connections[i].isValid) { RemoveConnection(i); continue; } SplinePoint point = connections[i].spline.GetPoint(connections[i].pointIndex); point.SetPosition(transform.position); SetPoint(i, point, true); } } #if UNITY_EDITOR //Use this to maintain the connections between computers in the editor public void EditorMaintainConnections() { RemoveInvalidConnections(); } #endif //Remove invalid connections protected void RemoveInvalidConnections() { for (int i = connections.Length - 1; i >= 0; i--) { if (connections[i] == null || !connections[i].isValid) RemoveConnection(i); } } public virtual void AddConnection(SplineComputer computer, int pointIndex) { RemoveInvalidConnections(); Node connected = computer.GetNode(pointIndex); if (connected != null) { Debug.LogError(computer.name + " is already connected to node " + connected.name + " at point " + pointIndex); return; } SplinePoint point = computer.GetPoint(pointIndex); point.SetPosition(transform.position); ArrayUtility.Add(ref connections, new Connection(computer, pointIndex, PointToLocal(point))); if(connections.Length == 1) SetPoint(connections.Length - 1, point, true); UpdateConnectedComputers(); } protected SplinePoint PointToLocal(SplinePoint worldPoint) { worldPoint.position = Vector3.zero; worldPoint.tangent = transform.InverseTransformPoint(worldPoint.tangent); worldPoint.tangent2 = transform.InverseTransformPoint(worldPoint.tangent2); worldPoint.normal = transform.InverseTransformDirection(worldPoint.normal); worldPoint.size /= (transform.localScale.x + transform.localScale.y + transform.localScale.z)/ 3f; return worldPoint; } protected SplinePoint PointToWorld(SplinePoint localPoint) { localPoint.position = transform.position; localPoint.tangent = transform.TransformPoint(localPoint.tangent); localPoint.tangent2 = transform.TransformPoint(localPoint.tangent2); localPoint.normal = transform.TransformDirection(localPoint.normal); localPoint.size *= (transform.localScale.x + transform.localScale.y + transform.localScale.z) / 3f; return localPoint; } public virtual void RemoveConnection(SplineComputer computer, int pointIndex) { int index = -1; for (int i = 0; i < connections.Length; i++) { if (connections[i].pointIndex == pointIndex && connections[i].spline == computer) { index = i; break; } } if (index < 0) return; RemoveConnection(index); } private void RemoveConnection(int index) { Connection[] newConnections = new Connection[connections.Length - 1]; SplineComputer spline = connections[index].spline; int pointIndex = connections[index].pointIndex; for (int i = 0; i < connections.Length; i++) { if (i < index) newConnections[i] = connections[i]; else if (i == index) continue; else newConnections[i - 1] = connections[i]; } connections = newConnections; } public virtual bool HasConnection(SplineComputer computer, int pointIndex) { for (int i = connections.Length - 1; i >= 0; i--) { if (!connections[i].isValid) { RemoveConnection(i); continue; } if (connections[i].spline == computer && connections[i].pointIndex == pointIndex) return true; } return false; } public Connection[] GetConnections() { return connections; } } }