2024-11-19 11:48:21 +01:00

2400 lines
78 KiB
C#

#region "Imports"
using UnityEngine;
using System.Collections.Generic;
#endregion
namespace RoadArchitect
{
public class SplineC : MonoBehaviour
{
#region "Vars"
public List<SplineN> nodes = new List<SplineN>();
public GameObject splineRoot;
public Road road;
public float distance = -1f;
public Vector3[] cachedPoints;
private const float cachedPointsSeperation = 1f;
//Editor preview splines for add and insert:
public SplineF previewSpline;
public SplineI previewSplineInsert;
#region "Nav data Vars"
public float RoadWidth;
public int Lanes;
public List<int> connectedIDs;
public int id = 0;
//Unique ID
public string uID;
public List<KeyValuePair<float, float>> BridgeParams;
public List<KeyValuePair<float, float>> TunnelParams;
public List<KeyValuePair<float, float>> HeightHistory;
public int[] RoadDefKeysArray;
public float[] RoadDefValuesArray;
public double editorOnlyLastNodeTimeSinceStartup = -1f;
#endregion
#region "Vars for intersections"
private const float metersToCheckNoTurnLane = 75f;
private const float metersToCheckNoTurnLaneSQ = 5625f;
private const float metersToCheckTurnLane = 125f;
private const float metersToCheckTurnLaneSQ = 15625f;
private const float metersToCheckBothTurnLane = 125f;
private const float metersToCheckBothTurnLaneSQ = 15625f;
private const bool isUsingSQ = true;
#endregion
#region "Road connections and 3-way intersections"
public bool isSpecialStartControlNode = false;
public bool isSpecialEndControlNode = false;
public bool isSpecialEndNodeIsStartDelay = false;
public bool isSpecialEndNodeIsEndDelay = false;
public float specialEndNodeDelayStart = 10f;
public float specialEndNodeDelayStartResult = 10f;
public float specialEndNodeDelayEnd = 10f;
public float specialEndNodeDelayEndResult = 10f;
public SplineC specialEndNodeStartOtherSpline = null;
public SplineC specialEndNodeEndOtherSpline = null;
#endregion
public Vector2 RoadV0 = default(Vector2);
public Vector2 RoadV1 = default(Vector2);
public Vector2 RoadV2 = default(Vector2);
public Vector2 RoadV3 = default(Vector2);
#endregion
#region "Setup"
public void TriggerSetup()
{
if (!road)
{
if (splineRoot != null)
{
road = splineRoot.transform.parent.transform.gameObject.GetComponent<Road>();
}
}
if (road)
{
road.UpdateRoad();
}
}
/// <summary> Setup Spline values </summary>
public void Setup()
{
//Setup unique ID:
RootUtils.SetupUniqueIdentifier(ref uID);
//Set spline root:
splineRoot = transform.gameObject;
//Create spline nodes:
SplineN[] rawNodes = splineRoot.GetComponentsInChildren<SplineN>();
List<SplineN> nodeList = new List<SplineN>();
int rawNodesLength = rawNodes.Length;
if (rawNodesLength == 0)
{
return;
}
// Stores nodes positions in pos and adds them to nodeList
for (int i = 0; i < rawNodesLength; i++)
{
if (rawNodes[i] != null)
{
rawNodes[i].pos = rawNodes[i].transform.position;
nodeList.Add(rawNodes[i]);
}
}
nodeList.Sort(CompareListByID);
//tList.Sort(delegate(SplineC i1, Item i2) { return i1.name.CompareTo(i2.name); });
rawNodes = nodeList.ToArray();
nodeList = null;
SetupNodes(ref rawNodes);
//Setup spline length, if more than 1 node:
if (GetNodeCount() > 1)
{
//RootUtils.StartProfiling(road, "SplineSetupLength");
SetupSplineLength();
//RootUtils.EndProfiling(road);
}
else if (GetNodeCount() == 1)
{
nodes[0].time = 0f;
}
//Setup preview spline:
if (previewSpline == null)
{
previewSpline = splineRoot.AddComponent<SplineF>();
previewSpline.spline = this;
}
//Setup preview spline for insertion mode:
if (previewSplineInsert == null)
{
previewSplineInsert = splineRoot.AddComponent<SplineI>();
previewSplineInsert.spline = this;
}
int nodesCount = nodes.Count;
SplineN splineNode = null;
Vector3[] nodePositions = new Vector3[nodesCount + 1];
for (int i = 0; i < nodesCount; i++)
{
splineNode = nodes[i];
splineNode.idOnSpline = i;
splineNode.isEndPoint = false;
nodePositions[i] = splineNode.pos;
}
nodePositions[nodePositions.Length - 1] = new Vector3(0f, 0f, 0f);
previewSpline.Setup(ref nodePositions);
RenameNodes();
#region "Setup bridge params"
if (BridgeParams != null)
{
BridgeParams.Clear();
}
BridgeParams = new List<KeyValuePair<float, float>>();
KeyValuePair<float, float> KVP;
#endregion
//Setup tunnel params:
if (TunnelParams != null)
{
TunnelParams.Clear();
}
TunnelParams = new List<KeyValuePair<float, float>>();
if (nodesCount > 1)
{
if (isSpecialStartControlNode)
{
nodes[1].isEndPoint = true;
}
else
{
nodes[0].isEndPoint = true;
}
if (isSpecialEndControlNode)
{
nodes[nodesCount - 2].isEndPoint = true;
}
else
{
nodes[nodesCount - 1].isEndPoint = true;
}
}
else if (nodesCount == 1)
{
nodes[0].isEndPoint = true;
distance = 1;
}
float splineStart = -1f;
float splineEnd = -1f;
if (nodesCount > 1)
{
for (int i = 0; i < nodesCount; i++)
{
splineNode = nodes[i];
//Bridges:
splineStart = -1f;
splineEnd = -1f;
if (splineNode.isBridgeStart && !splineNode.isTunnelStart)
{
splineStart = splineNode.time;
for (int j = i; j < nodesCount; j++)
{
if (nodes[j].isBridgeEnd)
{
splineEnd = nodes[j].time;
break;
}
}
if (splineEnd > 0f || RootUtils.IsApproximately(splineEnd, 0f, 0.0001f))
{
KVP = new KeyValuePair<float, float>(splineStart, splineEnd);
BridgeParams.Add(KVP);
}
}
//Tunnels:
splineStart = -1f;
splineEnd = -1f;
if (!splineNode.isBridgeStart && splineNode.isTunnelStart)
{
splineStart = splineNode.time;
for (int j = i; j < nodesCount; j++)
{
if (nodes[j].isTunnelEnd)
{
splineEnd = nodes[j].time;
break;
}
}
if (splineEnd > 0f || RootUtils.IsApproximately(splineEnd, 0f, 0.0001f))
{
KVP = new KeyValuePair<float, float>(splineStart, splineEnd);
TunnelParams.Add(KVP);
}
}
splineNode.SetGradePercent(nodesCount);
//splineNode.isEndPoint = false;
splineNode.tangent = GetSplineValue(nodes[i].time, true);
if (i < (nodesCount - 1))
{
splineNode.nextTime = nodes[i + 1].time;
splineNode.nextTan = nodes[i + 1].tangent;
}
}
}
else if (nodesCount == 1)
{
nodes[0].tangent = default(Vector3);
}
//Get bounds of road system:
float[] maxEffects = new float[3];
maxEffects[0] = road.matchHeightsDistance;
maxEffects[1] = road.clearDetailsDistance;
maxEffects[2] = road.clearTreesDistance;
//Add min/max clear diff to bound checks
float maxEffectDistance = Mathf.Max(maxEffects) * 2f;
nodesCount = GetNodeCount();
float[] minMaxX = new float[nodesCount];
float[] minMaxZ = new float[nodesCount];
for (int i = 0; i < nodesCount; i++)
{
minMaxX[i] = nodes[i].pos.x;
minMaxZ[i] = nodes[i].pos.z;
}
// calculate the biggest and lowest x and z positions
float minX = Mathf.Min(minMaxX) - maxEffectDistance;
float maxX = Mathf.Max(minMaxX) + maxEffectDistance;
float minZ = Mathf.Min(minMaxZ) - maxEffectDistance;
float maxZ = Mathf.Max(minMaxZ) + maxEffectDistance;
RoadV0 = new Vector3(minX, minZ);
RoadV1 = new Vector3(maxX, minZ);
RoadV2 = new Vector3(maxX, maxZ);
RoadV3 = new Vector3(minX, maxZ);
}
/// <summary> Renames the Nodes to their id on the Spline </summary>
private void RenameNodes()
{
int nodesCount = nodes.Count;
SplineN node;
for (int i = 0; i < nodesCount; i++)
{
node = nodes[i];
node.name = "Node" + node.idOnSpline;
}
}
private int CompareListByID(SplineN _i1, SplineN _i2)
{
return _i1.idOnSpline.CompareTo(_i2.idOnSpline);
}
/// <summary> Setup all nodes of this road </summary>
private void SetupNodes(ref SplineN[] _rawNodes)
{
//Process nodes:
int i = 0;
List<SplineN> nodes = new List<SplineN>();
int rawNodesLength = _rawNodes.Length;
for (i = 0; i < rawNodesLength; i++)
{
nodes.Add(_rawNodes[i]);
}
this.nodes.Clear();
this.nodes = new List<SplineN>();
SplineN node;
float step;
Quaternion rot;
Vector3 positionChange;
bool isClosed = false;
step = (isClosed) ? 1f / ((float)nodes.Count) : 1f / ((float)(nodes.Count - 1));
int nodesCount = nodes.Count;
for (i = 0; i < nodesCount; i++)
{
node = nodes[i];
// Calculate the rotation to the next node
rot = Quaternion.identity;
if (i != nodes.Count - 1)
{
positionChange = (nodes[i + 1].transform.position - node.transform.position);
if (positionChange == Vector3.zero)
{
rot = Quaternion.identity;
}
else
{
rot = Quaternion.LookRotation(positionChange, transform.up);
}
}
else if (isClosed)
{
rot = Quaternion.LookRotation(nodes[0].transform.position - node.transform.position, transform.up);
}
else
{
rot = Quaternion.identity;
}
node.Setup(node.transform.position, rot, new Vector2(0f, 1f), step * ((float)i), node.transform.gameObject.name);
RootUtils.SetupUniqueIdentifier(ref node.uID);
this.nodes.Add(node);
}
nodes = null;
_rawNodes = null;
}
/// <summary> Calculates distance between node for accuracy </summary>
private void SetupSplineLength()
{
int nodeCount = nodes.Count;
//First lets get the general distance, node to node:
nodes[0].time = 0f;
nodes[nodeCount - 1].time = 1f;
Vector3 node1 = new Vector3(0f, 0f, 0f);
Vector3 node2 = new Vector3(0f, 0f, 0f);
float roadLength = 0f;
float roadLengthOriginal = 0f;
// Calculate accumulated distance between nodes
for (int j = 0; j < nodeCount; j++)
{
node2 = nodes[j].pos;
if (j > 0)
{
roadLength += Vector3.Distance(node1, node2);
}
node1 = node2;
}
roadLengthOriginal = roadLength;
roadLength = roadLength * 1.05f;
float step = 0.5f / roadLength;
//Get a slightly more accurate portrayal of the time:
float nodeTime = 0f;
for (int j = 0; j < (nodeCount - 1); j++)
{
node2 = nodes[j].pos;
if (j > 0)
{
nodeTime += (Vector3.Distance(node1, node2) / roadLengthOriginal);
nodes[j].time = nodeTime;
}
node1 = node2;
}
//Using general distance and calculated step, get an accurate distance:
float splineDistance = 0f;
Vector3 prevPos = nodes[0].pos;
Vector3 currentPos = new Vector3(0f, 0f, 0f);
prevPos = GetSplineValue(0f);
for (float i = 0f; i < 1f; i += step)
{
currentPos = GetSplineValue(i);
splineDistance += Vector3.Distance(currentPos, prevPos);
prevPos = currentPos;
}
distance = splineDistance;
//Now get fine distance between nodes:
float newTotalDistance = 0f;
step = 0.5f / distance;
SplineN prevNode = null;
SplineN currentNode = null;
prevPos = GetSplineValue(0f, false);
for (int j = 1; j < (nodeCount - 1); j++)
{
prevNode = nodes[j - 1];
currentNode = nodes[j];
if (j == 1)
{
prevPos = GetSplineValue(prevNode.time);
}
splineDistance = 0.00001f;
for (float i = prevNode.time; i < currentNode.time; i += step)
{
currentPos = GetSplineValue(i);
if (!float.IsNaN(currentPos.x))
{
if (float.IsNaN(prevPos.x))
{
prevPos = currentPos;
}
splineDistance += Vector3.Distance(currentPos, prevPos);
prevPos = currentPos;
}
}
currentNode.tempSegmentTime = (splineDistance / distance);
newTotalDistance += splineDistance;
currentNode.dist = newTotalDistance;
}
nodes[0].dist = 0f;
prevNode = nodes[nodeCount - 2];
currentNode = nodes[nodeCount - 1];
splineDistance = 0.00001f;
for (float i = prevNode.time; i < currentNode.time; i += step)
{
currentPos = GetSplineValue(i, false);
if (!float.IsNaN(currentPos.x))
{
if (float.IsNaN(prevPos.x))
{
prevPos = currentPos;
}
splineDistance += Vector3.Distance(currentPos, prevPos);
prevPos = currentPos;
}
}
currentNode.tempSegmentTime = (splineDistance / distance);
newTotalDistance += splineDistance;
currentNode.dist = newTotalDistance;
distance = newTotalDistance;
// Set node data
SplineN node;
nodeTime = 0f;
for (int j = 1; j < (nodeCount - 1); j++)
{
node = nodes[j];
nodeTime += node.tempSegmentTime;
node.oldTime = node.time;
node.time = nodeTime;
node.tangent = GetSplineValueSkipOpt(node.time, true);
node.transform.rotation = Quaternion.LookRotation(node.tangent);
}
nodes[0].tangent = GetSplineValueSkipOpt(0f, true);
nodes[nodeCount - 1].tangent = GetSplineValueSkipOpt(1f, true);
nodes[0].dist = 0f;
step = distance / cachedPointsSeperation;
int ArrayCount = (int)Mathf.Floor(step) + 2;
cachedPoints = new Vector3[ArrayCount];
step = cachedPointsSeperation / distance;
for (int j = 1; j < (ArrayCount - 1); j++)
{
cachedPoints[j] = GetSplineValue(step * j);
}
cachedPoints[0] = nodes[0].pos;
cachedPoints[ArrayCount - 1] = nodes[nodeCount - 1].pos;
RoadDefCalcs();
}
#endregion
#region "Road definition cache and translation"
private void RoadDefCalcs()
{
float tMod = Mathf.Lerp(0.05f, 0.2f, distance / 9000f);
float step = tMod / distance;
Vector3 currentPos = GetSplineValue(0f);
Vector3 prevPos = currentPos;
float tempDistanceModMax = road.roadDefinition - step;
float tempDistanceMod = 0f;
float tempTotalDistance = 0f;
float tempDistanceHolder = 0f;
if (RoadDefKeysArray != null)
{
RoadDefKeysArray = null;
}
if (RoadDefValuesArray != null)
{
RoadDefValuesArray = null;
}
List<int> RoadDefKeys = new List<int>();
List<float> RoadDefValues = new List<float>();
RoadDefKeys.Add(0);
RoadDefValues.Add(0f);
for (float index = 0f; index < 1f; index += step)
{
currentPos = GetSplineValue(index);
tempDistanceHolder = Vector3.Distance(currentPos, prevPos);
tempTotalDistance += tempDistanceHolder;
tempDistanceMod += tempDistanceHolder;
if (tempDistanceMod > tempDistanceModMax)
{
tempDistanceMod = 0f;
RoadDefKeys.Add(TranslateParamToInt(index));
RoadDefValues.Add(tempTotalDistance);
}
prevPos = currentPos;
}
RoadDefKeysArray = RoadDefKeys.ToArray();
RoadDefValuesArray = RoadDefValues.ToArray();
}
public int TranslateParamToInt(float _value)
{
return Mathf.Clamp((int)(_value * 10000000f), 0, 10000000);
}
public float TranslateInverseParamToFloat(int _value)
{
return Mathf.Clamp(((float)(float)_value / 10000000f), 0f, 1f);
}
private void GetClosestRoadDefKeys(float _x, out int _lo, out int _hi, out int _loIndex, out int _hiIndex)
{
int x = TranslateParamToInt(_x);
_lo = -1;
_hi = RoadDefKeysArray.Length - 1;
int mid = -1;
while ((_hi - _lo) > 1)
{
mid = Mathf.RoundToInt((_lo + _hi) / 2);
if (RoadDefKeysArray[mid] <= x)
{
_lo = mid;
}
else
{
_hi = mid;
}
}
if (RoadDefKeysArray[_lo] == x)
{
_hi = _lo;
}
// if(hi > RoadDefKeysArray.Length-1){ hi = RoadDefKeysArray.Length-1; }
_loIndex = _lo;
_hiIndex = _hi;
_lo = RoadDefKeysArray[_lo];
_hi = RoadDefKeysArray[_hi];
}
public int GetClosestRoadDefIndex(float _x, bool _isRoundUp = false, bool _isRoundDown = false)
{
int lo, hi, loIndex, hiIndex;
GetClosestRoadDefKeys(_x, out lo, out hi, out loIndex, out hiIndex);
int x = TranslateParamToInt(_x);
if (_isRoundUp)
{
return hiIndex;
}
if (_isRoundDown)
{
return loIndex;
}
if ((x - lo) > (hi - x))
{
return hiIndex;
}
else
{
return loIndex;
}
}
private void GetClosestRoadDefValues(float _x, out float _loF, out float _hiF, out int _loIndex, out int _hiIndex)
{
int lo = -1;
int hi = RoadDefValuesArray.Length - 1;
int mid = -1;
while ((hi - lo) > 1)
{
mid = Mathf.RoundToInt((lo + hi) / 2);
if (RoadDefValuesArray[mid] < _x || RootUtils.IsApproximately(RoadDefValuesArray[mid], _x, 0.02f))
{
lo = mid;
}
else
{
hi = mid;
}
}
if (RootUtils.IsApproximately(RoadDefValuesArray[lo], _x, 0.02f))
{
hi = lo;
}
_loIndex = lo;
_hiIndex = hi;
_loF = RoadDefValuesArray[lo];
_hiF = RoadDefValuesArray[hi];
}
public int GetClosestRoadDefValuesIndex(float _x, bool _isRoundUp = false, bool _isRoundDown = false)
{
float lo, hi;
int loIndex, hiIndex;
GetClosestRoadDefValues(_x, out lo, out hi, out loIndex, out hiIndex);
if (_isRoundUp)
{
return hiIndex;
}
if (_isRoundDown)
{
return loIndex;
}
if ((_x - lo) > (hi - _x))
{
return hiIndex;
}
else
{
return loIndex;
}
}
public float TranslateDistBasedToParam(float _dist)
{
int tIndex = GetClosestRoadDefValuesIndex(_dist, false, false);
float tKey = TranslateInverseParamToFloat(RoadDefKeysArray[tIndex]);
int tCount = RoadDefKeysArray.Length;
float kDist = RoadDefValuesArray[tIndex];
if (tIndex < (tCount - 1))
{
if (_dist > kDist)
{
float NextValue = RoadDefValuesArray[tIndex + 1];
float tDiff1 = (_dist - kDist) / (NextValue - kDist);
tKey += (tDiff1 * (TranslateInverseParamToFloat(RoadDefKeysArray[tIndex + 1]) - tKey));
}
}
if (tIndex > 0)
{
if (_dist < kDist)
{
float PrevValue = RoadDefValuesArray[tIndex - 1];
float tDiff1 = (_dist - PrevValue) / (kDist - PrevValue);
tKey -= (tDiff1 * (tKey - TranslateInverseParamToFloat(RoadDefKeysArray[tIndex - 1])));
}
}
return tKey;
}
public float TranslateParamToDist(float _param)
{
int tIndex = GetClosestRoadDefIndex(_param, false, false);
float tKey = TranslateInverseParamToFloat(RoadDefKeysArray[tIndex]);
int tCount = RoadDefKeysArray.Length;
float kDist = RoadDefValuesArray[tIndex];
float xDiff = kDist;
if (tIndex < (tCount - 1))
{
if (_param > tKey)
{
float NextValue = TranslateInverseParamToFloat(RoadDefKeysArray[tIndex + 1]);
float tDiff1 = (_param - tKey) / (NextValue - tKey);
xDiff += (tDiff1 * (RoadDefValuesArray[tIndex + 1] - kDist));
}
}
if (tIndex > 0)
{
if (_param < tKey)
{
float PrevValue = TranslateInverseParamToFloat(RoadDefKeysArray[tIndex - 1]);
float tDiff1 = 1f - ((_param - PrevValue) / (tKey - PrevValue));
xDiff -= (tDiff1 * (kDist - RoadDefValuesArray[tIndex - 1]));
}
}
return xDiff;
}
#endregion
#region "Hermite math"
/// <summary> Gets the spline value. </summary>
/// <param name='_value'> The relevant param (0-1) of the spline. </param>
/// <param name='_isTangent'> True for is tangent, false (default) for vector3 position. </param>
public Vector3 GetSplineValue(float _value, bool _isTangent = false)
{
int index;
int idx = -1;
if (nodes.Count == 0)
{
return default(Vector3);
}
if (nodes.Count == 1)
{
return nodes[0].pos;
}
// This Code was outcommented, but it takes care about values above and below 0f and 1f and clamping them.
// This Fixes the Bug descripted by embeddedt/RoadArchitect/issues/4
if (RootUtils.IsApproximately(_value, 0f, 0.00001f))
{
if (_isTangent)
{
return nodes[0].tangent;
}
else
{
return nodes[0].pos;
}
}
else if (RootUtils.IsApproximately(_value, 1f, 0.00001f) || _value > 1f)
{
if (_isTangent)
{
return nodes[nodes.Count - 1].tangent;
}
else
{
return nodes[nodes.Count - 1].pos;
}
}
else
{
RootUtils.IsApproximately(_value, 1f, 0.00001f);
for (index = 0; index < nodes.Count; index++)
{
if (index == nodes.Count - 1)
{
idx = index - 1;
break;
}
if (nodes[index].time > _value)
{
idx = index - 1;
break;
}
}
if (idx < 0)
{
idx = 0;
}
}
float param = (_value - nodes[idx].time) / (nodes[idx + 1].time - nodes[idx].time);
param = RootUtils.Ease(param, nodes[idx].easeIO.x, nodes[idx].easeIO.y);
return GetHermiteInternal(idx, param, _isTangent);
}
/// <summary> Return position and tangent in Vector3 of spline progress </summary>
public void GetSplineValueBoth(float _value, out Vector3 _vect1, out Vector3 _vect2)
{
int index;
int idx = -1;
int nodeCount = GetNodeCount();
if (_value < 0f)
{
_value = 0f;
}
if (_value > 1f)
{
_value = 1f;
}
if (nodeCount == 0)
{
_vect1 = default(Vector3);
_vect2 = default(Vector3);
return;
}
if (nodeCount == 1)
{
if (nodes[0])
{
_vect1 = nodes[0].pos;
_vect2 = default(Vector3);
}
else
{
_vect1 = default(Vector3);
_vect2 = default(Vector3);
}
return;
}
// This Code was outcommented, but it takes care about values above and below 0f and 1f and clamping them.
// This code needs to be reevealuated if this isn't taken care of by the function above this one. GetSplineValue()
// part of embeddedt/RoadArchitect/issues/4 ?
if (RootUtils.IsApproximately(_value, 1f, 0.0001f))
{
_vect1 = nodes[nodes.Count - 1].pos;
_vect2 = nodes[nodes.Count - 1].tangent;
return;
}
else if (RootUtils.IsApproximately(_value, 0f, 0.0001f))
{
_vect1 = nodes[0].pos;
_vect2 = nodes[0].tangent;
return;
}
// Do Note: This does prevent EdgeObjects from being placed before or after
// 0f/1f on the Spline, but also causes the EdgeObject to be placed at the same Position at the End of the Spline.
for (index = 1; index < nodeCount; index++)
{
if (index == nodeCount - 1)
{
idx = index - 1;
break;
}
if (nodes[index].time > _value)
{
idx = index - 1;
break;
}
}
if (idx < 0)
{
idx = 0;
}
float param = (_value - nodes[idx].time) / (nodes[idx + 1].time - nodes[idx].time);
param = RootUtils.Ease(param, nodes[idx].easeIO.x, nodes[idx].easeIO.y);
_vect1 = GetHermiteInternal(idx, param, false);
_vect2 = GetHermiteInternal(idx, param, true);
}
public Vector3 GetSplineValueSkipOpt(float _value, bool _isTangent = false)
{
int index;
int idx = -1;
if (nodes.Count == 0)
{
return default(Vector3);
}
if (nodes.Count == 1)
{
return nodes[0].pos;
}
// if(RootUtils.IsApproximately(f,0f,0.00001f)){
// if(_isTangent){
// return mNodes[0].tangent;
// }else{
// return mNodes[0].pos;
// }
// }else
// if(RootUtils.IsApproximately(f,1f,0.00001f) || f > 1f){
// if(_isTangent){
// return mNodes[mNodes.Count-1].tangent;
// }else{
// return mNodes[mNodes.Count-1].pos;
// }
// }else{
for (index = 1; index < nodes.Count; index++)
{
if (index == nodes.Count - 1)
{
idx = index - 1;
break;
}
if (nodes[index].time > _value)
{
idx = index - 1;
break;
}
}
if (idx < 0)
{
idx = 0;
}
// if(b && RootUtils.IsApproximately(f,1f,0.00001f)){
// idx = mNodes.Count-2;
// }
// }
float param = (_value - nodes[idx].time) / (nodes[idx + 1].time - nodes[idx].time);
param = RootUtils.Ease(param, nodes[idx].easeIO.x, nodes[idx].easeIO.y);
return GetHermiteInternal(idx, param, _isTangent);
}
public float GetClosestParam(Vector3 _vect, bool _is20cmPrecision = false, bool _is1MeterPrecision = false)
{
return GetClosestParamDo(ref _vect, _is20cmPrecision, _is1MeterPrecision);
}
private float GetClosestParamDo(ref Vector3 _vect, bool _is20cmPrecision, bool _is1MeterPrecision)
{
//5m to 1m
float Step1 = cachedPointsSeperation / distance;
//20 cm
float Step2 = Step1 * 0.2f;
//8 cm
float Step3 = Step2 * 0.4f;
//2 cm
float Step4 = Step3 * 0.25f;
float tMin = 0f;
float tMax = 1f;
// Why is Best value set to -1f? at init
float BestValue = -1f;
float MaxStretch = 0.9f;
Vector3 BestVect_p = new Vector3(0f, 0f, 0f);
Vector3 BestVect_n = new Vector3(0f, 0f, 0f);
if (nodes.Count == 0)
{
return 0f;
}
if (nodes.Count == 1)
{
return 1f;
}
//Step 1: 1m
BestValue = GetClosestPointHelper(ref _vect, Step1, BestValue, tMin, tMax, ref BestVect_p, ref BestVect_n, true);
if (_is1MeterPrecision)
{
return BestValue;
}
//Step 2: 20cm
tMin = BestValue - (Step1 * MaxStretch);
tMax = BestValue + (Step1 * MaxStretch);
BestValue = GetClosestPointHelper(ref _vect, Step2, BestValue, tMin, tMax, ref BestVect_p, ref BestVect_n);
if (_is20cmPrecision)
{
return BestValue;
}
//Step 3: 8cm
tMin = BestValue - (Step2 * MaxStretch);
tMax = BestValue + (Step2 * MaxStretch);
BestValue = GetClosestPointHelper(ref _vect, Step3, BestValue, tMin, tMax, ref BestVect_p, ref BestVect_n);
//Step 4: 2cm
tMin = BestValue - (Step3 * MaxStretch);
tMax = BestValue + (Step3 * MaxStretch);
BestValue = GetClosestPointHelper(ref _vect, Step4, BestValue, tMin, tMax, ref BestVect_p, ref BestVect_n);
return BestValue;
}
private float GetClosestPointHelper(ref Vector3 _vect, float _step, float _bestValue, float _min, float _max, ref Vector3 _bestVectP, ref Vector3 _bestVectN, bool _isMeterCache = false)
{
float mDistance = 5000f;
float tDistance = 0f;
Vector3 cVect = new Vector3(0f, 0f, 0f);
Vector3 pVect = new Vector3(0f, 0f, 0f);
bool isFirstLoopHappened = false;
bool isSetBestValue = false;
//Get lean for tmin/tmax:
if (GetClosetPointMinMaxDirection(ref _vect, ref _bestVectP, ref _bestVectN))
{
_max = _bestValue;
}
else
{
_min = _bestValue;
}
_min = Mathf.Clamp(_min, 0f, 1f);
_max = Mathf.Clamp(_max, 0f, 1f);
if (_isMeterCache)
{
int CachedIndex = -1;
int Step1 = 10;
int CachedPointsLength = cachedPoints.Length;
for (int j = 0; j < CachedPointsLength; j += Step1)
{
cVect = cachedPoints[j];
tDistance = Vector3.Distance(_vect, cVect);
if (tDistance < mDistance)
{
mDistance = tDistance;
CachedIndex = j;
}
}
int jStart = (CachedIndex - Step1);
if (jStart < 50)
{
jStart = 0;
}
int jEnd = (CachedIndex + Step1);
if (jEnd > (CachedPointsLength))
{
jEnd = CachedPointsLength;
}
for (int j = jStart; j < jEnd; j++)
{
cVect = cachedPoints[j];
if (isSetBestValue)
{
_bestVectN = cVect;
isSetBestValue = false;
}
tDistance = Vector3.Distance(_vect, cVect);
if (tDistance < mDistance)
{
mDistance = tDistance;
if (!isFirstLoopHappened)
{
_bestVectP = cVect;
}
else
{
_bestVectP = pVect;
}
CachedIndex = j;
isSetBestValue = true;
isFirstLoopHappened = true;
}
pVect = cVect;
}
_bestValue = (CachedIndex / distance);
}
else
{
for (float index = _min; index <= _max; index += _step)
{
cVect = GetSplineValue(index);
if (isSetBestValue)
{
_bestVectN = cVect;
isSetBestValue = false;
}
tDistance = Vector3.Distance(_vect, cVect);
if (tDistance < mDistance)
{
mDistance = tDistance;
_bestValue = index;
if (!isFirstLoopHappened)
{
_bestVectP = cVect;
}
else
{
_bestVectP = pVect;
}
isSetBestValue = true;
isFirstLoopHappened = true;
}
pVect = cVect;
}
}
if (isSetBestValue)
{
_bestVectN = cVect;
}
//Debug.Log ("returning: " + BestValue + " tmin:" + tMin + " tmax:" + tMax);
return _bestValue;
}
//Returns true for tmin lean:
private bool GetClosetPointMinMaxDirection(ref Vector3 _vect, ref Vector3 _bestVectP, ref Vector3 _bestVectN)
{
float Distance1 = Vector3.Distance(_vect, _bestVectP);
float Distance2 = Vector3.Distance(_vect, _bestVectN);
if (Distance1 < Distance2)
{
//tMin lean
return true;
}
else
{
//tMax lean
return false;
}
}
private Vector3 GetHermiteInternal(int _i, double _t, bool _isTangent = false)
{
double t2, t3;
float BL0, BL1, BL2, BL3, tension;
if (!_isTangent)
{
t2 = _t * _t;
t3 = t2 * _t;
}
else
{
t2 = _t * _t;
_t = _t * 2.0;
t2 = t2 * 3.0;
//Prevent compiler error.
t3 = 0;
}
//Vectors:
Vector3 P0 = nodes[NGI(_i, NI[0])].pos;
Vector3 P1 = nodes[NGI(_i, NI[1])].pos;
Vector3 P2 = nodes[NGI(_i, NI[2])].pos;
Vector3 P3 = nodes[NGI(_i, NI[3])].pos;
//Tension:
tension = 0.5f;
//Tangents:
Vector3 xVect1 = (P1 - P2) * tension;
Vector3 xVect2 = (P3 - P0) * tension;
float tMaxMag = road.magnitudeThreshold;
if (Vector3.Distance(P1, P3) > tMaxMag)
{
if (xVect1.magnitude > tMaxMag)
{
xVect1 = Vector3.ClampMagnitude(xVect1, tMaxMag);
}
if (xVect2.magnitude > tMaxMag)
{
xVect2 = Vector3.ClampMagnitude(xVect2, tMaxMag);
}
}
else if (Vector3.Distance(P0, P2) > tMaxMag)
{
if (xVect1.magnitude > tMaxMag)
{
xVect1 = Vector3.ClampMagnitude(xVect1, tMaxMag);
}
if (xVect2.magnitude > tMaxMag)
{
xVect2 = Vector3.ClampMagnitude(xVect2, tMaxMag);
}
}
if (!_isTangent)
{
BL0 = (float)(CM[0] * t3 + CM[1] * t2 + CM[2] * _t + CM[3]);
BL1 = (float)(CM[4] * t3 + CM[5] * t2 + CM[6] * _t + CM[7]);
BL2 = (float)(CM[8] * t3 + CM[9] * t2 + CM[10] * _t + CM[11]);
BL3 = (float)(CM[12] * t3 + CM[13] * t2 + CM[14] * _t + CM[15]);
}
else
{
BL0 = (float)(CM[0] * t2 + CM[1] * _t + CM[2]);
BL1 = (float)(CM[4] * t2 + CM[5] * _t + CM[6]);
BL2 = (float)(CM[8] * t2 + CM[9] * _t + CM[10]);
BL3 = (float)(CM[12] * t2 + CM[13] * _t + CM[14]);
}
Vector3 tVect = BL0 * P0 + BL1 * P1 + BL2 * xVect1 + BL3 * xVect2;
if (!_isTangent)
{
if (tVect.y < 0f)
{
tVect.y = 0f;
}
}
return tVect;
}
private static readonly double[] CM = new double[] {
2.0, -3.0, 0.0, 1.0,
-2.0, 3.0, 0.0, 0.0,
1.0, -2.0, 1.0, 0.0,
1.0, -1.0, 0.0, 0.0
};
private static readonly int[] NI = new int[] { 0, 1, -1, 2 };
private int NGI(int _i, int _o)
{
int NGITI = _i + _o;
// if(bClosed){
// return (NGITI % mNodes.Count + mNodes.Count) % mNodes.Count;
// }else{
return Mathf.Clamp(NGITI, 0, nodes.Count - 1);
// }
}
#endregion
#region "Gizmos"
//private const bool isDrawingGizmos = true;
private float GizmoDrawMeters = 1f;
private void OnDrawGizmosSelected()
{
// if(!isDrawingGizmos){ return; }
if (nodes == null || nodes.Count < 2)
{
return;
}
if (transform == null)
{
return;
}
float DistanceFromCam = Vector3.SqrMagnitude(Camera.current.transform.position - nodes[0].transform.position);
if (DistanceFromCam > 16777216f)
{
return;
}
else if (DistanceFromCam > 4194304f)
{
GizmoDrawMeters = 16f;
}
else if (DistanceFromCam > 1048576f)
{
GizmoDrawMeters = 8f;
}
else if (DistanceFromCam > 262144f)
{
GizmoDrawMeters = 4f;
}
else if (DistanceFromCam > 65536)
{
GizmoDrawMeters = 1f;
}
else if (DistanceFromCam > 16384f)
{
GizmoDrawMeters = 0.5f;
}
else
{
GizmoDrawMeters = 0.1f;
}
Vector3 prevPos = nodes[0].pos;
Vector3 tempVect = new Vector3(0f, 0f, 0f);
float step = GizmoDrawMeters / distance;
step = Mathf.Clamp(step, 0f, 1f);
Gizmos.color = new Color(1f, 0f, 0f, 1f);
float index = 0f;
Vector3 cPos;
float tCheck = 0f;
Vector3 camPos = Camera.current.transform.position;
for (index = 0f; index <= 1f; index += step)
{
tCheck += step;
cPos = GetSplineValue(index);
if (tCheck > 0.1f)
{
DistanceFromCam = Vector3.SqrMagnitude(camPos - cPos);
if (DistanceFromCam > 16777216f)
{
return;
}
else if (DistanceFromCam > 4194304f)
{
GizmoDrawMeters = 16f;
}
else if (DistanceFromCam > 1048576f)
{
GizmoDrawMeters = 10f;
}
else if (DistanceFromCam > 262144f)
{
GizmoDrawMeters = 4f;
}
else if (DistanceFromCam > 65536)
{
GizmoDrawMeters = 1f;
}
else if (DistanceFromCam > 16384f)
{
GizmoDrawMeters = 0.5f;
}
else
{
GizmoDrawMeters = 0.1f;
}
step = GizmoDrawMeters / distance;
step = Mathf.Clamp(step, 0f, 1f);
tCheck = 0f;
}
Gizmos.DrawLine(prevPos + tempVect, cPos + tempVect);
prevPos = cPos;
if ((index + step) > 1f)
{
cPos = GetSplineValue(1f);
Gizmos.DrawLine(prevPos + tempVect, cPos + tempVect);
}
}
}
#endregion
#region "Intersections"
public bool IsNearIntersection(ref Vector3 _pos, ref float _result)
{
int mCount = GetNodeCount();
SplineN tNode;
float MetersToCheck = 75f * ((road.laneWidth / 5f) * (road.laneWidth / 5f));
float tDist;
for (int index = 0; index < mCount; index++)
{
tNode = nodes[index];
if (tNode.isIntersection)
{
tNode.intersection.height = tNode.pos.y;
if (isUsingSQ)
{
tDist = Vector3.SqrMagnitude(_pos - tNode.pos);
}
// else{
// tDist = Vector3.Distance(tPos,tNode.pos);
// }
if (tNode.intersection.roadType == RoadIntersection.RoadTypeEnum.NoTurnLane)
{
if (isUsingSQ)
{
MetersToCheck = metersToCheckNoTurnLaneSQ * ((road.laneWidth / 5f) * (road.laneWidth / 5f));
}
// else{
// MetersToCheck = MetersToCheck_NoTurnLane;
// }
}
else if (tNode.intersection.roadType == RoadIntersection.RoadTypeEnum.TurnLane)
{
if (isUsingSQ)
{
MetersToCheck = metersToCheckTurnLaneSQ * ((road.laneWidth / 5f) * (road.laneWidth / 5f));
;
}
// else{
// MetersToCheck = MetersToCheck_TurnLane;
// }
}
else if (tNode.intersection.roadType == RoadIntersection.RoadTypeEnum.BothTurnLanes)
{
if (isUsingSQ)
{
MetersToCheck = metersToCheckBothTurnLaneSQ * ((road.laneWidth / 5f) * (road.laneWidth / 5f));
;
}
// else{
// MetersToCheck = MetersToCheck_BothTurnLane;
// }
}
MetersToCheck *= 0.8f;
if (road.laneAmount == 4)
{
MetersToCheck *= 1.25f;
}
else if (road.laneAmount == 6)
{
MetersToCheck *= 1.35f;
}
if (tDist <= MetersToCheck)
{
_result = tNode.pos.y;
return true;
}
}
}
_result = _pos.y;
return false;
}
public float IntersectionStrength(ref Vector3 _pos, ref float _result, ref RoadIntersection _inter, ref bool _isPast, ref float _p, ref SplineN _node)
{
int nodeCount = GetNodeCount();
float tDist;
SplineN tNode;
float MetersToCheck = 75f * ((road.laneWidth / 5f) * (road.laneWidth / 5f));
for (int index = 0; index < nodeCount; index++)
{
tNode = nodes[index];
if (tNode.isIntersection)
{
tNode.intersection.height = tNode.pos.y;
SplineN xNode;
if (isUsingSQ)
{
tDist = Vector3.SqrMagnitude(_pos - tNode.pos);
}
// else{
// tDist = Vector3.Distance(tPos,tNode.pos);
// }
if (tNode.intersection.roadType == RoadIntersection.RoadTypeEnum.NoTurnLane)
{
if (isUsingSQ)
{
MetersToCheck = metersToCheckNoTurnLaneSQ * ((road.laneWidth / 5f) * (road.laneWidth / 5f));
}
// else{
// MetersToCheck = MetersToCheck_NoTurnLane;
// }
}
else if (tNode.intersection.roadType == RoadIntersection.RoadTypeEnum.TurnLane)
{
if (isUsingSQ)
{
MetersToCheck = metersToCheckTurnLaneSQ * ((road.laneWidth / 5f) * (road.laneWidth / 5f));
}
// else{
// MetersToCheck = MetersToCheck_TurnLane;
// }
}
else if (tNode.intersection.roadType == RoadIntersection.RoadTypeEnum.BothTurnLanes)
{
if (isUsingSQ)
{
MetersToCheck = metersToCheckBothTurnLaneSQ * ((road.laneWidth / 5f) * (road.laneWidth / 5f));
}
// else{
// MetersToCheck = MetersToCheck_BothTurnLane;
// }
}
if (road.laneAmount == 4)
{
MetersToCheck *= 1.25f;
}
else if (road.laneAmount == 6)
{
MetersToCheck *= 1.35f;
}
if (tDist <= MetersToCheck)
{
if (tNode.intersection.isSameSpline)
{
if (tNode.intersection.node1.uID != tNode.uID)
{
xNode = tNode.intersection.node1;
}
else
{
xNode = tNode.intersection.node2;
}
float P1 = tNode.time - _p;
if (P1 < 0f)
{ P1 *= -1f; }
float P2 = xNode.time - _p;
if (P2 < 0f)
{ P2 *= -1f; }
if (P1 > P2)
{
if (_p > xNode.time)
{
_isPast = true;
}
else
{
_isPast = false;
}
_node = xNode;
}
else
{
if (_p > tNode.time)
{
_isPast = true;
}
else
{
_isPast = false;
}
_node = tNode;
}
}
else
{
if (_p > tNode.time)
{
_isPast = true;
}
else
{
_isPast = false;
}
_node = tNode;
}
if (isUsingSQ)
{
tDist = Mathf.Sqrt(tDist);
MetersToCheck = Mathf.Sqrt(MetersToCheck);
}
_inter = tNode.intersection;
_result = tNode.pos.y + 0.1f;
tDist = 1f - (tDist / MetersToCheck);
tDist = Mathf.Pow(tDist, 3f) * 5f;
if (tDist > 1f)
tDist = 1f;
if (tDist < 0f)
tDist = 0f;
return tDist;
}
}
}
_result = _pos.y;
return 0f;
}
public float IntersectionStrengthNext(Vector3 _pos)
{
float result = 0f;
RoadIntersection intersection = null;
bool isPast = false;
float p = 0f;
SplineN node = null;
return IntersectionStrength(ref _pos, ref result, ref intersection, ref isPast, ref p, ref node);
}
public bool IntersectionIsPast(ref float _p, ref SplineN _node)
{
//int mCount = GetNodeCount();
//bool bIsPast;
//SplineN tNode = null;
//for(int i=0;i<mCount;i++)
//{
// tNode = mNodes[i];
// if(tNode.bIsIntersection)
//{
// float P1 = tNode.roadIntersection.Node1.tTime - p;
// if(P1 < 0f)
// {
// P1 *= -1f;
// }
// float P2 = tNode.roadIntersection.Node2.tTime - p;
// if(P2 < 0f)
// {
// P2 *= -1f;
// }
//
// if(P1 > P2)
// {
// if(p > tNode.roadIntersection.Node2.tTime)
// {
// bIsPast = true;
// }
// else
// {
// bIsPast = false;
// }
// }
// else
// {
// if(p > tNode.roadIntersection.Node1.tTime)
// {
// bIsPast = true;
// }
// else
// {
// bIsPast = false;
// }
// }
// return bIsPast;
// }
//}
//return false;
if (_p < _node.time)
{
return false;
}
else
{
return true;
}
}
private void DestroyIntersection(SplineN _node)
{
if (_node == null)
{
return;
}
if (_node.isEndPoint)
{
if (_node.idOnSpline == 1 && _node.spline.nodes[0].isSpecialEndNodeIsStart)
{
Object.DestroyImmediate(_node.spline.nodes[0].transform.gameObject);
_node.spline.isSpecialStartControlNode = false;
}
else if (_node.idOnSpline == _node.spline.GetNodeCount() - 2 && _node.spline.nodes[_node.spline.GetNodeCount() - 1].isSpecialEndNodeIsEnd)
{
Object.DestroyImmediate(_node.spline.nodes[_node.spline.GetNodeCount() - 1].transform.gameObject);
_node.spline.isSpecialEndControlNode = false;
}
}
_node.isIntersection = false;
_node.isSpecialIntersection = false;
if (_node.intersectionOtherNode != null)
{
if (_node.intersectionOtherNode.isEndPoint)
{
if (_node.intersectionOtherNode.idOnSpline == 1 && _node.intersectionOtherNode.spline.nodes[0].isSpecialEndNodeIsStart)
{
Object.DestroyImmediate(_node.intersectionOtherNode.spline.nodes[0].transform.gameObject);
_node.intersectionOtherNode.spline.isSpecialStartControlNode = false;
}
else if (_node.intersectionOtherNode.idOnSpline == _node.intersectionOtherNode.spline.GetNodeCount() - 2 && _node.intersectionOtherNode.spline.nodes[_node.intersectionOtherNode.spline.GetNodeCount() - 1].isSpecialEndNodeIsEnd)
{
Object.DestroyImmediate(_node.intersectionOtherNode.spline.nodes[_node.intersectionOtherNode.spline.GetNodeCount() - 1].transform.gameObject);
_node.intersectionOtherNode.spline.isSpecialEndControlNode = false;
}
}
_node.intersectionOtherNode.isIntersection = false;
_node.intersectionOtherNode.isSpecialIntersection = false;
_node.spline.road.isUpdatingSpline = true;
if (_node.spline != _node.intersectionOtherNode.spline)
{
_node.intersectionOtherNode.spline.road.isUpdatingSpline = true;
}
}
}
#endregion
#region "Bridges"
public bool IsInBridge(float _p)
{
KeyValuePair<float, float> KVP;
if (BridgeParams == null)
{
return false;
}
int cCount = BridgeParams.Count;
if (cCount < 1)
{
return false;
}
for (int index = 0; index < cCount; index++)
{
KVP = BridgeParams[index];
if (RootUtils.IsApproximately(KVP.Key, _p, 0.0001f) || RootUtils.IsApproximately(KVP.Value, _p, 0.0001f))
{
return true;
}
if (_p > KVP.Key && _p < KVP.Value)
{
return true;
}
}
return false;
}
public float BridgeUpComing(float _p)
{
float tDist = 20f / distance;
float OrigP = _p;
_p += tDist;
KeyValuePair<float, float> KVP;
if (BridgeParams == null)
{
return 1f;
}
int cCount = BridgeParams.Count;
if (cCount < 1)
{
return 1f;
}
for (int index = 0; index < cCount; index++)
{
KVP = BridgeParams[index];
if (RootUtils.IsApproximately(KVP.Key, _p, 0.0001f) || RootUtils.IsApproximately(KVP.Value, _p, 0.0001f))
{
return ((KVP.Key - OrigP) / tDist);
}
if (_p > KVP.Key && _p < KVP.Value)
{
return ((KVP.Key - OrigP) / tDist);
}
}
return 1f;
}
public bool IsInBridgeTerrain(float _p)
{
KeyValuePair<float, float> KVP;
if (BridgeParams == null)
{
return false;
}
int cCount = BridgeParams.Count;
if (cCount < 1)
{
return false;
}
for (int index = 0; index < cCount; index++)
{
KVP = BridgeParams[index];
if (RootUtils.IsApproximately(KVP.Key + (10f / distance), _p, 0.0001f) || RootUtils.IsApproximately(KVP.Value - (10f / distance), _p, 0.0001f))
{
return true;
}
if (_p > (KVP.Key + (10f / distance)) && _p < (KVP.Value - (10f / distance)))
{
return true;
}
}
return false;
}
public float GetBridgeEnd(float _p)
{
KeyValuePair<float, float> KVP;
if (BridgeParams == null)
{
return -1f;
}
int cCount = BridgeParams.Count;
if (cCount < 1)
{
return -1f;
}
for (int index = 0; index < cCount; index++)
{
KVP = BridgeParams[index];
if (_p >= KVP.Key && _p <= KVP.Value)
{
return KVP.Value;
}
}
return -1f;
}
#endregion
#region "Tunnels"
public bool IsInTunnel(float _p)
{
KeyValuePair<float, float> KVP;
if (TunnelParams == null)
{
return false;
}
int cCount = TunnelParams.Count;
if (cCount < 1)
{
return false;
}
for (int index = 0; index < cCount; index++)
{
KVP = TunnelParams[index];
if (RootUtils.IsApproximately(KVP.Key, _p, 0.0001f) || RootUtils.IsApproximately(KVP.Value, _p, 0.0001f))
{
return true;
}
if (_p > KVP.Key && _p < KVP.Value)
{
return true;
}
}
return false;
}
public float TunnelUpComing(float _p)
{
float tDist = 20f / distance;
float OrigP = _p;
_p += tDist;
KeyValuePair<float, float> KVP;
if (TunnelParams == null)
{
return 1f;
}
int cCount = TunnelParams.Count;
if (cCount < 1)
{
return 1f;
}
for (int index = 0; index < cCount; index++)
{
KVP = TunnelParams[index];
if (RootUtils.IsApproximately(KVP.Key, _p, 0.0001f) || RootUtils.IsApproximately(KVP.Value, _p, 0.0001f))
{
return ((KVP.Key - OrigP) / tDist);
}
if (_p > KVP.Key && _p < KVP.Value)
{
return ((KVP.Key - OrigP) / tDist);
}
}
return 1f;
}
public bool IsInTunnelTerrain(float _p)
{
KeyValuePair<float, float> KVP;
if (TunnelParams == null)
{
return false;
}
int cCount = TunnelParams.Count;
if (cCount < 1)
{
return false;
}
for (int index = 0; index < cCount; index++)
{
KVP = TunnelParams[index];
if (RootUtils.IsApproximately(KVP.Key + (10f / distance), _p, 0.0001f) || RootUtils.IsApproximately(KVP.Value - (10f / distance), _p, 0.0001f))
{
return true;
}
if (_p > (KVP.Key + (10f / distance)) && _p < (KVP.Value - (10f / distance)))
{
return true;
}
}
return false;
}
public float GetTunnelEnd(float _p)
{
KeyValuePair<float, float> KVP;
if (TunnelParams == null)
{
return -1f;
}
int cCount = TunnelParams.Count;
if (cCount < 1)
{
return -1f;
}
for (int index = 0; index < cCount; index++)
{
KVP = TunnelParams[index];
if (_p >= KVP.Key && _p <= KVP.Value)
{
return KVP.Value;
}
}
return -1f;
}
#endregion
#region "Road connections"
/// <summary> Creates a conncetion between first and last node </summary>
public void ActivateEndNodeConnection(SplineN _node1, SplineN _node2)
{
SplineC spline = _node2.spline;
int nodeCount = spline.GetNodeCount();
int mCount = GetNodeCount();
//Don't allow connection with less than 3 nodes:
if (mCount < 3 || nodeCount < 3)
{
EngineIntegration.DisplayDialog("Cannot connect roads", "Roads must have at least 3 nodes to be connected.", "ok");
return;
}
Vector3 node1ExtraPos = default(Vector3);
Vector3 node2ExtraPos = default(Vector3);
bool isNode1Start = false;
//bool isNode1End = false;
if (_node1.idOnSpline == 0)
{
isNode1Start = true;
node2ExtraPos = nodes[1].transform.position;
}
else
{
//isNode1End = true;
node2ExtraPos = nodes[mCount - 2].transform.position;
}
bool isNode2Start = false;
//bool isNode2End = false;
if (_node2.idOnSpline == 0)
{
isNode2Start = true;
node1ExtraPos = spline.nodes[1].transform.position;
}
else
{
//isNode2End = true;
node1ExtraPos = spline.nodes[nodeCount - 2].transform.position;
}
SplineN NodeCreated1 = null;
SplineN NodeCreated2 = null;
if (isNode1Start)
{
isSpecialStartControlNode = true;
if (nodes[0].isSpecialEndNode)
{
nodes[0].transform.position = node1ExtraPos;
nodes[0].pos = node1ExtraPos;
NodeCreated1 = nodes[0];
}
else
{
NodeCreated1 = Construction.InsertNode(road, true, node1ExtraPos, false, 0, true);
}
}
else
{
isSpecialEndControlNode = true;
SplineN zNode1 = spline.GetLastNodeAll();
if (zNode1 != null && zNode1.isSpecialEndNode)
{
zNode1.transform.position = node1ExtraPos;
zNode1.pos = node1ExtraPos;
NodeCreated1 = GetLastNodeAll();
}
else
{
NodeCreated1 = Construction.CreateNode(road, true, node1ExtraPos);
}
}
if (isNode2Start)
{
spline.isSpecialStartControlNode = true;
if (spline.nodes[0].isSpecialEndNode)
{
spline.nodes[0].transform.position = node2ExtraPos;
spline.nodes[0].pos = node2ExtraPos;
NodeCreated2 = spline.nodes[0];
}
else
{
NodeCreated2 = Construction.InsertNode(spline.road, true, node2ExtraPos, false, 0, true);
}
}
else
{
spline.isSpecialEndControlNode = true;
SplineN zNode2 = spline.GetLastNodeAll();
if (zNode2 != null && zNode2.isSpecialEndNode)
{
zNode2.transform.position = node2ExtraPos;
zNode2.pos = node2ExtraPos;
NodeCreated2 = spline.GetLastNodeAll();
}
else
{
NodeCreated2 = Construction.CreateNode(spline.road, true, node2ExtraPos);
}
}
NodeCreated1.isSpecialEndNodeIsStart = isNode1Start;
NodeCreated2.isSpecialEndNodeIsStart = isNode2Start;
NodeCreated1.isSpecialEndNodeIsEnd = !isNode1Start;
NodeCreated2.isSpecialEndNodeIsEnd = !isNode2Start;
NodeCreated1.specialNodeCounterpart = NodeCreated2;
NodeCreated2.specialNodeCounterpart = NodeCreated1;
float lWidth1 = _node1.spline.road.laneWidth;
float lWidth2 = _node2.spline.road.laneWidth;
float xWidth = Mathf.Max(lWidth1, lWidth2);
float tDelay = 0f;
// Handle different amount of lanes
if (_node1.spline.road.laneAmount > _node2.spline.road.laneAmount)
{
_node2.isSpecialRoadConnPrimary = true;
NodeCreated2.isSpecialRoadConnPrimary = true;
if (_node2.spline.road.laneAmount == 4)
{
xWidth *= 2f;
}
tDelay = (_node1.spline.road.laneAmount - _node2.spline.road.laneAmount) * xWidth;
if (tDelay < 10f)
{
tDelay = 10f;
}
if (isNode2Start)
{
_node2.spline.isSpecialEndNodeIsStartDelay = true;
_node2.spline.specialEndNodeDelayStart = tDelay;
_node2.spline.specialEndNodeDelayStartResult = _node1.spline.road.RoadWidth();
_node2.spline.specialEndNodeStartOtherSpline = _node1.spline;
}
else
{
_node2.spline.isSpecialEndNodeIsEndDelay = true;
_node2.spline.specialEndNodeDelayEnd = tDelay;
_node2.spline.specialEndNodeDelayEndResult = _node1.spline.road.RoadWidth();
_node2.spline.specialEndNodeEndOtherSpline = _node1.spline;
}
}
else if (_node2.spline.road.laneAmount > _node1.spline.road.laneAmount)
{
_node1.isSpecialRoadConnPrimary = true;
NodeCreated1.isSpecialRoadConnPrimary = true;
if (_node1.spline.road.laneAmount == 4)
{
xWidth *= 2f;
}
tDelay = (_node2.spline.road.laneAmount - _node1.spline.road.laneAmount) * xWidth;
if (tDelay < 10f)
{
tDelay = 10f;
}
if (isNode1Start)
{
_node1.spline.isSpecialEndNodeIsStartDelay = true;
_node1.spline.specialEndNodeDelayStart = tDelay;
_node1.spline.specialEndNodeDelayStartResult = _node2.spline.road.RoadWidth();
_node1.spline.specialEndNodeStartOtherSpline = _node2.spline;
}
else
{
_node1.spline.isSpecialEndNodeIsEndDelay = true;
_node1.spline.specialEndNodeDelayEnd = tDelay;
_node1.spline.specialEndNodeDelayEndResult = _node2.spline.road.RoadWidth();
_node1.spline.specialEndNodeEndOtherSpline = _node2.spline;
}
}
else
{
_node1.isSpecialRoadConnPrimary = true;
NodeCreated1.isSpecialRoadConnPrimary = true;
tDelay = 0f;
_node1.spline.isSpecialEndNodeIsEndDelay = false;
_node1.spline.isSpecialEndNodeIsStartDelay = false;
_node1.spline.specialEndNodeDelayEnd = tDelay;
_node1.spline.specialEndNodeDelayEndResult = _node2.spline.road.RoadWidth();
_node1.spline.specialEndNodeEndOtherSpline = _node2.spline;
_node2.spline.isSpecialEndNodeIsEndDelay = false;
_node2.spline.isSpecialEndNodeIsStartDelay = false;
_node2.spline.specialEndNodeDelayEnd = tDelay;
_node2.spline.specialEndNodeDelayEndResult = _node1.spline.road.RoadWidth();
_node2.spline.specialEndNodeEndOtherSpline = _node1.spline;
}
_node1.specialNodeCounterpart = NodeCreated1;
_node2.specialNodeCounterpart = NodeCreated2;
NodeCreated1.specialNodeCounterpartMaster = _node1;
NodeCreated2.specialNodeCounterpartMaster = _node2;
NodeCreated1.ToggleHideFlags(true);
NodeCreated2.ToggleHideFlags(true);
SplineN[] OrigNodes = new SplineN[2];
OrigNodes[0] = _node1;
OrigNodes[1] = _node2;
_node1.originalConnectionNodes = OrigNodes;
_node2.originalConnectionNodes = OrigNodes;
//tNode1.spline.Setup_Trigger();
//if(tNode1.spline != tNode2.spline)
//{
// tNode2.spline.Setup_Trigger();
//}
// Schedule update
if (_node1 != null && _node2 != null)
{
if (_node1.spline != _node2.spline)
{
_node1.spline.road.PiggyBacks = new SplineC[1];
_node1.spline.road.PiggyBacks[0] = _node2.spline;
}
_node1.spline.road.isUpdateRequired = true;
}
previewSpline.isDrawingGizmos = false;
spline.previewSpline.isDrawingGizmos = false;
EngineIntegration.RepaintAllSceneView();
}
#endregion
#region "General Util"
public int GetNodeCount()
{
return nodes.Count;
}
public int GetNodeCountNonNull()
{
int nodeCount = GetNodeCount();
int validCount = 0;
for (int index = 0; index < nodeCount; index++)
{
if (nodes[index] != null)
{
validCount += 1;
if (nodes[index].isIntersection && nodes[index].intersection == null)
{
DestroyIntersection(nodes[index]);
}
}
}
return validCount;
}
/// <summary> Checks if the Nodes are null </summary>
public bool CheckInvalidNodeCount()
{
int nodeCount = GetNodeCount();
int validCount = 0;
for (int index = 0; index < nodeCount; index++)
{
if (nodes[index] != null)
{
validCount += 1;
if (nodes[index].isIntersection && nodes[index].intersection == null)
{
DestroyIntersection(nodes[index]);
}
}
}
if (validCount != nodeCount)
{
return true;
}
else
{
return false;
}
}
/// <summary> Get node from spline progress </summary>
public SplineN GetCurrentNode(float _p)
{
int nodeCount = GetNodeCount();
SplineN node = null;
for (int index = 0; index < nodeCount; index++)
{
node = nodes[index];
if (node.time > _p)
{
node = nodes[index - 1];
return node;
}
}
return node;
}
public SplineN GetLastLegitimateNode()
{
int nodeCount = GetNodeCount();
SplineN node = null;
for (int index = (nodeCount - 1); index >= 0; index--)
{
node = nodes[index];
if (node.IsLegitimate())
{
return node;
}
}
return null;
}
public SplineN GetLastNodeAll()
{
int startIndex = (GetNodeCount() - 1);
SplineN node = null;
int i = startIndex;
while (i >= 0)
{
if (i <= (nodes.Count - 1))
{
node = nodes[i];
if (node != null)
{
return node;
}
}
i -= 1;
}
return null;
}
public SplineN GetPrevLegitimateNode(int _index)
{
try
{
SplineN node = null;
for (int index = (_index - 1); index >= 0; index--)
{
node = nodes[index];
if (node.IsLegitimateGrade())
{
return node;
}
}
return null;
}
catch
{
return null;
}
}
public SplineN GetNextLegitimateNode(int _index)
{
SplineN node = null;
int nodeCount = GetNodeCount();
for (int index = (_index + 1); index < nodeCount; index++)
{
node = nodes[index];
if (node.IsLegitimateGrade())
{
return node;
}
}
return null;
}
/// <summary> Removes materials on all nodes </summary>
public void ClearAllRoadCuts()
{
int nodeCount = GetNodeCount();
for (int index = 0; index < nodeCount; index++)
{
nodes[index].ClearCuts();
}
}
public void ResetNavigationData()
{
connectedIDs = null;
connectedIDs = new List<int>();
}
#endregion
}
}