Add Unity3D projects
This commit is contained in:
@ -0,0 +1,223 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public static class Construction
|
||||
{
|
||||
/// <summary> Creates a node and performs validation checks </summary>
|
||||
public static SplineN CreateNode(Road _road, bool _isSpecialEndNode = false, Vector3 _vectorSpecialLoc = default(Vector3), bool _isInterNode = false)
|
||||
{
|
||||
Object[] worldNodeCount = GameObject.FindObjectsOfType<SplineN>();
|
||||
GameObject nodeObj = new GameObject("Node" + worldNodeCount.Length.ToString());
|
||||
|
||||
if (!_isInterNode)
|
||||
{
|
||||
EngineIntegration.RegisterUndo(nodeObj, "Created node");
|
||||
}
|
||||
|
||||
|
||||
SplineN node = nodeObj.AddComponent<SplineN>();
|
||||
|
||||
if (_isSpecialEndNode)
|
||||
{
|
||||
node.isSpecialEndNode = true;
|
||||
nodeObj.transform.position = _vectorSpecialLoc;
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeObj.transform.position = _road.editorMousePos;
|
||||
//This helps prevent double clicks:
|
||||
int nodeCount = _road.spline.GetNodeCount();
|
||||
for (int index = 0; index < nodeCount; index++)
|
||||
{
|
||||
if (Vector3.Distance(_road.editorMousePos, _road.spline.nodes[index].pos) < 5f)
|
||||
{
|
||||
Object.DestroyImmediate(nodeObj);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//End double click prevention
|
||||
}
|
||||
|
||||
|
||||
Vector3 xVect = nodeObj.transform.position;
|
||||
if (xVect.y < 0.03f)
|
||||
{
|
||||
xVect.y = 0.03f;
|
||||
}
|
||||
nodeObj.transform.position = xVect;
|
||||
|
||||
nodeObj.transform.parent = _road.splineObject.transform;
|
||||
node.idOnSpline = _road.spline.GetNodeCount() + 1;
|
||||
node.spline = _road.spline;
|
||||
|
||||
//Enforce max road grade:
|
||||
if (_road.isMaxGradeEnabled && !_isSpecialEndNode)
|
||||
{
|
||||
node.EnsureGradeValidity(-1, true);
|
||||
}
|
||||
|
||||
if (!_isInterNode && !_isSpecialEndNode)
|
||||
{
|
||||
_road.UpdateRoad();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Insert
|
||||
/// Detect closest node (if end node, auto select other node)
|
||||
/// Determine which node is closest (up or down) on spline
|
||||
/// Place node, adjust all id on splines
|
||||
/// Setup spline </summary>
|
||||
public static SplineN InsertNode(Road _road, bool _isForcedLoc = false, Vector3 _forcedLoc = default(Vector3), bool _isPreNode = false, int _insertIndex = -1, bool _isSpecialEndNode = false, bool _isInterNode = false)
|
||||
{
|
||||
GameObject nodeObj;
|
||||
Object[] worldNodeCount = GameObject.FindObjectsOfType<SplineN>();
|
||||
if (!_isForcedLoc)
|
||||
{
|
||||
nodeObj = new GameObject("Node" + worldNodeCount.Length.ToString());
|
||||
}
|
||||
else if (_isForcedLoc && !_isSpecialEndNode)
|
||||
{
|
||||
nodeObj = new GameObject("Node" + worldNodeCount.Length.ToString() + "Ignore");
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeObj = new GameObject("Node" + worldNodeCount.Length.ToString());
|
||||
}
|
||||
|
||||
|
||||
if (!_isInterNode)
|
||||
{
|
||||
EngineIntegration.RegisterUndo(nodeObj, "Inserted node");
|
||||
}
|
||||
|
||||
|
||||
if (!_isForcedLoc)
|
||||
{
|
||||
nodeObj.transform.position = _road.editorMousePos;
|
||||
|
||||
//This helps prevent double clicks:
|
||||
int nodeCount = _road.spline.GetNodeCount();
|
||||
for (int index = 0; index < nodeCount; index++)
|
||||
{
|
||||
if (Vector3.Distance(_road.editorMousePos, _road.spline.nodes[index].pos) < 15f)
|
||||
{
|
||||
Object.DestroyImmediate(nodeObj);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//End double click prevention
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeObj.transform.position = _forcedLoc;
|
||||
}
|
||||
Vector3 xVect = nodeObj.transform.position;
|
||||
if (xVect.y < 0.03f)
|
||||
{
|
||||
xVect.y = 0.03f;
|
||||
}
|
||||
nodeObj.transform.position = xVect;
|
||||
nodeObj.transform.parent = _road.splineObject.transform;
|
||||
|
||||
int childCount = _road.spline.nodes.Count;
|
||||
//float mDistance = 50000f;
|
||||
//float tDistance = 0f;
|
||||
|
||||
float param;
|
||||
if (!_isForcedLoc)
|
||||
{
|
||||
param = _road.spline.GetClosestParam(_road.editorMousePos, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
param = _road.spline.GetClosestParam(_forcedLoc, false, true);
|
||||
}
|
||||
bool isEndInsert = false;
|
||||
bool isZeroInsert = false;
|
||||
int iStart = 0;
|
||||
if (RootUtils.IsApproximately(param, 0f, 0.0001f))
|
||||
{
|
||||
isZeroInsert = true;
|
||||
iStart = 0;
|
||||
}
|
||||
else if (RootUtils.IsApproximately(param, 1f, 0.0001f))
|
||||
{
|
||||
isEndInsert = true;
|
||||
}
|
||||
|
||||
if (_isForcedLoc)
|
||||
{
|
||||
iStart = _insertIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < childCount; index++)
|
||||
{
|
||||
SplineN xNode = _road.spline.nodes[index];
|
||||
if (!isZeroInsert && !isEndInsert)
|
||||
{
|
||||
if (param > xNode.time)
|
||||
{
|
||||
iStart = xNode.idOnSpline + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isEndInsert)
|
||||
{
|
||||
iStart = _road.spline.nodes.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = iStart; i < childCount; i++)
|
||||
{
|
||||
_road.spline.nodes[i].idOnSpline += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new node
|
||||
SplineN node = nodeObj.AddComponent<SplineN>();
|
||||
if (_isForcedLoc && !_isSpecialEndNode)
|
||||
{
|
||||
node.isBridge = true;
|
||||
node.isIgnore = true;
|
||||
//tNode.bIsBridge_PreNode = bIsPreNode;
|
||||
//tNode.bIsBridge_PostNode = !bIsPreNode;
|
||||
}
|
||||
node.spline = _road.spline;
|
||||
node.idOnSpline = iStart;
|
||||
node.isSpecialEndNode = _isSpecialEndNode;
|
||||
if (!_isForcedLoc)
|
||||
{
|
||||
node.pos = _road.editorMousePos;
|
||||
}
|
||||
else
|
||||
{
|
||||
node.pos = _forcedLoc;
|
||||
}
|
||||
|
||||
_road.spline.nodes.Insert(iStart, node);
|
||||
|
||||
//Enforce maximum road grade:
|
||||
if (!_isForcedLoc && !_isSpecialEndNode && _road.isMaxGradeEnabled)
|
||||
{
|
||||
node.EnsureGradeValidity(iStart);
|
||||
}
|
||||
|
||||
if (!_isInterNode && !_isSpecialEndNode)
|
||||
{
|
||||
if (!_isForcedLoc)
|
||||
{
|
||||
_road.UpdateRoad();
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3decfdad53e38bd45a25cb1252e303cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,364 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class Construction2DRect
|
||||
{
|
||||
public Vector2 P1, P2, P3, P4;
|
||||
public float MaxDistance = 200f;
|
||||
public float MaxDistanceSQ = 200f;
|
||||
public float Height = 0f;
|
||||
public float MinI = 0f;
|
||||
public float MaxI = 0f;
|
||||
|
||||
private const float NearDist = 0.15f;
|
||||
private const float NearDistSQ = 0.0225f;
|
||||
private Vector2[] poly;
|
||||
private Vector2 x1 = default(Vector2);
|
||||
private Vector2 x2 = default(Vector2);
|
||||
private Vector2 oldPoint = default(Vector2);
|
||||
private Vector2 newPoint = default(Vector2);
|
||||
private bool inside = false;
|
||||
|
||||
|
||||
public Construction2DRect(Vector2 _P1, Vector2 _P2, Vector2 _P3, Vector2 _P4, float tHeight = 0f)
|
||||
{
|
||||
Construction2DRectDo(ref _P1, ref _P2, ref _P3, ref _P4, ref tHeight);
|
||||
}
|
||||
|
||||
|
||||
private void Construction2DRectDo(ref Vector2 _P1, ref Vector2 _P2, ref Vector2 _P3, ref Vector2 _P4, ref float _height)
|
||||
{
|
||||
P1 = _P1;
|
||||
P2 = _P2;
|
||||
P3 = _P3;
|
||||
P4 = _P4;
|
||||
Height = _height;
|
||||
|
||||
if (RootUtils.IsApproximately(P1.x, P2.x, 0.0001f))
|
||||
{
|
||||
P2.x += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P1.x, P3.x, 0.0001f))
|
||||
{
|
||||
P3.x += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P1.x, P4.x, 0.0001f))
|
||||
{
|
||||
P4.x += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P2.x, P3.x, 0.0001f))
|
||||
{
|
||||
P3.x += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P2.x, P4.x, 0.0001f))
|
||||
{
|
||||
P4.x += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P3.x, P4.x, 0.0001f))
|
||||
{
|
||||
P4.x += 0.0002f;
|
||||
}
|
||||
|
||||
if (RootUtils.IsApproximately(P1.y, P2.y, 0.0001f))
|
||||
{
|
||||
P2.y += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P1.y, P3.y, 0.0001f))
|
||||
{
|
||||
P3.y += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P1.y, P4.y, 0.0001f))
|
||||
{
|
||||
P4.y += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P2.y, P3.y, 0.0001f))
|
||||
{
|
||||
P3.y += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P2.y, P4.y, 0.0001f))
|
||||
{
|
||||
P4.y += 0.0002f;
|
||||
}
|
||||
if (RootUtils.IsApproximately(P3.y, P4.y, 0.0001f))
|
||||
{
|
||||
P4.y += 0.0002f;
|
||||
}
|
||||
|
||||
//Find two with smallest x, etc
|
||||
float[] tX = new float[4];
|
||||
float[] tY = new float[4];
|
||||
|
||||
tX[0] = P1.x;
|
||||
tX[1] = P2.x;
|
||||
tX[2] = P3.x;
|
||||
tX[3] = P4.x;
|
||||
|
||||
tY[0] = P1.y;
|
||||
tY[1] = P2.y;
|
||||
tY[2] = P3.y;
|
||||
tY[3] = P4.y;
|
||||
|
||||
float MinX1, MinX2;
|
||||
bool bIgnoreP1, bIgnoreP2, bIgnoreP3, bIgnoreP4;
|
||||
bIgnoreP1 = bIgnoreP2 = bIgnoreP3 = bIgnoreP4 = false;
|
||||
|
||||
//Get top two minimum X
|
||||
MinX1 = Mathf.Min(tX);
|
||||
tX = new float[3];
|
||||
int tCounter = 0;
|
||||
if (!RootUtils.IsApproximately(MinX1, P1.x, 0.0001f))
|
||||
{
|
||||
tX[tCounter] = P1.x;
|
||||
tCounter += 1;
|
||||
}
|
||||
if (!RootUtils.IsApproximately(MinX1, P2.x, 0.0001f))
|
||||
{
|
||||
tX[tCounter] = P2.x;
|
||||
tCounter += 1;
|
||||
}
|
||||
if (!RootUtils.IsApproximately(MinX1, P3.x, 0.0001f))
|
||||
{
|
||||
tX[tCounter] = P3.x;
|
||||
tCounter += 1;
|
||||
}
|
||||
if (!RootUtils.IsApproximately(MinX1, P4.x, 0.0001f))
|
||||
{
|
||||
tX[tCounter] = P4.x;
|
||||
tCounter += 1;
|
||||
}
|
||||
MinX2 = Mathf.Min(tX);
|
||||
|
||||
Vector2 xMin1 = default(Vector2);
|
||||
Vector2 xMin2 = default(Vector2);
|
||||
if (RootUtils.IsApproximately(MinX1, P1.x, 0.0001f))
|
||||
{
|
||||
xMin1 = P1;
|
||||
bIgnoreP1 = true;
|
||||
}
|
||||
else if (RootUtils.IsApproximately(MinX1, P2.x, 0.0001f))
|
||||
{
|
||||
xMin1 = P2;
|
||||
bIgnoreP2 = true;
|
||||
}
|
||||
else if (RootUtils.IsApproximately(MinX1, P3.x, 0.0001f))
|
||||
{
|
||||
xMin1 = P3;
|
||||
bIgnoreP3 = true;
|
||||
}
|
||||
else if (RootUtils.IsApproximately(MinX1, P4.x, 0.0001f))
|
||||
{
|
||||
xMin1 = P4;
|
||||
bIgnoreP4 = true;
|
||||
}
|
||||
|
||||
if (RootUtils.IsApproximately(MinX2, P1.x, 0.0001f))
|
||||
{
|
||||
xMin2 = P1;
|
||||
bIgnoreP1 = true;
|
||||
}
|
||||
else if (RootUtils.IsApproximately(MinX2, P2.x, 0.0001f))
|
||||
{
|
||||
xMin2 = P2;
|
||||
bIgnoreP2 = true;
|
||||
}
|
||||
else if (RootUtils.IsApproximately(MinX2, P3.x, 0.0001f))
|
||||
{
|
||||
xMin2 = P3;
|
||||
bIgnoreP3 = true;
|
||||
}
|
||||
else if (RootUtils.IsApproximately(MinX2, P4.x, 0.0001f))
|
||||
{
|
||||
xMin2 = P4;
|
||||
bIgnoreP4 = true;
|
||||
}
|
||||
|
||||
Vector2 TopLeft = default(Vector2);
|
||||
Vector2 BottomLeft = default(Vector2);
|
||||
if (xMin1.y > xMin2.y)
|
||||
{
|
||||
TopLeft = xMin1;
|
||||
BottomLeft = xMin2;
|
||||
}
|
||||
else
|
||||
{
|
||||
TopLeft = xMin2;
|
||||
BottomLeft = xMin1;
|
||||
}
|
||||
|
||||
Vector2 xMax1 = default(Vector2);
|
||||
Vector2 xMax2 = default(Vector2);
|
||||
bool bXmax1 = false;
|
||||
if (!bIgnoreP1)
|
||||
{
|
||||
xMax1 = P1;
|
||||
bXmax1 = true;
|
||||
}
|
||||
if (!bIgnoreP2)
|
||||
{
|
||||
if (bXmax1)
|
||||
{
|
||||
xMax2 = P2;
|
||||
}
|
||||
else
|
||||
{
|
||||
xMax1 = P2;
|
||||
bXmax1 = true;
|
||||
}
|
||||
}
|
||||
if (!bIgnoreP3)
|
||||
{
|
||||
if (bXmax1)
|
||||
{
|
||||
xMax2 = P3;
|
||||
}
|
||||
else
|
||||
{
|
||||
xMax1 = P3;
|
||||
bXmax1 = true;
|
||||
}
|
||||
}
|
||||
if (!bIgnoreP4)
|
||||
{
|
||||
if (bXmax1)
|
||||
{
|
||||
xMax2 = P4;
|
||||
}
|
||||
else
|
||||
{
|
||||
xMax1 = P4;
|
||||
bXmax1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 TopRight = default(Vector2);
|
||||
Vector2 BottomRight = default(Vector2);
|
||||
if (xMax1.y > xMax2.y)
|
||||
{
|
||||
TopRight = xMax1;
|
||||
BottomRight = xMax2;
|
||||
}
|
||||
else
|
||||
{
|
||||
TopRight = xMax2;
|
||||
BottomRight = xMax1;
|
||||
}
|
||||
|
||||
P1 = BottomLeft;
|
||||
P2 = BottomRight;
|
||||
P3 = TopRight;
|
||||
P4 = TopLeft;
|
||||
|
||||
poly = new Vector2[4];
|
||||
poly[0] = P1;
|
||||
poly[1] = P2;
|
||||
poly[2] = P3;
|
||||
poly[3] = P4;
|
||||
|
||||
float[] tMaxes = new float[6];
|
||||
tMaxes[0] = Vector2.Distance(P1, P2);
|
||||
tMaxes[1] = Vector2.Distance(P1, P3);
|
||||
tMaxes[2] = Vector2.Distance(P1, P4);
|
||||
tMaxes[3] = Vector2.Distance(P2, P3);
|
||||
tMaxes[4] = Vector2.Distance(P2, P4);
|
||||
tMaxes[5] = Vector2.Distance(P3, P4);
|
||||
MaxDistance = Mathf.Max(tMaxes) * 1.5f;
|
||||
|
||||
float[] tMaxesSQ = new float[6];
|
||||
tMaxesSQ[0] = Vector2.SqrMagnitude(P1 - P2);
|
||||
tMaxesSQ[1] = Vector2.SqrMagnitude(P1 - P3);
|
||||
tMaxesSQ[2] = Vector2.SqrMagnitude(P1 - P4);
|
||||
tMaxesSQ[3] = Vector2.SqrMagnitude(P2 - P3);
|
||||
tMaxesSQ[4] = Vector2.SqrMagnitude(P2 - P4);
|
||||
tMaxesSQ[5] = Vector2.SqrMagnitude(P3 - P4);
|
||||
MaxDistanceSQ = Mathf.Max(tMaxesSQ) * 1.5f;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns true if _p is inside the rect </summary>
|
||||
public bool Contains(ref Vector2 _p)
|
||||
{
|
||||
//if(Vector2.Distance(_p,P1) > MaxDistance)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
if (Vector2.SqrMagnitude(_p - P1) > MaxDistanceSQ)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//if(poly.Length != 4)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
|
||||
inside = false;
|
||||
oldPoint = new Vector2(poly[4 - 1].x, poly[4 - 1].y);
|
||||
for (int index = 0; index < 4; index++)
|
||||
{
|
||||
newPoint = new Vector2(poly[index].x, poly[index].y);
|
||||
if (newPoint.x > oldPoint.x)
|
||||
{
|
||||
x1 = oldPoint;
|
||||
x2 = newPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = newPoint;
|
||||
x2 = oldPoint;
|
||||
}
|
||||
if ((newPoint.x < _p.x) == (_p.x <= oldPoint.x) && (_p.y - x1.y) * (x2.x - x1.x) < (x2.y - x1.y) * (_p.x - x1.x))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
oldPoint = newPoint;
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns true if _vect is near the P1-P4 values </summary>
|
||||
public bool Near(ref Vector2 _vect, out Vector2 _nearVect)
|
||||
{
|
||||
if (Vector2.SqrMagnitude(_vect - P1) > MaxDistanceSQ)
|
||||
{
|
||||
//if(Vector2.Distance(tVect,P1) > MaxDistance){
|
||||
_nearVect = default(Vector2);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Vector2.SqrMagnitude(_vect - P1) < NearDistSQ)
|
||||
{
|
||||
//if(Vector2.Distance(tVect,P1) < NearDist){
|
||||
_nearVect = P1;
|
||||
return true;
|
||||
}
|
||||
if (Vector2.SqrMagnitude(_vect - P2) < NearDistSQ)
|
||||
{
|
||||
//if(Vector2.Distance(tVect,P2) < NearDist){
|
||||
_nearVect = P2;
|
||||
return true;
|
||||
}
|
||||
if (Vector2.SqrMagnitude(_vect - P3) < NearDistSQ)
|
||||
{
|
||||
//if(Vector2.Distance(tVect,P3) < NearDist){
|
||||
_nearVect = P3;
|
||||
return true;
|
||||
}
|
||||
if (Vector2.SqrMagnitude(_vect - P4) < NearDistSQ)
|
||||
{
|
||||
//if(Vector2.Distance(tVect,P4) < NearDist){
|
||||
_nearVect = P4;
|
||||
return true;
|
||||
}
|
||||
_nearVect = default(Vector2);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public string ToStringRA()
|
||||
{
|
||||
return ("P1:" + P1.ToString() + " P2:" + P2.ToString() + " P3:" + P3.ToString() + " P4:" + P4.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9128cfbedf3667e4d94db8f6382e15c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,258 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Construction3DTri
|
||||
{
|
||||
public Vector3 P1, P2, P3;
|
||||
const float NearDist = 0.15f;
|
||||
const float NearDistSQ = 0.0225f;
|
||||
Vector2[] poly2D;
|
||||
Vector3[] poly3D;
|
||||
public float MaxDistance = 200f;
|
||||
public float MaxDistanceSq = 200f;
|
||||
public Vector3 normal = default(Vector3);
|
||||
public Vector3 pMiddle = default(Vector3);
|
||||
public float MinI = 0f;
|
||||
public float MaxI = 1f;
|
||||
|
||||
|
||||
public Construction3DTri(Vector3 _P1, Vector3 _P2, Vector3 _P3, float _MinI, float _MaxI)
|
||||
{
|
||||
MinI = _MinI;
|
||||
MaxI = _MaxI;
|
||||
P1 = _P1;
|
||||
P2 = _P2;
|
||||
P3 = _P3;
|
||||
|
||||
poly2D = new Vector2[3];
|
||||
poly2D[0] = new Vector2(P1.x, P1.z);
|
||||
poly2D[1] = new Vector2(P2.x, P2.z);
|
||||
poly2D[2] = new Vector2(P3.x, P3.z);
|
||||
|
||||
poly3D = new Vector3[3];
|
||||
poly3D[0] = P1;
|
||||
poly3D[1] = P2;
|
||||
poly3D[2] = P3;
|
||||
|
||||
float[] tMaxes = new float[3];
|
||||
tMaxes[0] = Vector3.Distance(P1, P2);
|
||||
tMaxes[1] = Vector3.Distance(P1, P3);
|
||||
tMaxes[2] = Vector3.Distance(P2, P3);
|
||||
MaxDistance = Mathf.Max(tMaxes) * 1.5f;
|
||||
|
||||
float[] tMaxesSQ = new float[3];
|
||||
tMaxesSQ[0] = Vector3.SqrMagnitude(P1 - P2);
|
||||
tMaxesSQ[1] = Vector3.SqrMagnitude(P1 - P3);
|
||||
tMaxesSQ[2] = Vector3.SqrMagnitude(P2 - P3);
|
||||
MaxDistanceSq = Mathf.Max(tMaxesSQ) * 1.5f;
|
||||
|
||||
PlaneFrom3Points(out normal, out pMiddle, P1, P2, P3);
|
||||
|
||||
normal = Vector3.Cross((P3 - P1), (P2 - P1));
|
||||
|
||||
//////This creates middle point:
|
||||
//Vector3 tMiddle1 = ((P3 - P1) * 0.5f) + P1;
|
||||
//Vector3 tMiddle2 = ((P2 - P1) * 0.5f) + P1;
|
||||
//pMiddle = ((tMiddle2 - tMiddle1) * 0.5f) + tMiddle1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Get the intersection between a line and a plane.
|
||||
/// If the line and plane are not parallel, the function outputs true, otherwise false. </summary>
|
||||
public Vector3 LinePlaneIntersection(ref Vector3 _F1)
|
||||
{
|
||||
_F1.y = 0f;
|
||||
|
||||
//calculate the distance between the linePoint and the line-plane intersection point
|
||||
float dotNumerator = Vector3.Dot((pMiddle - _F1), normal);
|
||||
float dotDenominator = Vector3.Dot(Vector3.up.normalized, normal);
|
||||
|
||||
//line and plane are not parallel
|
||||
if (!RootUtils.IsApproximately(0f, dotDenominator, 0.001f))
|
||||
{
|
||||
//get the coordinates of the line-plane intersection point
|
||||
return (_F1 + (Vector3.up.normalized * (dotNumerator / dotDenominator)));
|
||||
}
|
||||
else
|
||||
{
|
||||
//output not valid
|
||||
return default(Vector3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Convert a plane defined by 3 points to a plane defined by a vector and a point.
|
||||
/// The plane point is the middle of the triangle defined by the 3 points. </summary>
|
||||
public static void PlaneFrom3Points(out Vector3 _planeNormal, out Vector3 _planePoint, Vector3 _pointA, Vector3 _pointB, Vector3 _pointC)
|
||||
{
|
||||
_planeNormal = Vector3.zero;
|
||||
_planePoint = Vector3.zero;
|
||||
|
||||
//Make two vectors from the 3 input points, originating from point A
|
||||
Vector3 AB = _pointB - _pointA;
|
||||
Vector3 AC = _pointC - _pointA;
|
||||
|
||||
//Calculate the normal
|
||||
_planeNormal = Vector3.Normalize(Vector3.Cross(AB, AC));
|
||||
|
||||
//Get the points in the middle AB and AC
|
||||
Vector3 middleAB = _pointA + (AB / 2.0f);
|
||||
Vector3 middleAC = _pointA + (AC / 2.0f);
|
||||
|
||||
//Get vectors from the middle of AB and AC to the point which is not on that line.
|
||||
Vector3 middleABtoC = _pointC - middleAB;
|
||||
Vector3 middleACtoB = _pointB - middleAC;
|
||||
|
||||
//Calculate the intersection between the two lines. This will be the center
|
||||
//of the triangle defined by the 3 points.
|
||||
//We could use LineLineIntersection instead of ClosestPointsOnTwoLines but due to rounding errors
|
||||
//this sometimes doesn't work.
|
||||
Vector3 temp;
|
||||
ClosestPointsOnTwoLines(out _planePoint, out temp, middleAB, middleABtoC, middleAC, middleACtoB);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Two non-parallel lines which may or may not touch each other have a point on each line which are closest
|
||||
/// to each other. This function finds those two points. If the lines are not parallel, the function
|
||||
/// outputs true, otherwise false. </summary>
|
||||
public static bool ClosestPointsOnTwoLines(out Vector3 _closestPointLine1, out Vector3 _closestPointLine2, Vector3 _linePoint1, Vector3 _lineVec1, Vector3 _linePoint2, Vector3 _lineVec2)
|
||||
{
|
||||
_closestPointLine1 = Vector3.zero;
|
||||
_closestPointLine2 = Vector3.zero;
|
||||
|
||||
float a = Vector3.Dot(_lineVec1, _lineVec1);
|
||||
float b = Vector3.Dot(_lineVec1, _lineVec2);
|
||||
float e = Vector3.Dot(_lineVec2, _lineVec2);
|
||||
|
||||
float d = a * e - b * b;
|
||||
|
||||
//lines are not parallel
|
||||
if (d != 0.0f)
|
||||
{
|
||||
|
||||
Vector3 r = _linePoint1 - _linePoint2;
|
||||
float c = Vector3.Dot(_lineVec1, r);
|
||||
float f = Vector3.Dot(_lineVec2, r);
|
||||
|
||||
float s = (b * f - c * e) / d;
|
||||
float t = (a * f - c * b) / d;
|
||||
|
||||
_closestPointLine1 = _linePoint1 + _lineVec1 * s;
|
||||
_closestPointLine2 = _linePoint2 + _lineVec2 * t;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Creates a vector of direction _vector with length _size </summary>
|
||||
public static Vector3 SetVectorLength(Vector3 _vector, float _size)
|
||||
{
|
||||
//normalize the vector
|
||||
Vector3 vectorNormalized = Vector3.Normalize(_vector);
|
||||
|
||||
//scale the vector
|
||||
return vectorNormalized *= _size;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns true if _p is contained in this </summary>
|
||||
public bool Contains2D(ref Vector2 _p)
|
||||
{
|
||||
if (Vector2.SqrMagnitude(_p - poly2D[0]) > MaxDistanceSq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//if(Vector2.Distance(p,P1) > MaxDistance)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
//if(poly2D.Length != 3)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
|
||||
Vector2 x1;
|
||||
Vector2 x2;
|
||||
Vector2 oldPoint;
|
||||
Vector2 newPoint;
|
||||
bool inside = false;
|
||||
|
||||
oldPoint = new Vector2(poly2D[3 - 1].x, poly2D[3 - 1].y);
|
||||
for (int index = 0; index < 3; index++)
|
||||
{
|
||||
newPoint = new Vector2(poly2D[index].x, poly2D[index].y);
|
||||
if (newPoint.x > oldPoint.x)
|
||||
{
|
||||
x1 = oldPoint;
|
||||
x2 = newPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = newPoint;
|
||||
x2 = oldPoint;
|
||||
}
|
||||
if ((newPoint.x < _p.x) == (_p.x <= oldPoint.x) && (_p.y - x1.y) * (x2.x - x1.x) < (x2.y - x1.y) * (_p.x - x1.x))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
oldPoint = newPoint;
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns true if Vector2(_p.x, _p.z) is contained in this </summary>
|
||||
public bool Contains2D(ref Vector3 _p)
|
||||
{
|
||||
Vector2 tVect = new Vector2(_p.x, _p.z);
|
||||
return Contains2D(ref tVect);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns true if _vect is near the P1-P3 values </summary>
|
||||
public bool Near(ref Vector3 _vect, out Vector3 _nearVect)
|
||||
{
|
||||
if (Vector3.SqrMagnitude(_vect - P1) > MaxDistanceSq)
|
||||
{
|
||||
//if(Vector3.Distance(tVect,P1) > MaxDistance){
|
||||
_nearVect = default(Vector3);
|
||||
return false;
|
||||
}
|
||||
|
||||
//if(Vector3.Distance(tVect,P1) < NearDist){
|
||||
if (Vector3.SqrMagnitude(_vect - P1) < NearDistSQ)
|
||||
{
|
||||
_nearVect = P1;
|
||||
return true;
|
||||
}
|
||||
//if(Vector3.Distance(tVect,P2) < NearDist){
|
||||
if (Vector3.SqrMagnitude(_vect - P2) < NearDistSQ)
|
||||
{
|
||||
_nearVect = P2;
|
||||
return true;
|
||||
}
|
||||
//if(Vector3.Distance(tVect,P3) < NearDist){
|
||||
if (Vector3.SqrMagnitude(_vect - P3) < NearDistSQ)
|
||||
{
|
||||
_nearVect = P3;
|
||||
return true;
|
||||
}
|
||||
_nearVect = default(Vector3);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public string ToStringRA()
|
||||
{
|
||||
return ("P1:" + P1.ToString() + " P2:" + P2.ToString() + " P3:" + P3.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76e9419cbeed3894195e9636c89bcf07
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2602b8c6977a4bd4a8774de9e0c51159
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8907a34bd7695644eb4ed18dedfc90ad
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,196 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using RoadArchitect;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
/// <summary> Provides the menu items inside the editor </summary>
|
||||
public class EditorMenu
|
||||
{
|
||||
/// <summary> Creates the road system. </summary>
|
||||
[MenuItem("Window/Road Architect/Create road system")]
|
||||
public static void CreateRoadSystem()
|
||||
{
|
||||
Object[] allRoadSystemObjects = GameObject.FindObjectsOfType<RoadSystem>();
|
||||
int nextCount = (allRoadSystemObjects.Length + 1);
|
||||
allRoadSystemObjects = null;
|
||||
|
||||
GameObject newRoadSystemObject = new GameObject("RoadArchitectSystem" + nextCount.ToString());
|
||||
RoadSystem newRoadSystem = newRoadSystemObject.AddComponent<RoadSystem>();
|
||||
//Add road for new road system.
|
||||
newRoadSystem.AddRoad(true);
|
||||
|
||||
GameObject masterIntersectionsObject = new GameObject("Intersections");
|
||||
masterIntersectionsObject.transform.parent = newRoadSystemObject.transform;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Add road to gameobject. Not sure if this is necessary. </summary>
|
||||
[MenuItem("Window/Road Architect/Add road")]
|
||||
public static void AddRoad()
|
||||
{
|
||||
Object[] allRoadSystemObjects = GameObject.FindObjectsOfType<RoadSystem>();
|
||||
if (allRoadSystemObjects != null && allRoadSystemObjects.Length == 0)
|
||||
{
|
||||
CreateRoadSystem();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
RoadSystem firstRoadSystem = (RoadSystem)allRoadSystemObjects[0];
|
||||
Selection.activeGameObject = firstRoadSystem.AddRoad();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Updates all roads. Used when things get out of sync. </summary>
|
||||
[MenuItem("Window/Road Architect/Update All Roads")]
|
||||
public static void UpdateAllRoads()
|
||||
{
|
||||
Road[] allRoadObjects = GameObject.FindObjectsOfType<Road>();
|
||||
|
||||
int roadCount = allRoadObjects.Length;
|
||||
|
||||
Road singleRoad = null;
|
||||
SplineC[] tPiggys = null;
|
||||
if (roadCount > 1)
|
||||
{
|
||||
tPiggys = new SplineC[roadCount - 1];
|
||||
}
|
||||
|
||||
for (int count = 0; count < roadCount; count++)
|
||||
{
|
||||
singleRoad = allRoadObjects[count];
|
||||
if (count > 0)
|
||||
{
|
||||
tPiggys[count - 1] = singleRoad.spline;
|
||||
}
|
||||
}
|
||||
|
||||
singleRoad = allRoadObjects[0];
|
||||
if (tPiggys != null && tPiggys.Length > 0)
|
||||
{
|
||||
singleRoad.PiggyBacks = tPiggys;
|
||||
}
|
||||
singleRoad.UpdateRoad();
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Show the help screen. </summary>
|
||||
[MenuItem("Window/Road Architect/Help")]
|
||||
public static void ShowHelpWindow()
|
||||
{
|
||||
HelpWindow helpWindow = EditorWindow.GetWindow<HelpWindow>();
|
||||
helpWindow.Initialize();
|
||||
}
|
||||
|
||||
|
||||
/// <summary> WARNING: Only call this on an empty scene that has some terrains on it. We are not responsbile for data loss if this function is called by the user. </summary>
|
||||
[MenuItem("Window/Road Architect/Testing/Run all unit tests (caution)")]
|
||||
public static void TestProgram()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning !", "This will delete your RoadSystem 1, 6, 7, 8 and 9 and will create a lot of test roads.", "OK", "Cancel"))
|
||||
{
|
||||
RoadArchitect.Tests.UnitTests.RoadArchitectUnitTests();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Testing/Run unit test 1-5 (caution)")]
|
||||
public static void Test1To5()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning !", "This will delete your first RoadSystem and will create a lot of test roads.", "OK", "Cancel"))
|
||||
{
|
||||
RoadArchitect.Tests.UnitTests.RoadArchitectUnitTest1To5();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Testing/Run unit test 6 (caution)")]
|
||||
public static void Test6()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning !", "This will delete your sixth RoadSystem and will create a lot of test roads.", "OK", "Cancel"))
|
||||
{
|
||||
RoadArchitect.Tests.UnitTests.RoadArchitectUnitTest6();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Testing/Run unit test 7 (caution)")]
|
||||
public static void Test7()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning !", "This will delete your seventh RoadSystem and will create a lot of test roads.", "OK", "Cancel"))
|
||||
{
|
||||
RoadArchitect.Tests.UnitTests.RoadArchitectUnitTest7();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Testing/Run unit test 8 (caution)")]
|
||||
public static void Test8()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning !", "This will delete your eigth RoadSystem and will create a test roads.", "OK", "Cancel"))
|
||||
{
|
||||
RoadArchitect.Tests.UnitTests.RoadArchitectUnitTest8();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Testing/Run unit test 9 (caution)")]
|
||||
public static void Test9()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning !", "This will delete your RoadArchitectSystem9 and will create test roads.", "OK", "Cancel"))
|
||||
{
|
||||
RoadArchitect.Tests.UnitTests.RoadArchitectUnitTest9();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Testing/Run unit test 10 (caution)")]
|
||||
public static void Test10()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning !", "This will delete your RoadArchitectSystem10 and will create test roads.", "OK", "Cancel"))
|
||||
{
|
||||
RoadArchitect.Tests.UnitTests.RoadArchitectUnitTest10();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> WARNING: Only call this on an empty scene that has some terrains on it. We are not responsbile for data loss if this function is called by the user. </summary>
|
||||
[MenuItem("Window/Road Architect/Testing/Clean up tests (caution)")]
|
||||
public static void TestCleanup()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning !", "This will delete your RoadSystem 1, 6, 7, 8 and 9", "OK", "Cancel"))
|
||||
{
|
||||
RoadArchitect.Tests.UnitTests.CleanupAllTests();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Get code line count for RA project </summary>
|
||||
[MenuItem("Window/Road Architect/Testing/Get line count of RA")]
|
||||
public static void TestCodeCount()
|
||||
{
|
||||
string mainDir = System.IO.Path.Combine(System.Environment.CurrentDirectory, RoadEditorUtility.GetBasePathForIO());
|
||||
string[] files = System.IO.Directory.GetFiles(mainDir, "*.cs", System.IO.SearchOption.AllDirectories);
|
||||
int lineCount = 0;
|
||||
foreach (string file in files)
|
||||
{
|
||||
lineCount += System.IO.File.ReadAllLines(file).Length;
|
||||
}
|
||||
Debug.Log(string.Format("{0:n0}", lineCount) + " lines of code in Road Architect.");
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Report a Bug")]
|
||||
public static void ReportBug()
|
||||
{
|
||||
Application.OpenURL("https://github.com/FritzsHero/RoadArchitect/issues");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09c94f91ab2d0d34c9c464536e8633e9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,60 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
/// <summary> Used for progress information for other areas of RA. </summary>
|
||||
public class EditorProgressWindow : EditorWindow
|
||||
{
|
||||
private float seconds = 10.0f;
|
||||
private float startValue = 0f;
|
||||
private float progress = 0f;
|
||||
|
||||
|
||||
private static void Init()
|
||||
{
|
||||
EditorProgressWindow window = (EditorProgressWindow)EditorWindow.GetWindow(typeof(EditorProgressWindow));
|
||||
window.Show();
|
||||
}
|
||||
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
seconds = EditorGUILayout.FloatField("Time to wait:", seconds);
|
||||
if (GUILayout.Button("Display bar"))
|
||||
{
|
||||
if (seconds < 1)
|
||||
{
|
||||
Debug.LogError("Seconds should be bigger than 1");
|
||||
return;
|
||||
}
|
||||
startValue = (float)EditorApplication.timeSinceStartup;
|
||||
}
|
||||
|
||||
if (progress < seconds)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar(
|
||||
"Simple Progress Bar",
|
||||
"Shows a progress bar for the given seconds",
|
||||
progress / seconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
EngineIntegration.ClearProgressBar();
|
||||
}
|
||||
|
||||
progress = (float)(EditorApplication.timeSinceStartup - startValue);
|
||||
}
|
||||
|
||||
|
||||
private void OnInspectorUpdate()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36611904ee7524a45afea00db85e5039
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,32 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class EditorUtilities
|
||||
{
|
||||
/// <summary> Opens the loacally stored manual </summary>
|
||||
public static void OpenOfflineManual()
|
||||
{
|
||||
Application.OpenURL(System.Environment.CurrentDirectory.Replace(@"\", "/") + "/" + RoadEditorUtility.GetBasePath() + "/RoadArchitectManual.htm");
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Loads the _texture from _path if necessary </summary>
|
||||
public static void LoadTexture<T>(ref T _texture, string _path) where T : Texture
|
||||
{
|
||||
_texture = EngineIntegration.LoadAssetFromPath<T>(_path);
|
||||
}
|
||||
|
||||
|
||||
public static void DrawLine(float _spacing = 4f, float _size = 1f)
|
||||
{
|
||||
//Horizontal bar
|
||||
GUILayout.Space(_spacing);
|
||||
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(_size));
|
||||
GUILayout.Space(_spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f40193a8b5e9ed84f9597bbaefb0635d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,70 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class HelpWindow : EditorWindow
|
||||
{
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorStyles.label.wordWrap = true;
|
||||
EditorStyles.miniLabel.wordWrap = true;
|
||||
|
||||
EditorGUILayout.LabelField("Road Architect Help", EditorStyles.boldLabel);
|
||||
GUILayout.Space(12f);
|
||||
EditorGUILayout.LabelField("Please visit the online manual for help.");
|
||||
GUILayout.Space(4f);
|
||||
|
||||
|
||||
if (GUILayout.Button("Click here to open online manual", EditorStyles.toolbarButton, GUILayout.Width(310f)))
|
||||
{
|
||||
Application.OpenURL("https://github.com/FritzsHero/RoadArchitect/wiki");
|
||||
}
|
||||
EditorGUILayout.LabelField("https://github.com/FritzsHero/RoadArchitect/wiki");
|
||||
|
||||
if (GUILayout.Button("Click here to open offline manual", EditorStyles.toolbarButton, GUILayout.Width(310f)))
|
||||
{
|
||||
EditorUtilities.OpenOfflineManual();
|
||||
}
|
||||
|
||||
|
||||
GUILayout.Space(12f);
|
||||
|
||||
|
||||
EditorGUILayout.LabelField("Please visit us or reach out to us on Github (links below) with any questions or comments.");
|
||||
EditorGUILayout.LabelField("If you encounter Bugs or have a Feature Suggestion, you can submit them on the following sites:");
|
||||
|
||||
|
||||
GUILayout.Space(12f);
|
||||
|
||||
if (GUILayout.Button("RoadArchitect Repository", EditorStyles.toolbarButton, GUILayout.Width(310f)))
|
||||
{
|
||||
Application.OpenURL("https://github.com/FritzsHero/RoadArchitect");
|
||||
}
|
||||
EditorGUILayout.LabelField("https://github.com/FritzsHero/RoadArchitect");
|
||||
|
||||
GUILayout.Space(4f);
|
||||
|
||||
if (GUILayout.Button("RoadArchitect Issues", EditorStyles.toolbarButton, GUILayout.Width(310f)))
|
||||
{
|
||||
Application.OpenURL("https://github.com/FritzsHero/RoadArchitect/issues");
|
||||
}
|
||||
EditorGUILayout.LabelField("https://github.com/FritzsHero/RoadArchitect/issues");
|
||||
GUILayout.Space(12f);
|
||||
}
|
||||
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Rect rect = new Rect(340, 170, 400, 400);
|
||||
position = rect;
|
||||
Show();
|
||||
titleContent.text = "Help Info";
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83a696e187e0572418fa95bc2a0cecd0
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,395 @@
|
||||
/*
|
||||
Based on ObjExporter.cs, this "wrapper" lets you export to .OBJ directly from the editor menu.
|
||||
|
||||
Use by selecting the objects you want to export, and select the appropriate menu item from "Custom->Export".
|
||||
Exported models are put in a folder called "ExportedObj" in the root of your project.
|
||||
Textures should also be copied and placed in the same folder.
|
||||
*/
|
||||
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class ObjExporter : ScriptableObject
|
||||
{
|
||||
private static int vertexOffset = 0;
|
||||
private static int normalOffset = 0;
|
||||
private static int uvOffset = 0;
|
||||
|
||||
|
||||
//User should probably be able to change this. It is currently left as an excercise for
|
||||
//the reader.
|
||||
private static string targetFolder = "ExportedObj";
|
||||
|
||||
|
||||
private struct ObjMaterial
|
||||
{
|
||||
public string name;
|
||||
public string textureName;
|
||||
}
|
||||
|
||||
|
||||
private static string MeshToString(MeshFilter _meshFilter, Dictionary<string, ObjMaterial> _materialList)
|
||||
{
|
||||
Mesh mesh = _meshFilter.sharedMesh;
|
||||
Renderer renderer = _meshFilter.GetComponent<Renderer>();
|
||||
//Material[] mats = mf.renderer.sharedMaterials;
|
||||
Material[] materials = renderer.sharedMaterials;
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.Append("g ").Append(_meshFilter.name).Append("\n");
|
||||
foreach (Vector3 lv in mesh.vertices)
|
||||
{
|
||||
Vector3 wv = _meshFilter.transform.TransformPoint(lv);
|
||||
|
||||
//This is sort of ugly - inverting x-component since we're in
|
||||
//a different coordinate system than "everyone" is "used to".
|
||||
stringBuilder.Append(string.Format("v {0} {1} {2}\n", -wv.x, wv.y, wv.z));
|
||||
}
|
||||
stringBuilder.Append("\n");
|
||||
|
||||
foreach (Vector3 lv in mesh.normals)
|
||||
{
|
||||
Vector3 wv = _meshFilter.transform.TransformDirection(lv);
|
||||
|
||||
stringBuilder.Append(string.Format("vn {0} {1} {2}\n", -wv.x, wv.y, wv.z));
|
||||
}
|
||||
stringBuilder.Append("\n");
|
||||
|
||||
foreach (Vector3 v in mesh.uv)
|
||||
{
|
||||
stringBuilder.Append(string.Format("vt {0} {1}\n", v.x, v.y));
|
||||
}
|
||||
|
||||
for (int material = 0; material < mesh.subMeshCount; material++)
|
||||
{
|
||||
stringBuilder.Append("\n");
|
||||
stringBuilder.Append("usemtl ").Append(materials[material].name).Append("\n");
|
||||
stringBuilder.Append("usemap ").Append(materials[material].name).Append("\n");
|
||||
|
||||
//See if this material is already in the materiallist.
|
||||
try
|
||||
{
|
||||
ObjMaterial objMaterial = new ObjMaterial();
|
||||
|
||||
objMaterial.name = materials[material].name;
|
||||
|
||||
if (materials[material].mainTexture)
|
||||
{
|
||||
objMaterial.textureName = EngineIntegration.GetAssetPath(materials[material].mainTexture);
|
||||
}
|
||||
else
|
||||
{
|
||||
objMaterial.textureName = null;
|
||||
}
|
||||
|
||||
_materialList.Add(objMaterial.name, objMaterial);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
//Already in the dictionary
|
||||
}
|
||||
|
||||
|
||||
int[] triangles = mesh.GetTriangles(material);
|
||||
for (int index = 0; index < triangles.Length; index += 3)
|
||||
{
|
||||
//Because we inverted the x-component, we also needed to alter the triangle winding.
|
||||
stringBuilder.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n",
|
||||
triangles[index] + 1 + vertexOffset, triangles[index + 1] + 1 + normalOffset, triangles[index + 2] + 1 + uvOffset));
|
||||
}
|
||||
}
|
||||
|
||||
vertexOffset += mesh.vertices.Length;
|
||||
normalOffset += mesh.normals.Length;
|
||||
uvOffset += mesh.uv.Length;
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
|
||||
private static void Clear()
|
||||
{
|
||||
vertexOffset = 0;
|
||||
normalOffset = 0;
|
||||
uvOffset = 0;
|
||||
}
|
||||
|
||||
|
||||
private static Dictionary<string, ObjMaterial> PrepareFileWrite()
|
||||
{
|
||||
Clear();
|
||||
|
||||
return new Dictionary<string, ObjMaterial>();
|
||||
}
|
||||
|
||||
|
||||
private static void MaterialsToFile(Dictionary<string, ObjMaterial> _materialList, string _folder, string _fileName)
|
||||
{
|
||||
using (StreamWriter streamWriter = new StreamWriter(Path.Combine(_folder, _fileName) + ".mtl"))
|
||||
{
|
||||
foreach (KeyValuePair<string, ObjMaterial> kvp in _materialList)
|
||||
{
|
||||
streamWriter.Write("\n");
|
||||
streamWriter.Write("newmtl {0}\n", kvp.Key);
|
||||
streamWriter.Write("Ka 0.6 0.6 0.6\n");
|
||||
streamWriter.Write("Kd 0.6 0.6 0.6\n");
|
||||
streamWriter.Write("Ks 0.9 0.9 0.9\n");
|
||||
streamWriter.Write("d 1.0\n");
|
||||
streamWriter.Write("Ns 0.0\n");
|
||||
streamWriter.Write("illum 2\n");
|
||||
|
||||
if (kvp.Value.textureName != null)
|
||||
{
|
||||
string destinationFile = kvp.Value.textureName;
|
||||
|
||||
int stripIndex = destinationFile.LastIndexOf(Path.PathSeparator);
|
||||
|
||||
if (stripIndex >= 0)
|
||||
{
|
||||
destinationFile = destinationFile.Substring(stripIndex + 1).Trim();
|
||||
}
|
||||
|
||||
|
||||
string relativeFile = destinationFile;
|
||||
|
||||
destinationFile = Path.Combine(_folder, destinationFile);
|
||||
|
||||
//Debug.Log("Copying texture from " + kvp.Value.textureName + " to " + destinationFile);
|
||||
|
||||
try
|
||||
{
|
||||
//Copy the source file
|
||||
File.Copy(kvp.Value.textureName, destinationFile);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
streamWriter.Write("map_Kd {0}", relativeFile);
|
||||
}
|
||||
|
||||
streamWriter.Write("\n\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void MeshToFile(MeshFilter _meshFilter, string _folder, string _fileName)
|
||||
{
|
||||
Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();
|
||||
|
||||
using (StreamWriter streamWriter = new StreamWriter(Path.Combine(_folder, _fileName) + ".obj"))
|
||||
{
|
||||
streamWriter.Write("mtllib ./" + _fileName + ".mtl\n");
|
||||
|
||||
streamWriter.Write(MeshToString(_meshFilter, materialList));
|
||||
}
|
||||
|
||||
MaterialsToFile(materialList, _folder, _fileName);
|
||||
}
|
||||
|
||||
|
||||
private static void MeshesToFile(MeshFilter[] _meshFilter, string _folder, string _fileName)
|
||||
{
|
||||
Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();
|
||||
|
||||
using (StreamWriter streamWriter = new StreamWriter(Path.Combine(_folder, _fileName) + ".obj"))
|
||||
{
|
||||
streamWriter.Write("mtllib ./" + _fileName + ".mtl\n");
|
||||
|
||||
for (int index = 0; index < _meshFilter.Length; index++)
|
||||
{
|
||||
streamWriter.Write(MeshToString(_meshFilter[index], materialList));
|
||||
}
|
||||
}
|
||||
|
||||
MaterialsToFile(materialList, _folder, _fileName);
|
||||
}
|
||||
|
||||
|
||||
private static bool CreateTargetFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.Directory.CreateDirectory(targetFolder);
|
||||
}
|
||||
catch
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error!", "Failed to create target folder!", "");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Export/Export all MeshFilters in selection to separate OBJs")]
|
||||
private static void ExportSelectionToSeparate()
|
||||
{
|
||||
if (!CreateTargetFolder())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
|
||||
|
||||
if (selection.Length == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
|
||||
return;
|
||||
}
|
||||
|
||||
int exportedObjects = 0;
|
||||
|
||||
for (int index = 0; index < selection.Length; index++)
|
||||
{
|
||||
Component[] meshfilter = selection[index].GetComponentsInChildren<MeshFilter>();
|
||||
|
||||
for (int m = 0; m < meshfilter.Length; m++)
|
||||
{
|
||||
exportedObjects++;
|
||||
MeshToFile((MeshFilter)meshfilter[m], targetFolder, selection[index].name + "_" + index + "_" + m);
|
||||
}
|
||||
}
|
||||
|
||||
if (exportedObjects > 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Export/Export whole selection to single OBJ")]
|
||||
private static void ExportWholeSelectionToSingle()
|
||||
{
|
||||
if (!CreateTargetFolder())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
|
||||
|
||||
if (selection.Length == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
|
||||
return;
|
||||
}
|
||||
|
||||
int exportedObjects = 0;
|
||||
|
||||
ArrayList mfList = new ArrayList();
|
||||
|
||||
for (int index = 0; index < selection.Length; index++)
|
||||
{
|
||||
Component[] meshfilter = selection[index].GetComponentsInChildren<MeshFilter>();
|
||||
|
||||
for (int m = 0; m < meshfilter.Length; m++)
|
||||
{
|
||||
exportedObjects++;
|
||||
mfList.Add(meshfilter[m]);
|
||||
}
|
||||
}
|
||||
|
||||
if (exportedObjects > 0)
|
||||
{
|
||||
MeshFilter[] meshFilters = new MeshFilter[mfList.Count];
|
||||
|
||||
for (int index = 0; index < mfList.Count; index++)
|
||||
{
|
||||
meshFilters[index] = (MeshFilter)mfList[index];
|
||||
}
|
||||
|
||||
|
||||
string sceneName = UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().name;
|
||||
//string filename = EditorApplication.currentScene + "_" + exportedObjects;
|
||||
string filename = sceneName + "_" + exportedObjects;
|
||||
|
||||
int stripIndex = filename.LastIndexOf(Path.PathSeparator);
|
||||
|
||||
if (stripIndex >= 0)
|
||||
{
|
||||
filename = filename.Substring(stripIndex + 1).Trim();
|
||||
}
|
||||
|
||||
MeshesToFile(meshFilters, targetFolder, filename);
|
||||
|
||||
|
||||
EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects to " + filename, "");
|
||||
}
|
||||
else
|
||||
EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Export/Export each selected to single OBJ")]
|
||||
private static void ExportEachSelectionToSingle()
|
||||
{
|
||||
if (!CreateTargetFolder())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
|
||||
|
||||
if (selection.Length == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
|
||||
return;
|
||||
}
|
||||
|
||||
int exportedObjects = 0;
|
||||
|
||||
|
||||
for (int index = 0; index < selection.Length; index++)
|
||||
{
|
||||
Component[] meshfilter = selection[index].GetComponentsInChildren<MeshFilter>();
|
||||
|
||||
MeshFilter[] mf = new MeshFilter[meshfilter.Length];
|
||||
|
||||
for (int m = 0; m < meshfilter.Length; m++)
|
||||
{
|
||||
exportedObjects++;
|
||||
mf[m] = (MeshFilter)meshfilter[m];
|
||||
}
|
||||
|
||||
MeshesToFile(mf, targetFolder, selection[index].name + "_" + index);
|
||||
}
|
||||
|
||||
if (exportedObjects > 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("Window/Road Architect/Export/Exporters by Hrafnkell Freyr Hlooversson from Unity3D wiki")]
|
||||
private static void OpenLink()
|
||||
{
|
||||
Application.OpenURL("http://wiki.unity3d.com/index.php?title=ObjExporter");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd895ddcc671fce4897906afe978103f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2d67b50d8bfdea40b03587f29096fa8
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db6a849a9f10a2b4c947989f1bbf77a5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,378 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
[CustomEditor(typeof(RoadSystem))]
|
||||
public class RoadSystemEditor : Editor
|
||||
{
|
||||
#region "Vars"
|
||||
//Main target for this editor file:
|
||||
private RoadSystem roadSystem;
|
||||
|
||||
//Serialized properties:
|
||||
private SerializedProperty isAllowingUpdates;
|
||||
private SerializedProperty isTempMultithreading;
|
||||
private SerializedProperty isTempSaveMeshAssets;
|
||||
|
||||
//Editor only variables:
|
||||
private bool isInitialized;
|
||||
|
||||
// //Editor only camera variables:
|
||||
private RoadIntersection[] intersections = null;
|
||||
private int intersectionIndex = 0;
|
||||
private SplineN[] bridges = null;
|
||||
private int bridgesIndex = 0;
|
||||
private bool isBridgeInitialized = false;
|
||||
private bool isIntersectionInitialized = false;
|
||||
private bool isEditorCameraFlipped = false;
|
||||
private float cameraZoomFactor = 1f;
|
||||
private float cameraHeightOffset = 1f;
|
||||
private bool isCameraCustomRotated = false;
|
||||
private Vector3 customCameraRotation = new Vector3(0.5f, 0f, -0.5f);
|
||||
|
||||
//Editor only graphic variables:
|
||||
private Texture2D loadButtonBG = null;
|
||||
private Texture2D loadButtonBGGlow = null;
|
||||
private Texture2D warningLabelBG;
|
||||
private GUIStyle warningLabelStyle;
|
||||
private GUIStyle loadButton = null;
|
||||
#endregion
|
||||
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
roadSystem = (RoadSystem)target;
|
||||
|
||||
isAllowingUpdates = serializedObject.FindProperty("isAllowingRoadUpdates");
|
||||
isTempMultithreading = serializedObject.FindProperty("isMultithreaded");
|
||||
isTempSaveMeshAssets = serializedObject.FindProperty("isSavingMeshes");
|
||||
}
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorStyles.label.wordWrap = true;
|
||||
|
||||
if(!isInitialized)
|
||||
{
|
||||
isInitialized = true;
|
||||
InitChecks();
|
||||
}
|
||||
|
||||
//Add road button:
|
||||
RoadArchitect.EditorUtilities.DrawLine();
|
||||
if (GUILayout.Button("Add road", loadButton, GUILayout.Width(128f)))
|
||||
{
|
||||
Selection.activeObject = roadSystem.AddRoad();
|
||||
}
|
||||
RoadArchitect.EditorUtilities.DrawLine();
|
||||
|
||||
//Update all roads button:
|
||||
if (GUILayout.Button("Update all roads", EditorStyles.miniButton, GUILayout.Width(120f)))
|
||||
{
|
||||
roadSystem.UpdateAllRoads();
|
||||
}
|
||||
|
||||
isAllowingUpdates.boolValue = EditorGUILayout.Toggle("Allow Roads to Update", roadSystem.isAllowingRoadUpdates);
|
||||
|
||||
//Multi-threading input:
|
||||
isTempMultithreading.boolValue = EditorGUILayout.Toggle("Multi-threading enabled", roadSystem.isMultithreaded);
|
||||
if (isTempMultithreading.boolValue != roadSystem.isMultithreaded)
|
||||
{
|
||||
roadSystem.UpdateAllRoadsMultiThreadedOption(isTempMultithreading.boolValue);
|
||||
}
|
||||
|
||||
//Save mesh assets input:
|
||||
isTempSaveMeshAssets.boolValue = EditorGUILayout.Toggle("Save mesh assets: ", roadSystem.isSavingMeshes);
|
||||
if (isTempSaveMeshAssets.boolValue != roadSystem.isSavingMeshes)
|
||||
{
|
||||
roadSystem.UpdateAllRoadsSavingMeshesOption(isTempSaveMeshAssets.boolValue);
|
||||
}
|
||||
if (roadSystem.isSavingMeshes)
|
||||
{
|
||||
GUILayout.Label("WARNING: Saving meshes as assets is very slow and can increase road generation time by several minutes.", warningLabelStyle);
|
||||
}
|
||||
|
||||
//Online manual button:
|
||||
GUILayout.Space(4f);
|
||||
if (GUILayout.Button("Online manual", EditorStyles.miniButton, GUILayout.Width(120f)))
|
||||
{
|
||||
Application.OpenURL("https://github.com/MicroGSD/RoadArchitect/wiki");
|
||||
}
|
||||
|
||||
//Offline manual button:
|
||||
GUILayout.Space(4f);
|
||||
if (GUILayout.Button("Offline manual", EditorStyles.miniButton, GUILayout.Width(120f)))
|
||||
{
|
||||
RoadArchitect.EditorUtilities.OpenOfflineManual();
|
||||
}
|
||||
|
||||
if (roadSystem.editorPlayCamera == null)
|
||||
{
|
||||
roadSystem.EditorCameraSetSingle();
|
||||
}
|
||||
RoadArchitect.EditorUtilities.DrawLine();
|
||||
|
||||
//View intersection
|
||||
IntersectionView();
|
||||
//View bridges
|
||||
BridgeView();
|
||||
if(bridges.Length > 0 || intersections.Length > 0)
|
||||
{
|
||||
EditorGUILayout.LabelField("* Hotkeys only work when this RoadArchitectSystem object is selected and the scene view has focus", EditorStyles.miniLabel);
|
||||
}
|
||||
|
||||
|
||||
//Hotkey check:
|
||||
DoHotKeyCheck();
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(roadSystem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void InitChecks()
|
||||
{
|
||||
string basePath = RoadEditorUtility.GetBasePath();
|
||||
|
||||
RoadArchitect.EditorUtilities.LoadTexture(ref warningLabelBG, basePath + "/Editor/Icons/WarningLabelBG.png");
|
||||
RoadArchitect.EditorUtilities.LoadTexture(ref loadButtonBG, basePath + "/Editor/Icons/otherbg.png");
|
||||
RoadArchitect.EditorUtilities.LoadTexture(ref loadButtonBGGlow, basePath + "/Editor/Icons/otherbg2.png");
|
||||
|
||||
if (loadButton == null)
|
||||
{
|
||||
loadButton = new GUIStyle(GUI.skin.button);
|
||||
loadButton.contentOffset = new Vector2(0f, 1f);
|
||||
loadButton.normal.textColor = new Color(1f, 1f, 1f, 1f);
|
||||
loadButton.normal.background = loadButtonBG;
|
||||
loadButton.active.background = loadButtonBGGlow;
|
||||
loadButton.focused.background = loadButtonBGGlow;
|
||||
loadButton.hover.background = loadButtonBGGlow;
|
||||
loadButton.fixedHeight = 16f;
|
||||
loadButton.fixedWidth = 128f;
|
||||
loadButton.padding = new RectOffset(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (warningLabelStyle == null)
|
||||
{
|
||||
warningLabelStyle = new GUIStyle(GUI.skin.textArea);
|
||||
warningLabelStyle.normal.textColor = Color.red;
|
||||
warningLabelStyle.active.textColor = Color.red;
|
||||
warningLabelStyle.hover.textColor = Color.red;
|
||||
warningLabelStyle.normal.background = warningLabelBG;
|
||||
warningLabelStyle.active.background = warningLabelBG;
|
||||
warningLabelStyle.hover.background = warningLabelBG;
|
||||
warningLabelStyle.padding = new RectOffset(8, 8, 8, 8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void IntersectionView()
|
||||
{
|
||||
//View intersection
|
||||
if (!isIntersectionInitialized)
|
||||
{
|
||||
isIntersectionInitialized = true;
|
||||
intersections = roadSystem.GetComponentsInChildren<RoadIntersection>();
|
||||
}
|
||||
if (intersections.Length > 0)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("View next intersection", GUILayout.Width(150f)))
|
||||
{
|
||||
IncrementIntersection();
|
||||
}
|
||||
EditorGUILayout.LabelField("Hotkey K");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void IncrementIntersection()
|
||||
{
|
||||
if (intersections.Length > 0)
|
||||
{
|
||||
intersectionIndex += 1;
|
||||
if (intersectionIndex >= intersections.Length)
|
||||
{
|
||||
intersectionIndex = 0;
|
||||
}
|
||||
ShowIntersection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void BridgeView()
|
||||
{
|
||||
//View bridges
|
||||
if (!isBridgeInitialized)
|
||||
{
|
||||
isBridgeInitialized = true;
|
||||
SplineN[] nodes = roadSystem.transform.GetComponentsInChildren<SplineN>();
|
||||
List<SplineN> nodeList = new List<SplineN>();
|
||||
foreach (SplineN node in nodes)
|
||||
{
|
||||
if (node.isBridgeStart && node.isBridgeMatched)
|
||||
{
|
||||
nodeList.Add(node);
|
||||
}
|
||||
}
|
||||
bridges = nodeList.ToArray();
|
||||
bridgesIndex = 0;
|
||||
}
|
||||
|
||||
if (bridges.Length > 0)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("View next bridge", GUILayout.Width(150f)))
|
||||
{
|
||||
IncrementBridge();
|
||||
}
|
||||
EditorGUILayout.LabelField("Hotkey L");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (EditorApplication.isPlaying)
|
||||
{
|
||||
bool isCameraFlipped = EditorGUILayout.Toggle("Flip camera Y:", isEditorCameraFlipped);
|
||||
if (isCameraFlipped != isEditorCameraFlipped)
|
||||
{
|
||||
isEditorCameraFlipped = isCameraFlipped;
|
||||
ShowBridge();
|
||||
}
|
||||
|
||||
float changeChecker = EditorGUILayout.Slider("Zoom factor:", cameraZoomFactor, 0.02f, 10f);
|
||||
if (!RootUtils.IsApproximately(changeChecker, cameraZoomFactor, 0.001f))
|
||||
{
|
||||
cameraZoomFactor = changeChecker;
|
||||
ShowBridge();
|
||||
}
|
||||
changeChecker = EditorGUILayout.Slider("Height offset:", cameraHeightOffset, 0f, 8f);
|
||||
if (!RootUtils.IsApproximately(changeChecker, cameraHeightOffset, 0.001f))
|
||||
{
|
||||
cameraHeightOffset = changeChecker;
|
||||
ShowBridge();
|
||||
}
|
||||
|
||||
bool isCustomRotated = EditorGUILayout.Toggle("Custom camera rot:", isCameraCustomRotated);
|
||||
if (isCustomRotated != isCameraCustomRotated)
|
||||
{
|
||||
isCameraCustomRotated = isCustomRotated;
|
||||
ShowBridge();
|
||||
}
|
||||
if (isCameraCustomRotated)
|
||||
{
|
||||
Vector3 changedRotation = default(Vector3);
|
||||
changedRotation.x = EditorGUILayout.Slider("Rotation X:", customCameraRotation.x, -1f, 1f);
|
||||
changedRotation.z = EditorGUILayout.Slider("Rotation Z:", customCameraRotation.z, -1f, 1f);
|
||||
|
||||
if (changedRotation != customCameraRotation)
|
||||
{
|
||||
customCameraRotation = changedRotation;
|
||||
ShowBridge();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void IncrementBridge()
|
||||
{
|
||||
if (bridges.Length > 0)
|
||||
{
|
||||
bridgesIndex += 1;
|
||||
if (bridgesIndex >= bridges.Length)
|
||||
{
|
||||
bridgesIndex = 0;
|
||||
}
|
||||
ShowBridge();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ShowIntersection()
|
||||
{
|
||||
if (EditorApplication.isPlaying && roadSystem.editorPlayCamera != null)
|
||||
{
|
||||
roadSystem.editorPlayCamera.transform.position = intersections[intersectionIndex].transform.position + new Vector3(-40f, 20f, -40f);
|
||||
roadSystem.editorPlayCamera.transform.rotation = Quaternion.LookRotation(intersections[intersectionIndex].transform.position - (intersections[intersectionIndex].transform.position + new Vector3(-40f, 20f, -40f)));
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneView.lastActiveSceneView.pivot = intersections[intersectionIndex].transform.position;
|
||||
SceneView.lastActiveSceneView.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ShowBridge()
|
||||
{
|
||||
if (EditorApplication.isPlaying && roadSystem.editorPlayCamera != null)
|
||||
{
|
||||
Vector3 bridgePosition = ((bridges[bridgesIndex].pos - bridges[bridgesIndex].bridgeCounterpartNode.pos) * 0.5f) + bridges[bridgesIndex].bridgeCounterpartNode.pos;
|
||||
float bridgeLength = Vector3.Distance(bridges[bridgesIndex].pos, bridges[bridgesIndex].bridgeCounterpartNode.pos);
|
||||
|
||||
//Rotation:
|
||||
Vector3 cameraRotation = Vector3.Cross((bridges[bridgesIndex].pos - bridges[bridgesIndex].bridgeCounterpartNode.pos), Vector3.up);
|
||||
if (isCameraCustomRotated)
|
||||
{
|
||||
cameraRotation = customCameraRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
cameraRotation = cameraRotation.normalized;
|
||||
}
|
||||
|
||||
//Calc offset:
|
||||
Vector3 bridgeOffset = cameraRotation * (bridgeLength * 0.5f * cameraZoomFactor);
|
||||
|
||||
//Height offset:
|
||||
bridgeOffset.y = Mathf.Lerp(20f, 120f, (bridgeLength * 0.001f)) * cameraZoomFactor * cameraHeightOffset;
|
||||
|
||||
roadSystem.editorPlayCamera.transform.position = bridgePosition + bridgeOffset;
|
||||
roadSystem.editorPlayCamera.transform.rotation = Quaternion.LookRotation(bridgePosition - (bridgePosition + bridgeOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneView.lastActiveSceneView.pivot = bridges[bridgesIndex].transform.position;
|
||||
SceneView.lastActiveSceneView.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void OnSceneGUI()
|
||||
{
|
||||
DoHotKeyCheck();
|
||||
}
|
||||
|
||||
|
||||
private void DoHotKeyCheck()
|
||||
{
|
||||
Event current = Event.current;
|
||||
|
||||
if (current.type == EventType.KeyDown)
|
||||
{
|
||||
if (current.keyCode == KeyCode.K)
|
||||
{
|
||||
IncrementIntersection();
|
||||
}
|
||||
else if (current.keyCode == KeyCode.L)
|
||||
{
|
||||
IncrementBridge();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d698b63effc4b914e93d9826206bee51
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,297 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class SaveWindow : EditorWindow
|
||||
{
|
||||
public enum WindowTypeEnum
|
||||
{
|
||||
Extrusion,
|
||||
Edge,
|
||||
BridgeWizard
|
||||
};
|
||||
|
||||
|
||||
#region "Vars"
|
||||
private WindowTypeEnum windowType = WindowTypeEnum.Extrusion;
|
||||
|
||||
private Texture2D temp2D = null;
|
||||
private Texture2D temp2D2 = null;
|
||||
private string thumbString = "";
|
||||
private string desc = "";
|
||||
private string fileName = "DefaultName";
|
||||
private string displayName = "DefaultName";
|
||||
private string displayName2 = "";
|
||||
private string titleText = "";
|
||||
// private string tPath = "";
|
||||
private bool isFileExisting = false;
|
||||
private bool isBridge = false;
|
||||
|
||||
private Splination.SplinatedMeshMaker[] tSMMs = null;
|
||||
private EdgeObjects.EdgeObjectMaker[] tEOMs = null;
|
||||
private string path = "";
|
||||
private const int titleLabelHeight = 20;
|
||||
#endregion
|
||||
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Space(4f);
|
||||
EditorGUILayout.LabelField(titleText, EditorStyles.boldLabel);
|
||||
|
||||
temp2D2 = (Texture2D)EditorGUILayout.ObjectField("Square thumb (optional):", temp2D, typeof(Texture2D), false);
|
||||
if (temp2D2 != temp2D)
|
||||
{
|
||||
temp2D = temp2D2;
|
||||
thumbString = EngineIntegration.GetAssetPath(temp2D);
|
||||
}
|
||||
|
||||
if (path.Length < 5)
|
||||
{
|
||||
path = RootUtils.GetDirLibrary();
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField("Short description (optional):");
|
||||
desc = EditorGUILayout.TextArea(desc, GUILayout.Height(40f));
|
||||
displayName2 = EditorGUILayout.TextField("Display name:", displayName);
|
||||
if (string.Compare(displayName2, displayName) != 0)
|
||||
{
|
||||
displayName = displayName2;
|
||||
SanitizeFilename();
|
||||
|
||||
CheckFileExistence();
|
||||
}
|
||||
|
||||
|
||||
if (isFileExisting)
|
||||
{
|
||||
EditorGUILayout.LabelField("File exists already!", EditorStyles.miniLabel);
|
||||
}
|
||||
|
||||
if (windowType == WindowTypeEnum.Edge)
|
||||
{
|
||||
EditorGUILayout.LabelField(Path.Combine(path, "EOM" + fileName + ".rao"), EditorStyles.miniLabel);
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
EditorGUILayout.LabelField(Path.Combine(path, "ESO" + fileName + ".rao"), EditorStyles.miniLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField(Path.Combine(Path.Combine(path, "Groups"), fileName + ".rao"), EditorStyles.miniLabel);
|
||||
}
|
||||
|
||||
GUILayout.Space(4f);
|
||||
|
||||
isBridge = EditorGUILayout.Toggle("Is bridge related:", isBridge);
|
||||
GUILayout.Space(8f);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Cancel"))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
DoExtrusion();
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Edge)
|
||||
{
|
||||
DoEdgeObject();
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.BridgeWizard)
|
||||
{
|
||||
DoBridge();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
|
||||
private void DoExtrusion()
|
||||
{
|
||||
if (GUILayout.Button("Save extrusion"))
|
||||
{
|
||||
SanitizeFilename();
|
||||
tSMMs[0].isBridge = isBridge;
|
||||
tSMMs[0].thumbString = thumbString;
|
||||
tSMMs[0].desc = desc;
|
||||
tSMMs[0].displayName = displayName;
|
||||
tSMMs[0].SaveToLibrary(fileName, false);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DoEdgeObject()
|
||||
{
|
||||
if (GUILayout.Button("Save edge object"))
|
||||
{
|
||||
SanitizeFilename();
|
||||
tEOMs[0].isBridge = isBridge;
|
||||
tEOMs[0].thumbString = thumbString;
|
||||
tEOMs[0].desc = desc;
|
||||
tEOMs[0].displayName = displayName;
|
||||
tEOMs[0].SaveToLibrary(fileName, false);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DoBridge()
|
||||
{
|
||||
if (GUILayout.Button("Save group"))
|
||||
{
|
||||
SanitizeFilename();
|
||||
WizardObject WO = new WizardObject();
|
||||
WO.thumbString = thumbString;
|
||||
WO.desc = desc;
|
||||
WO.displayName = displayName;
|
||||
WO.fileName = fileName;
|
||||
WO.isBridge = isBridge;
|
||||
WO.isDefault = false;
|
||||
|
||||
RoadUtility.SaveNodeObjects(ref tSMMs, ref tEOMs, ref WO);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SanitizeFilename()
|
||||
{
|
||||
Regex regex = new Regex("[^a-zA-Z0-9 -]");
|
||||
fileName = regex.Replace(displayName, "");
|
||||
fileName = fileName.Replace(" ", "-");
|
||||
fileName = fileName.Replace("_", "-");
|
||||
}
|
||||
|
||||
|
||||
private void CheckFileExistence()
|
||||
{
|
||||
if (windowType == WindowTypeEnum.Edge)
|
||||
{
|
||||
if (File.Exists(Path.Combine(path, "EOM" + fileName + ".rao")))
|
||||
{
|
||||
isFileExisting = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isFileExisting = false;
|
||||
}
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
if (File.Exists(Path.Combine(path, "ESO" + fileName + ".rao")))
|
||||
{
|
||||
isFileExisting = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isFileExisting = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (File.Exists(Path.Combine(Path.Combine(path, "B"), fileName + ".rao")))
|
||||
{
|
||||
isFileExisting = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isFileExisting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region "Init"
|
||||
public void Initialize(ref Rect _rect, WindowTypeEnum _windowType, SplineN _node, Splination.SplinatedMeshMaker _SMM = null, EdgeObjects.EdgeObjectMaker _EOM = null)
|
||||
{
|
||||
int rectHeight = 300;
|
||||
int rectWidth = 360;
|
||||
float Rx = ((float)_rect.width / 2f) - ((float)rectWidth / 2f) + _rect.x;
|
||||
float Ry = ((float)_rect.height / 2f) - ((float)rectHeight / 2f) + _rect.y;
|
||||
|
||||
if (Rx < 0)
|
||||
{
|
||||
Rx = _rect.x;
|
||||
}
|
||||
if (Ry < 0)
|
||||
{
|
||||
Ry = _rect.y;
|
||||
}
|
||||
if (Rx > (_rect.width + _rect.x))
|
||||
{
|
||||
Rx = _rect.x;
|
||||
}
|
||||
if (Ry > (_rect.height + _rect.y))
|
||||
{
|
||||
Ry = _rect.y;
|
||||
}
|
||||
|
||||
Rect rect = new Rect(Rx, Ry, rectWidth, rectHeight);
|
||||
|
||||
if (rect.width < 300)
|
||||
{
|
||||
rect.width = 300;
|
||||
rect.x = _rect.x;
|
||||
}
|
||||
if (rect.height < 300)
|
||||
{
|
||||
rect.height = 300;
|
||||
rect.y = _rect.y;
|
||||
}
|
||||
|
||||
position = rect;
|
||||
windowType = _windowType;
|
||||
Show();
|
||||
titleContent.text = "Save";
|
||||
if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
titleText = "Save extrusion";
|
||||
tSMMs = new Splination.SplinatedMeshMaker[1];
|
||||
tSMMs[0] = _SMM;
|
||||
if (_SMM != null)
|
||||
{
|
||||
fileName = _SMM.objectName;
|
||||
displayName = fileName;
|
||||
}
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Edge)
|
||||
{
|
||||
titleText = "Save edge object";
|
||||
tEOMs = new EdgeObjects.EdgeObjectMaker[1];
|
||||
tEOMs[0] = _EOM;
|
||||
if (_EOM != null)
|
||||
{
|
||||
fileName = _EOM.objectName;
|
||||
displayName = fileName;
|
||||
}
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.BridgeWizard)
|
||||
{
|
||||
isBridge = true;
|
||||
tSMMs = _node.SplinatedObjects.ToArray();
|
||||
tEOMs = _node.EdgeObjects.ToArray();
|
||||
titleText = "Save group";
|
||||
fileName = "Group" + Random.Range(0, 10000).ToString();
|
||||
displayName = fileName;
|
||||
}
|
||||
|
||||
if (path.Length < 5)
|
||||
{
|
||||
path = RootUtils.GetDirLibrary();
|
||||
}
|
||||
|
||||
CheckFileExistence();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d228ad4f1407c8439cc06fbea6105f3
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,41 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
[CustomEditor(typeof(SplineC))]
|
||||
public class SplineCEditor : Editor
|
||||
{
|
||||
private SplineC spline;
|
||||
private int browseNode = 0;
|
||||
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
spline = (SplineC)target;
|
||||
}
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
#region NodeBrowser
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Browse to node:", EditorStyles.boldLabel);
|
||||
browseNode = EditorGUILayout.IntField(browseNode);
|
||||
if (GUILayout.Button("Browse"))
|
||||
{
|
||||
if (browseNode < spline.nodes.Count)
|
||||
{
|
||||
Selection.objects = new Object[1] { spline.nodes[browseNode] };
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d58d1757d440da4d888627fc1b0d993
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,27 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEditor;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
[CustomEditor(typeof(SplineF))]
|
||||
public class SplineFEditor : Editor
|
||||
{
|
||||
private SplineF splineF;
|
||||
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
splineF = (SplineF)target;
|
||||
}
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
//Intentionally left empty.
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1dd0c2cac8820d84ea773a41ba7cde2f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,27 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEditor;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
[CustomEditor(typeof(SplineI))]
|
||||
public class SplineIEditor : Editor
|
||||
{
|
||||
private SplineI splineI;
|
||||
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
splineI = (SplineI)target;
|
||||
}
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
//Intentionally left empty.
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ca689edb7ae35b43b50bd73fc934e96
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b18264adf12b2e4fad840ded7592b28
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,304 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
[CustomEditor(typeof(RoadTerrain))]
|
||||
public class TerrainEditor : Editor
|
||||
{
|
||||
#region "Vars"
|
||||
private RoadTerrain terrain;
|
||||
|
||||
//Serialized properties:
|
||||
SerializedProperty splatImageWidth;
|
||||
SerializedProperty splatImageHeight;
|
||||
SerializedProperty splatBackgroundColor;
|
||||
SerializedProperty splatForegroundColor;
|
||||
SerializedProperty splatWidth;
|
||||
SerializedProperty isSkippingBridges;
|
||||
SerializedProperty isSkippingTunnels;
|
||||
SerializedProperty isSplatSingleRoad;
|
||||
SerializedProperty splatSingleChoiceIndex;
|
||||
SerializedProperty roadSingleChoiceUID;
|
||||
|
||||
//Editor only variables:
|
||||
private bool isInitialized;
|
||||
private string[] roads = null;
|
||||
private string[] roadsString = null;
|
||||
private Texture refreshButtonText = null;
|
||||
private GUIStyle imageButton = null;
|
||||
private Texture2D loadButtonBG = null;
|
||||
private Texture2D loadButtonBGGlow = null;
|
||||
private GUIStyle loadButton = null;
|
||||
SplatImageResoMatchingEnum splatReso = SplatImageResoMatchingEnum.None;
|
||||
#endregion
|
||||
|
||||
|
||||
public enum SplatImageResoMatchingEnum
|
||||
{
|
||||
None,
|
||||
Match512x512,
|
||||
Match1024x1024,
|
||||
Match2048x2048,
|
||||
Match4096x4096,
|
||||
MatchHeightmapResolution,
|
||||
MatchDetailResolution,
|
||||
MatchTerrainSize
|
||||
};
|
||||
|
||||
|
||||
private static string[] TheSplatResoOptions = new string[]{
|
||||
"Select option to match resolution",
|
||||
"512 x 512",
|
||||
"1024 x 1024",
|
||||
"2048 x 2048",
|
||||
"4096 x 4096",
|
||||
"Match heightmap resolution",
|
||||
"Match detail resolution",
|
||||
"Match terrain size"
|
||||
};
|
||||
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
terrain = (RoadTerrain)target;
|
||||
|
||||
splatImageWidth = serializedObject.FindProperty("splatResoWidth");
|
||||
splatImageHeight = serializedObject.FindProperty("splatResoHeight");
|
||||
splatBackgroundColor = serializedObject.FindProperty("splatBackground");
|
||||
splatForegroundColor = serializedObject.FindProperty("splatForeground");
|
||||
splatWidth = serializedObject.FindProperty("splatWidth");
|
||||
isSkippingBridges = serializedObject.FindProperty("isSplatSkipBridges");
|
||||
isSkippingTunnels = serializedObject.FindProperty("isSplatSkipTunnels");
|
||||
isSplatSingleRoad = serializedObject.FindProperty("isSplatSingleRoad");
|
||||
splatSingleChoiceIndex = serializedObject.FindProperty("splatSingleChoiceIndex");
|
||||
roadSingleChoiceUID = serializedObject.FindProperty("roadSingleChoiceUID");
|
||||
}
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
if(!isInitialized)
|
||||
{
|
||||
isInitialized = true;
|
||||
InitNullChecks();
|
||||
}
|
||||
|
||||
RoadArchitect.EditorUtilities.DrawLine();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
//Main label:
|
||||
EditorGUILayout.LabelField("Splat map generation:", EditorStyles.boldLabel);
|
||||
//Online manual button:
|
||||
if (GUILayout.Button("Online manual", EditorStyles.miniButton, GUILayout.Width(120f)))
|
||||
{
|
||||
Application.OpenURL("https://github.com/MicroGSD/RoadArchitect/wiki");
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
GUILayout.Space(6f);
|
||||
|
||||
//Splat Resolution input:
|
||||
splatImageWidth.intValue = terrain.splatResoWidth;
|
||||
splatImageHeight.intValue = terrain.splatResoHeight;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
splatReso = (SplatImageResoMatchingEnum)EditorGUILayout.Popup("Match resolutions:", (int)splatReso, TheSplatResoOptions);
|
||||
if (GUILayout.Button(refreshButtonText, imageButton, GUILayout.Width(16f)))
|
||||
{
|
||||
splatImageWidth.intValue = 1024;
|
||||
splatImageHeight.intValue = 1024;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (splatReso != SplatImageResoMatchingEnum.None)
|
||||
{
|
||||
if (splatReso == SplatImageResoMatchingEnum.MatchHeightmapResolution)
|
||||
{
|
||||
splatImageWidth.intValue = terrain.terrain.terrainData.heightmapResolution;
|
||||
splatImageHeight.intValue = terrain.terrain.terrainData.heightmapResolution;
|
||||
}
|
||||
else if (splatReso == SplatImageResoMatchingEnum.MatchDetailResolution)
|
||||
{
|
||||
splatImageWidth.intValue = terrain.terrain.terrainData.detailResolution;
|
||||
splatImageHeight.intValue = terrain.terrain.terrainData.detailResolution;
|
||||
}
|
||||
else if (splatReso == SplatImageResoMatchingEnum.MatchTerrainSize)
|
||||
{
|
||||
splatImageWidth.intValue = (int)terrain.terrain.terrainData.size.x;
|
||||
splatImageHeight.intValue = (int)terrain.terrain.terrainData.size.z;
|
||||
}
|
||||
else if (splatReso == SplatImageResoMatchingEnum.Match512x512)
|
||||
{
|
||||
splatImageWidth.intValue = 512;
|
||||
splatImageHeight.intValue = 512;
|
||||
}
|
||||
else if (splatReso == SplatImageResoMatchingEnum.Match1024x1024)
|
||||
{
|
||||
splatImageWidth.intValue = 1024;
|
||||
splatImageHeight.intValue = 1024;
|
||||
}
|
||||
else if (splatReso == SplatImageResoMatchingEnum.Match2048x2048)
|
||||
{
|
||||
splatImageWidth.intValue = 2048;
|
||||
splatImageHeight.intValue = 2048;
|
||||
}
|
||||
else if (splatReso == SplatImageResoMatchingEnum.Match4096x4096)
|
||||
{
|
||||
splatImageWidth.intValue = 4096;
|
||||
splatImageHeight.intValue = 4096;
|
||||
}
|
||||
splatReso = SplatImageResoMatchingEnum.None;
|
||||
}
|
||||
|
||||
//Splat image width input:
|
||||
splatImageWidth.intValue = EditorGUILayout.IntField("Splat image width:", splatImageWidth.intValue);
|
||||
//Splat image height input:
|
||||
splatImageHeight.intValue = EditorGUILayout.IntField("Splat image height:", splatImageHeight.intValue);
|
||||
|
||||
|
||||
//Splat background color input:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
splatBackgroundColor.colorValue = EditorGUILayout.ColorField("Splat background:", terrain.splatBackground);
|
||||
//Default button:
|
||||
if (GUILayout.Button(refreshButtonText, imageButton, GUILayout.Width(16f)))
|
||||
{
|
||||
splatBackgroundColor.colorValue = new Color(0f, 0f, 0f, 1f);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
//Splat foreground color input:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
splatForegroundColor.colorValue = EditorGUILayout.ColorField("Splat foreground:", terrain.splatForeground);
|
||||
//Default button:
|
||||
if (GUILayout.Button(refreshButtonText, imageButton, GUILayout.Width(16f)))
|
||||
{
|
||||
splatForegroundColor.colorValue = new Color(1f, 1f, 1f, 1f);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
//Splat width (meters) input:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
splatWidth.floatValue = EditorGUILayout.Slider("Splat width (meters):", terrain.splatWidth, 0.02f, 256f);
|
||||
//Default button:
|
||||
if (GUILayout.Button(refreshButtonText, imageButton, GUILayout.Width(16f)))
|
||||
{
|
||||
splatWidth.floatValue = 30f;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
//Skip bridges:
|
||||
isSkippingBridges.boolValue = EditorGUILayout.Toggle("Skip bridges: ", terrain.isSplatSkipBridges);
|
||||
|
||||
//Skip tunnels:
|
||||
isSkippingTunnels.boolValue = EditorGUILayout.Toggle("Skip tunnels: ", terrain.isSplatSkipTunnels);
|
||||
|
||||
//Splat single road bool input:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
isSplatSingleRoad.boolValue = EditorGUILayout.Toggle("Splat a single road: ", terrain.isSplatSingleRoad);
|
||||
|
||||
//Splat single road , road input:
|
||||
if (terrain.isSplatSingleRoad)
|
||||
{
|
||||
LoadSplatSingleChoice();
|
||||
splatSingleChoiceIndex.intValue = EditorGUILayout.Popup(terrain.splatSingleChoiceIndex, roadsString, GUILayout.Width(150f));
|
||||
roadSingleChoiceUID.stringValue = roads[splatSingleChoiceIndex.intValue];
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
//Generate splatmap button:
|
||||
GUILayout.Space(8f);
|
||||
if (GUILayout.Button("Generate splatmap for this terrain"))
|
||||
{
|
||||
GenerateSplatMap();
|
||||
}
|
||||
GUILayout.Space(10f);
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
//Necessary?
|
||||
//EditorUtility.SetDirty(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void InitNullChecks()
|
||||
{
|
||||
string basePath = RoadEditorUtility.GetBasePath();
|
||||
|
||||
RoadArchitect.EditorUtilities.LoadTexture(ref refreshButtonText, basePath + "/Editor/Icons/refresh2.png");
|
||||
RoadArchitect.EditorUtilities.LoadTexture(ref loadButtonBG, basePath + "/Editor/Icons/FlexBG.png");
|
||||
RoadArchitect.EditorUtilities.LoadTexture(ref loadButtonBGGlow, basePath + "/Editor/Icons/FlexBG.png");
|
||||
|
||||
if (imageButton == null)
|
||||
{
|
||||
imageButton = new GUIStyle(GUI.skin.button);
|
||||
imageButton.contentOffset = new Vector2(0f, 0f);
|
||||
imageButton.border = new RectOffset(0, 0, 0, 0);
|
||||
imageButton.fixedHeight = 16f;
|
||||
imageButton.padding = new RectOffset(0, 0, 0, 0);
|
||||
imageButton.normal.background = null;
|
||||
}
|
||||
|
||||
if (loadButton == null)
|
||||
{
|
||||
loadButton = new GUIStyle(GUI.skin.button);
|
||||
loadButton.contentOffset = new Vector2(0f, 1f);
|
||||
loadButton.normal.textColor = new Color(1f, 1f, 1f, 1f);
|
||||
loadButton.normal.background = loadButtonBG;
|
||||
loadButton.active.background = loadButtonBGGlow;
|
||||
loadButton.focused.background = loadButtonBGGlow;
|
||||
loadButton.hover.background = loadButtonBGGlow;
|
||||
loadButton.fixedHeight = 16f;
|
||||
loadButton.padding = new RectOffset(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LoadSplatSingleChoice()
|
||||
{
|
||||
roads = null;
|
||||
roadsString = null;
|
||||
Object[] allRoads = GameObject.FindObjectsOfType<Road>();
|
||||
int roadsCount = allRoads.Length;
|
||||
roads = new string[roadsCount];
|
||||
roadsString = new string[roadsCount];
|
||||
int counter = 0;
|
||||
foreach (Road road in allRoads)
|
||||
{
|
||||
roads[counter] = road.UID;
|
||||
roadsString[counter] = road.transform.name;
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void GenerateSplatMap()
|
||||
{
|
||||
byte[] bytes = null;
|
||||
if (terrain.isSplatSingleRoad && terrain.roadSingleChoiceUID != "")
|
||||
{
|
||||
bytes = RoadUtility.MakeSplatMap(terrain.terrain, terrain.splatBackground, terrain.splatForeground, terrain.splatResoWidth, terrain.splatResoHeight, terrain.splatWidth, terrain.isSplatSkipBridges, terrain.isSplatSkipTunnels, terrain.roadSingleChoiceUID);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes = RoadUtility.MakeSplatMap(terrain.terrain, terrain.splatBackground, terrain.splatForeground, terrain.splatResoWidth, terrain.splatResoHeight, terrain.splatWidth, terrain.isSplatSkipBridges, terrain.isSplatSkipTunnels);
|
||||
}
|
||||
|
||||
if (bytes != null && bytes.Length > 3)
|
||||
{
|
||||
string path = UnityEditor.EditorUtility.SaveFilePanel("Save splat map", Application.dataPath, "Splat", "png");
|
||||
if (path != null && path.Length > 3)
|
||||
{
|
||||
System.IO.File.WriteAllBytes(path, bytes);
|
||||
}
|
||||
bytes = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a8e0720738e16f42b9c3590ab753d23
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,559 @@
|
||||
#if UNITY_EDITOR
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class Wizard : EditorWindow
|
||||
{
|
||||
public enum WindowTypeEnum
|
||||
{
|
||||
Extrusion,
|
||||
Edge,
|
||||
Groups,
|
||||
BridgeComplete,
|
||||
};
|
||||
|
||||
|
||||
private readonly string[] WindowTypeDescBridge = new string[]{
|
||||
"Extrusion items",
|
||||
"Edge objects",
|
||||
"Other groups",
|
||||
"Complete bridges",
|
||||
};
|
||||
|
||||
|
||||
private readonly string[] WindowTypeDesc = new string[]{
|
||||
"Extrusion items",
|
||||
"Edge objects",
|
||||
"Other groups"
|
||||
};
|
||||
|
||||
|
||||
#region "Vars"
|
||||
private WindowTypeEnum windowType = WindowTypeEnum.Extrusion;
|
||||
private WindowTypeEnum windowTypePrevious = WindowTypeEnum.Extrusion;
|
||||
private static string path = "";
|
||||
|
||||
private GUIStyle thumbStyle;
|
||||
private Vector2 scrollPos = new Vector2(0f, 25f);
|
||||
private SplineN thisNode = null;
|
||||
private List<WizardObject> objectList = null;
|
||||
private bool isUsingNoGUI = false;
|
||||
public Rect rect;
|
||||
#endregion
|
||||
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
DoGUI();
|
||||
}
|
||||
|
||||
|
||||
private void DoGUI()
|
||||
{
|
||||
if (isUsingNoGUI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (objectList == null)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
GUILayout.Space(4f);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
string[] options = thisNode.isBridgeStart ? WindowTypeDescBridge : WindowTypeDesc;
|
||||
if (!thisNode.isBridgeStart && windowType == WindowTypeEnum.BridgeComplete)
|
||||
{
|
||||
// Prevent category error when changing from bridge to normal node
|
||||
windowType = WindowTypeEnum.Extrusion;
|
||||
}
|
||||
windowType = (WindowTypeEnum)EditorGUILayout.Popup("Category: ", (int)windowType, options, GUILayout.Width(312f));
|
||||
|
||||
if (windowType != windowTypePrevious)
|
||||
{
|
||||
windowTypePrevious = windowType;
|
||||
InitWindow();
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField("");
|
||||
EditorGUILayout.LabelField("Single-click items to load", EditorStyles.boldLabel, GUILayout.Width(200f));
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (objectList.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int objectCount = objectList.Count;
|
||||
|
||||
int spacingWidth = 160;
|
||||
int spacingHeight = 200;
|
||||
int heightOffset = 30;
|
||||
int scrollHeightOffset = 25;
|
||||
|
||||
int xCount = 0;
|
||||
int yCount = 0;
|
||||
int yMod = Mathf.FloorToInt((float)position.width / 142f) - 1;
|
||||
|
||||
int yMax = 0;
|
||||
if (yMod == 0)
|
||||
{
|
||||
yMax = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
yMax = Mathf.CeilToInt((float)objectCount / (float)yMod);
|
||||
}
|
||||
|
||||
bool isScrolling = false;
|
||||
if ((((yMax) * spacingHeight) + 25) > position.height)
|
||||
{
|
||||
scrollPos = GUI.BeginScrollView(new Rect(0, 25, position.width - 10, position.height - 30), scrollPos, new Rect(0, 0, (yMod * spacingWidth) + 25, (((yMax) * spacingHeight) + 50)));
|
||||
isScrolling = true;
|
||||
heightOffset = scrollHeightOffset;
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
bool isClicked = false;
|
||||
for (int i = 0; i < objectCount; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
if (yMod == 0)
|
||||
{
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
yCount += 1;
|
||||
xCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i % yMod == 0)
|
||||
{
|
||||
EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); yCount += 1; xCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xCount == 0)
|
||||
{
|
||||
isClicked = DoItem((xCount * spacingWidth) + 5, (yCount * spacingHeight) + heightOffset, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
isClicked = DoItem(xCount * spacingWidth, (yCount * spacingHeight) + heightOffset, i);
|
||||
}
|
||||
|
||||
if (isClicked)
|
||||
{
|
||||
if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
Splination.SplinatedMeshMaker SMM = thisNode.AddSplinatedObject();
|
||||
SMM.SetDefaultTimes(thisNode.isEndPoint, thisNode.time, thisNode.nextTime, thisNode.idOnSpline, thisNode.spline.distance);
|
||||
SMM.LoadFromLibrary(objectList[i].fileName, objectList[i].isDefault);
|
||||
thisNode.CheckRenameSplinatedObject(SMM);
|
||||
SMM.isDefault = objectList[i].isDefault;
|
||||
SMM.Setup(true);
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Edge)
|
||||
{
|
||||
EdgeObjects.EdgeObjectMaker EOM = thisNode.AddEdgeObject();
|
||||
EOM.SetDefaultTimes(thisNode.isEndPoint, thisNode.time, thisNode.nextTime, thisNode.idOnSpline, thisNode.spline.distance);
|
||||
EOM.LoadFromLibrary(objectList[i].fileName, objectList[i].isDefault);
|
||||
thisNode.CheckRenameEdgeObject(EOM);
|
||||
EOM.isDefault = objectList[i].isDefault;
|
||||
EOM.Setup();
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Groups)
|
||||
{
|
||||
thisNode.LoadWizardObjectsFromLibrary(objectList[i].fileName, objectList[i].isDefault, objectList[i].isBridge);
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.BridgeComplete)
|
||||
{
|
||||
thisNode.LoadWizardObjectsFromLibrary(objectList[i].fileName, objectList[i].isDefault, objectList[i].isBridge);
|
||||
}
|
||||
objectList.Clear();
|
||||
objectList = null;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (isScrolling)
|
||||
{
|
||||
GUI.EndScrollView();
|
||||
}
|
||||
isUsingNoGUI = true;
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
xCount += 1;
|
||||
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (isScrolling)
|
||||
{
|
||||
GUI.EndScrollView();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool DoItem(int _x1, int _y1, int _i)
|
||||
{
|
||||
if (objectList[_i].thumb != null)
|
||||
{
|
||||
if (GUI.Button(new Rect(_x1, _y1, 132f, 132f), objectList[_i].thumb))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUI.Button(new Rect(_x1, _y1, 132f, 132f), "No image"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
GUI.Label(new Rect(_x1, _y1 + 132f, 148f, 20f), objectList[_i].displayName, EditorStyles.boldLabel);
|
||||
GUI.Label(new Rect(_x1, _y1 + 148f, 148f, 52f), objectList[_i].desc, EditorStyles.miniLabel);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#region "Init"
|
||||
/// <summary> Initializes the wizard </summary>
|
||||
public void Initialize(WindowTypeEnum _windowType, SplineN _node)
|
||||
{
|
||||
if (rect.width < 1f && rect.height < 1f)
|
||||
{
|
||||
rect.x = 275f;
|
||||
rect.y = 200f;
|
||||
rect.width = 860f;
|
||||
rect.height = 500f;
|
||||
}
|
||||
|
||||
position = rect;
|
||||
windowType = _windowType;
|
||||
windowTypePrevious = _windowType;
|
||||
thisNode = _node;
|
||||
InitWindow();
|
||||
Show();
|
||||
}
|
||||
|
||||
|
||||
private void InitWindow()
|
||||
{
|
||||
if (objectList != null)
|
||||
{
|
||||
objectList.Clear();
|
||||
objectList = null;
|
||||
}
|
||||
objectList = new List<WizardObject>();
|
||||
if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
titleContent.text = "Extrusion";
|
||||
InitObjs();
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Edge)
|
||||
{
|
||||
titleContent.text = "Edge objects";
|
||||
InitObjs();
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.BridgeComplete)
|
||||
{
|
||||
titleContent.text = "Bridges";
|
||||
InitGroups(true);
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Groups)
|
||||
{
|
||||
titleContent.text = "Groups";
|
||||
InitGroups(false);
|
||||
}
|
||||
|
||||
thumbStyle = new GUIStyle(GUI.skin.button);
|
||||
thumbStyle.contentOffset = new Vector2(0f, 0f);
|
||||
thumbStyle.border = new RectOffset(0, 0, 0, 0);
|
||||
thumbStyle.fixedHeight = 128f;
|
||||
thumbStyle.fixedWidth = 128f;
|
||||
thumbStyle.padding = new RectOffset(0, 0, 0, 0);
|
||||
thumbStyle.normal.background = null;
|
||||
thumbStyle.hover.background = null;
|
||||
thumbStyle.active.background = null;
|
||||
|
||||
EditorStyles.label.wordWrap = true;
|
||||
EditorStyles.miniLabel.wordWrap = true;
|
||||
GUI.skin.label.wordWrap = true;
|
||||
}
|
||||
|
||||
|
||||
#region "Init complete bridges"
|
||||
private void InitGroups(bool _isBridge)
|
||||
{
|
||||
string[] names = null;
|
||||
string[] paths = null;
|
||||
//Load user custom ones first:
|
||||
GetGroupListing(out names, out paths, thisNode.spline.road.laneAmount, false);
|
||||
LoadGroupObjs(ref names, ref paths, _isBridge);
|
||||
//Load RoadArchitect ones last:
|
||||
GetGroupListing(out names, out paths, thisNode.spline.road.laneAmount, true);
|
||||
LoadGroupObjs(ref names, ref paths, _isBridge);
|
||||
}
|
||||
|
||||
|
||||
private void LoadGroupObjs(ref string[] _names, ref string[] _paths, bool _isBridge)
|
||||
{
|
||||
int nameCount = _names.Length;
|
||||
string path = "";
|
||||
//string thumbString = "";
|
||||
for (int index = 0; index < nameCount; index++)
|
||||
{
|
||||
WizardObject tO = WizardObject.LoadFromLibrary(_paths[index]);
|
||||
if (tO == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (tO.isBridge != _isBridge)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
tO.thumb = EngineIntegration.LoadAssetFromPath<Texture2D>(tO.thumbString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
tO.thumb = null;
|
||||
}
|
||||
tO.fileName = _names[index];
|
||||
tO.FullPath = path;
|
||||
|
||||
objectList.Add(tO);
|
||||
}
|
||||
oListSort();
|
||||
}
|
||||
|
||||
|
||||
public static void GetGroupListing(out string[] _names, out string[] _paths, int _lanes, bool _isDefault = false)
|
||||
{
|
||||
path = RootUtils.GetDirLibrary();
|
||||
Debug.Log(path);
|
||||
|
||||
string laneText = "-2L";
|
||||
if (_lanes == 4)
|
||||
{
|
||||
laneText = "-4L";
|
||||
}
|
||||
else if (_lanes == 6)
|
||||
{
|
||||
laneText = "-6L";
|
||||
}
|
||||
|
||||
_names = null;
|
||||
_paths = null;
|
||||
DirectoryInfo info;
|
||||
if (_isDefault)
|
||||
{
|
||||
// W folder is now the Default folder
|
||||
info = new DirectoryInfo(Path.Combine(Path.Combine(path, "Groups"), "Default"));
|
||||
}
|
||||
else
|
||||
{
|
||||
info = new DirectoryInfo(Path.Combine(path, "Groups"));
|
||||
}
|
||||
|
||||
FileInfo[] fileInfo = info.GetFiles();
|
||||
int count = 0;
|
||||
foreach (FileInfo tInfo in fileInfo)
|
||||
{
|
||||
if (tInfo.Extension.ToLower().Contains("rao"))
|
||||
{
|
||||
if (!_isDefault)
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tInfo.Name.Contains(laneText))
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_names = new string[count];
|
||||
_paths = new string[count];
|
||||
count = 0;
|
||||
foreach (FileInfo tInfo in fileInfo)
|
||||
{
|
||||
if (tInfo.Extension.ToLower().Contains("rao"))
|
||||
{
|
||||
if (!_isDefault)
|
||||
{
|
||||
_names[count] = tInfo.Name.Replace(".rao", "");
|
||||
_paths[count] = tInfo.FullName;
|
||||
count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tInfo.Name.Contains(laneText))
|
||||
{
|
||||
_names[count] = tInfo.Name.Replace(".rao", "");
|
||||
_paths[count] = tInfo.FullName;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region "Init objs"
|
||||
private void InitObjs()
|
||||
{
|
||||
string[] names = null;
|
||||
string[] paths = null;
|
||||
|
||||
//Load user custom ones first:
|
||||
if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
Splination.SplinatedMeshMaker.GetLibraryFiles(out names, out paths, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
EdgeObjects.EdgeObjectMaker.GetLibraryFiles(out names, out paths, false);
|
||||
}
|
||||
LoadObjs(ref names, ref paths, false);
|
||||
|
||||
|
||||
//Load RoadArchitect ones last:
|
||||
if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
Splination.SplinatedMeshMaker.GetLibraryFiles(out names, out paths, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
EdgeObjects.EdgeObjectMaker.GetLibraryFiles(out names, out paths, true);
|
||||
}
|
||||
LoadObjs(ref names, ref paths, true);
|
||||
}
|
||||
|
||||
|
||||
private void LoadObjs(ref string[] _names, ref string[] _paths, bool _isDefault = false)
|
||||
{
|
||||
int namesCount = _names.Length;
|
||||
string path = "";
|
||||
string stringPath = "";
|
||||
string desc = "";
|
||||
string displayName = "";
|
||||
string thumbString = "";
|
||||
bool isBridge;
|
||||
|
||||
for (int i = 0; i < namesCount; i++)
|
||||
{
|
||||
isBridge = false;
|
||||
path = _paths[i];
|
||||
|
||||
if (windowType == WindowTypeEnum.Extrusion)
|
||||
{
|
||||
Splination.SplinatedMeshMaker.SplinatedMeshLibraryMaker SLM = RootUtils.LoadXML<Splination.SplinatedMeshMaker.SplinatedMeshLibraryMaker>(ref path);
|
||||
if (SLM == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
stringPath = SLM.CurrentSplinationString;
|
||||
desc = SLM.desc;
|
||||
displayName = SLM.displayName;
|
||||
thumbString = SLM.thumbString;
|
||||
isBridge = SLM.isBridge;
|
||||
}
|
||||
else if (windowType == WindowTypeEnum.Edge)
|
||||
{
|
||||
EdgeObjects.EdgeObjectMaker.EdgeObjectLibraryMaker ELM = RootUtils.LoadXML<EdgeObjects.EdgeObjectMaker.EdgeObjectLibraryMaker>(ref path);
|
||||
if (ELM == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
stringPath = ELM.edgeObjectString;
|
||||
desc = ELM.desc;
|
||||
displayName = ELM.displayName;
|
||||
thumbString = ELM.thumbString;
|
||||
isBridge = ELM.isBridge;
|
||||
}
|
||||
|
||||
//Don't continue if bridge pieces and this is not a bridge piece:
|
||||
if (windowType == WindowTypeEnum.Extrusion && isBridge)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
WizardObject tO = new WizardObject();
|
||||
#region "Image"
|
||||
try
|
||||
{
|
||||
tO.thumb = EngineIntegration.LoadAssetFromPath<Texture2D>(thumbString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
tO.thumb = null;
|
||||
}
|
||||
|
||||
|
||||
if (tO.thumb == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
GameObject xObj = EngineIntegration.LoadAssetFromPath<GameObject>(stringPath);
|
||||
tO.thumb = AssetPreview.GetAssetPreview(xObj);
|
||||
}
|
||||
catch
|
||||
{
|
||||
tO.thumb = null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
tO.displayName = displayName;
|
||||
tO.fileName = _names[i];
|
||||
tO.FullPath = path;
|
||||
tO.desc = desc;
|
||||
tO.isDefault = _isDefault;
|
||||
|
||||
objectList.Add(tO);
|
||||
}
|
||||
oListSort();
|
||||
}
|
||||
|
||||
|
||||
private void oListSort()
|
||||
{
|
||||
objectList.Sort((WizardObject object1, WizardObject object2) =>
|
||||
{
|
||||
if (object1.isDefault != object2.isDefault)
|
||||
{
|
||||
return object1.isDefault.CompareTo(object2.isDefault);
|
||||
}
|
||||
else if (object1.sortID != object2.sortID)
|
||||
{
|
||||
return object1.sortID.CompareTo(object2.sortID);
|
||||
}
|
||||
else
|
||||
{
|
||||
return object1.displayName.CompareTo(object2.displayName);
|
||||
}
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ed90bd0a0e8eca4788cb2fc0a693239
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,163 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public static class EngineIntegration
|
||||
{
|
||||
/// <summary>
|
||||
/// Unity works with forward slash so we convert
|
||||
/// If you want to implement your own Asset creation and saving you should just use finalName
|
||||
/// or what applies to the engine you use
|
||||
/// </summary>
|
||||
public static string GetUnityFilePath(string _filePath)
|
||||
{
|
||||
string path;
|
||||
path = _filePath.Replace(Path.DirectorySeparatorChar, '/');
|
||||
path = path.Replace(Path.AltDirectorySeparatorChar, '/');
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
public static void DisplayDialog(string _title, string _message, string _ok)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorUtility.DisplayDialog(_title, _message, _ok);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void RepaintAllSceneView()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.SceneView.RepaintAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void SetActiveGameObject(GameObject _object, bool _isSelected = true)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (_isSelected)
|
||||
{
|
||||
UnityEditor.Selection.activeGameObject = _object;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static GameObject InstantiatePrefab(GameObject _gameObject)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// Instantiate prefab instead of object
|
||||
return (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(_gameObject);
|
||||
#else
|
||||
// Line to instantiate the object instead of an prefab
|
||||
return GameObject.Instantiate(_gameObject);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static GameObject GetObjectFromSource(GameObject _object)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
#if UNITY_2018_2_OR_NEWER
|
||||
return UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(_object);
|
||||
#else
|
||||
return UnityEditor.PrefabUtility.GetPrefabParent(_object);
|
||||
#endif
|
||||
#else
|
||||
//TODO: Check if this is correct to do
|
||||
//Return your object
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void SetSelectedRenderState(Renderer _renderer, bool _isDrawingWireframe)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorUtility.SetSelectedRenderState(_renderer, _isDrawingWireframe ? EditorSelectedRenderState.Wireframe : EditorSelectedRenderState.Hidden);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void CreateAsset(Object _object, string _path)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.AssetDatabase.CreateAsset(_object, _path);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static string GetAssetPath<T>(T _asset) where T : UnityEngine.Object
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return UnityEditor.AssetDatabase.GetAssetPath(_asset);
|
||||
#else
|
||||
//TODO: Check if this is correct to do
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void GenerateSecondaryUVSet(Mesh _mesh)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.Unwrapping.GenerateSecondaryUVSet(_mesh);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void SaveAssets()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.AssetDatabase.SaveAssets();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void SetStaticEditorFlags(GameObject _object)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
#if UNITY_2019_2_OR_NEWER
|
||||
UnityEditor.GameObjectUtility.SetStaticEditorFlags(_object, UnityEditor.StaticEditorFlags.ContributeGI);
|
||||
#else
|
||||
UnityEditor.GameObjectUtility.SetStaticEditorFlags(_object, UnityEditor.StaticEditorFlags.LightmapStatic);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void ClearProgressBar()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorUtility.ClearProgressBar();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static T LoadAssetFromPath<T>(string _path) where T: UnityEngine.Object
|
||||
{
|
||||
//RoadEditorUtility.GetBasePath() + "/Prefabs/Signs/StopSignAllway.prefab"
|
||||
#if UNITY_EDITOR
|
||||
return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(_path);
|
||||
#else
|
||||
// Get your prefab in your runtime build
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void RegisterUndo(Object _objectToUndo, string _name)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.Undo.RegisterCreatedObjectUndo(_objectToUndo, _name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60c818167efdaca42b821a9783d2499b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 218998ce7cd01014e93378848b026646
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8844514acb92ad846b0b50ee865c0437
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57589cfe9e0ca704790ba51de619e96e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public static class Navigation
|
||||
{
|
||||
/// <summary> Resets navigation and then links each node with previous and next node </summary>
|
||||
public static void UpdateConnectedNodes()
|
||||
{
|
||||
InitResetNavigationData();
|
||||
|
||||
Object[] allSplines = GameObject.FindObjectsOfType<SplineC>();
|
||||
|
||||
//Store connected spline nodes on each other:
|
||||
SplineN node;
|
||||
foreach (SplineC spline in allSplines)
|
||||
{
|
||||
int nodeCount = spline.nodes.Count;
|
||||
for (int i = 0; i < nodeCount; i++)
|
||||
{
|
||||
node = spline.nodes[i];
|
||||
//Add next node if not last node:
|
||||
if ((i + 1) < nodeCount)
|
||||
{
|
||||
node.connectedID.Add(spline.nodes[i + 1].id);
|
||||
node.connectedNode.Add(spline.nodes[i + 1]);
|
||||
}
|
||||
//Add prev node if not first node:
|
||||
if (i > 0)
|
||||
{
|
||||
node.connectedID.Add(spline.nodes[i - 1].id);
|
||||
node.connectedNode.Add(spline.nodes[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Resets navigation data of all splines and their nodes </summary>
|
||||
public static void InitResetNavigationData()
|
||||
{
|
||||
Object[] allSplines = GameObject.FindObjectsOfType<SplineC>();
|
||||
foreach (SplineC spline in allSplines)
|
||||
{
|
||||
foreach (SplineN node in spline.nodes)
|
||||
{
|
||||
node.ResetNavigationData();
|
||||
}
|
||||
spline.ResetNavigationData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb1cc4328f3ca5c4cbca5e1b8ee3cbab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,40 @@
|
||||
namespace RoadArchitect
|
||||
{
|
||||
// RenderQueue provides ID's for Unity render queues. These can be applied to sub-shader tags,
|
||||
// but it's easier to just set material.renderQueue. Static class instead of enum because these
|
||||
// are int's, so this way client code doesn't need to use typecasting.
|
||||
//
|
||||
// From the documentation:
|
||||
// For special uses in-between queues can be used. Internally each queue is represented
|
||||
// by integer index; Background is 1000, Geometry is 2000, Transparent is 3000 and
|
||||
// Overlay is 4000.
|
||||
//
|
||||
// NOTE: Keep these in numerical order for ease of understanding. Use plurals for start of
|
||||
// a group of layers.
|
||||
public static class RenderQueue
|
||||
{
|
||||
public const int Background = 1000;
|
||||
|
||||
// Mid-ground.
|
||||
// +1, 2, 3, ... for additional layers
|
||||
public const int ParallaxLayers = Background + 100;
|
||||
|
||||
// Lines on the ground.
|
||||
public const int GroundLines = Background + 200;
|
||||
|
||||
public const int Tracks = GroundLines + 0;
|
||||
public const int Routes = GroundLines + 1;
|
||||
public const int IndicatorRings = GroundLines + 2;
|
||||
public const int Road = GroundLines + 3;
|
||||
|
||||
public const int Geometry = 2000;
|
||||
|
||||
|
||||
public const int Transparent = 3000;
|
||||
|
||||
// Lines on the screen. (Over world, but under GUI.)
|
||||
public const int ScreenLines = Transparent + 100;
|
||||
|
||||
public const int Overlay = 4000;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1235a2ff9a78ead43a82b06cef257a4b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,81 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class RigidBody : MonoBehaviour
|
||||
{
|
||||
public float minCollisionVelocity = 2f;
|
||||
//bool isForcedSleeping = false;
|
||||
private Rigidbody rigidBody;
|
||||
//bool isIgnoringRigidBody = false;
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
rigidBody = transform.GetComponent<Rigidbody>();
|
||||
DestroyImmediate(rigidBody);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
private void OnCollisionEnter(Collision collision)
|
||||
{
|
||||
if ( isIgnoringRigidBody || !isForcedSleeping )
|
||||
{
|
||||
return;
|
||||
}
|
||||
Debug.Log( collision.relativeVelocity.magnitude );
|
||||
if ( rigidbody != null )
|
||||
{
|
||||
if ( collision.relativeVelocity.magnitude <= minCollisionVelocity )
|
||||
{
|
||||
rigidbody.Sleep();
|
||||
}
|
||||
else
|
||||
{
|
||||
//RB.isKinematic = false;
|
||||
isForcedSleeping = false;
|
||||
//RB.AddForce(collision.relativeVelocity*collision.relativeVelocity.magnitude*(RB.mass*0.3f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnCollisionExit(Collision collisionInfo)
|
||||
{
|
||||
if ( isIgnoringRigidBody || !isForcedSleeping )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( isForcedSleeping && rigidbody != null )
|
||||
{
|
||||
rigidbody.Sleep();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float TimerMax = 0.1f;
|
||||
float TimerNow = 0f;
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if ( isForcedSleeping )
|
||||
{
|
||||
TimerNow += Time.deltaTime;
|
||||
if ( TimerNow > TimerMax )
|
||||
{
|
||||
if ( rigidbody != null && !rigidbody.IsSleeping() )
|
||||
{
|
||||
rigidbody.Sleep();
|
||||
}
|
||||
TimerNow = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2759b1170f87beb4ebaadcee989329b7
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b135db4f64080a94690da38eededc382
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,375 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using RoadArchitect;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect.Roads
|
||||
{
|
||||
/* Proper automation flow:
|
||||
* 1. Make sure isAllowingRoadUpdates in the scene's RoadSystem is set to FALSE.
|
||||
* 2. Create your roads programmatically via CreateRoadProgrammatically (pass it the road, and then the points in a list)
|
||||
* a. Optionally you can do it via CreateNodeProgrammatically and InsertNodeProgrammatically
|
||||
* 3. Call CreateIntersectionsProgrammaticallyForRoad for each road to create intersections automatically at intersection points.
|
||||
* 4. Set isAllowingRoadUpdates in the scene's RoadSystem is set to TRUE.
|
||||
* 5. Call RoadSystem.UpdateAllRoads();
|
||||
* 6. Call RoadSystem.UpdateAllRoads(); after step #5 completes.
|
||||
*
|
||||
* See "UnitTests.cs" for an example on automation (ignore unit test #3).
|
||||
*/
|
||||
|
||||
|
||||
public static class RoadAutomation
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this to create nodes via coding while in editor mode. Make sure isAllowingRoadUpdates is set to false in RS.roadSystem.isAllowingRoadUpdates.
|
||||
/// </summary>
|
||||
/// <param name="RS">The road system to create nodes on.</param>
|
||||
/// <param name="NodeLocation">The location of the newly created node.</param>
|
||||
public static Road CreateRoadProgrammatically(RoadSystem _RoadSys, ref List<Vector3> _positions)
|
||||
{
|
||||
GameObject roadObject = _RoadSys.AddRoad(false);
|
||||
Road road = roadObject.GetComponent<Road>();
|
||||
|
||||
int count = _positions.Count;
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
CreateNodeProgrammatically(road, _positions[index]);
|
||||
}
|
||||
|
||||
return road;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Use this to create nodes via coding while in editor mode. Make sure isAllowingRoadUpdates is set to false in RS.roadSystem.isAllowingRoadUpdates.
|
||||
/// </summary>
|
||||
/// <param name="RS">The road system to create nodes on.</param>
|
||||
/// <param name="_nodePosition">The location of the newly created node.</param>
|
||||
public static SplineN CreateNodeProgrammatically(Road _road, Vector3 _nodePosition)
|
||||
{
|
||||
int splineChildCount = _road.spline.transform.childCount;
|
||||
//Add the node
|
||||
GameObject nodeObj = new GameObject("Node" + (splineChildCount + 1).ToString());
|
||||
SplineN node = nodeObj.AddComponent<SplineN>();
|
||||
|
||||
//Set node location:
|
||||
//Make sure it doesn't try to create a node below 0 height
|
||||
if (_nodePosition.y < 0.03f)
|
||||
{
|
||||
_nodePosition.y = 0.03f;
|
||||
}
|
||||
nodeObj.transform.position = _nodePosition;
|
||||
|
||||
//Set the node's parent:
|
||||
nodeObj.transform.parent = _road.splineObject.transform;
|
||||
|
||||
//Set the idOnSpline:
|
||||
node.idOnSpline = (splineChildCount + 1);
|
||||
node.spline = _road.spline;
|
||||
|
||||
//Make sure isAllowingRoadUpdates is set to false in RS.roadSystem.isAllowingRoadUpdates
|
||||
_road.UpdateRoad();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Use this to insert nodes via coding while in editor mode. Make sure isAllowingRoadUpdates is set to false in RS.roadSystem.isAllowingRoadUpdates.
|
||||
/// </summary>
|
||||
/// <param name="_road">The road system to insert nodes in.</param>
|
||||
/// <param name="_nodePosition">The location of the newly inserted node.</param>
|
||||
public static SplineN InsertNodeProgrammatically(Road _road, Vector3 _nodePosition)
|
||||
{
|
||||
GameObject nodeObj;
|
||||
Object[] worldNodeCount = GameObject.FindObjectsOfType<SplineN>();
|
||||
nodeObj = new GameObject("Node" + worldNodeCount.Length.ToString());
|
||||
|
||||
//Set node location:
|
||||
//Make sure it doesn't try to create a node below 0 height.
|
||||
if (_nodePosition.y < 0.03f)
|
||||
{
|
||||
_nodePosition.y = 0.03f;
|
||||
}
|
||||
nodeObj.transform.position = _nodePosition;
|
||||
|
||||
//Set the node's parent:
|
||||
nodeObj.transform.parent = _road.splineObject.transform;
|
||||
|
||||
int nodesCount = _road.spline.nodes.Count;
|
||||
|
||||
//Get the closet param on spline:
|
||||
float param = _road.spline.GetClosestParam(_nodePosition, false, true);
|
||||
|
||||
bool isInsertEnded = false;
|
||||
bool isInsertZero = false;
|
||||
int start = 0;
|
||||
if (RootUtils.IsApproximately(param, 0f, 0.0001f))
|
||||
{
|
||||
isInsertZero = true;
|
||||
start = 0;
|
||||
}
|
||||
else if (RootUtils.IsApproximately(param, 1f, 0.0001f))
|
||||
{
|
||||
//Inserted at end, switch to create node instead:
|
||||
Object.DestroyImmediate(nodeObj);
|
||||
return CreateNodeProgrammatically(_road, _nodePosition);
|
||||
}
|
||||
|
||||
//Figure out where to insert the node:
|
||||
for (int index = 0; index < nodesCount; index++)
|
||||
{
|
||||
SplineN node = _road.spline.nodes[index];
|
||||
if (!isInsertZero && !isInsertEnded)
|
||||
{
|
||||
if (param > node.time)
|
||||
{
|
||||
start = node.idOnSpline + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int index = start; index < nodesCount; index++)
|
||||
{
|
||||
_road.spline.nodes[index].idOnSpline += 1;
|
||||
}
|
||||
|
||||
SplineN newNode = nodeObj.AddComponent<SplineN>();
|
||||
newNode.spline = _road.spline;
|
||||
newNode.idOnSpline = start;
|
||||
newNode.pos = _nodePosition;
|
||||
_road.spline.nodes.Insert(start, newNode);
|
||||
|
||||
//Make sure isAllowingRoadUpdates is set to false in RS.roadSystem.isAllowingRoadUpdates
|
||||
_road.UpdateRoad();
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Creates intersections where this road intersects with other roads. </summary>
|
||||
/// <param name="_road">The primary road to create intersections for.</param>
|
||||
/// <param name="_iStopType">Stop signs, traffic lights #1 (US) or traffic lights #2 (Euro). Defaults to none.</param>
|
||||
/// <param name="_roadType">Intersection type: No turn lane, left turn lane or both turn lanes. Defaults to no turn lane.</param>
|
||||
public static void CreateIntersectionsProgrammaticallyForRoad(Road _road, RoadIntersection.iStopTypeEnum _iStopType = RoadIntersection.iStopTypeEnum.None, RoadIntersection.RoadTypeEnum _roadType = RoadIntersection.RoadTypeEnum.NoTurnLane)
|
||||
{
|
||||
/*
|
||||
General logic:
|
||||
20m increments to gather collection of which roads intersect
|
||||
2m increments to find actual intersection point
|
||||
each 2m, primary road checks all intersecting array for an intersection.
|
||||
find intersection point
|
||||
if any intersections already within 75m or 100m, dont create intersection here
|
||||
check if nodes within 50m, if more than one just grab closest, and move it to intersecting point
|
||||
if no node within 50m, add
|
||||
create intersection with above two nodes
|
||||
*/
|
||||
|
||||
Object[] roadObjects = Object.FindObjectsOfType<Road>();
|
||||
|
||||
//20m increments to gather collection of which roads intersect
|
||||
List<Road> roads = new List<Road>();
|
||||
foreach (Road road in roadObjects)
|
||||
{
|
||||
if (_road != road)
|
||||
{
|
||||
float earlyDistanceCheckMeters = 10f;
|
||||
float earlyDistanceCheckThreshold = 50f;
|
||||
bool isEarlyDistanceFound = false;
|
||||
float tempRoadMod = earlyDistanceCheckMeters / _road.spline.distance;
|
||||
float roadMod = earlyDistanceCheckMeters / road.spline.distance;
|
||||
Vector3 vector1 = default(Vector3);
|
||||
Vector3 vector2 = default(Vector3);
|
||||
for (float index = 0f; index < 1.0000001f; index += tempRoadMod)
|
||||
{
|
||||
vector1 = _road.spline.GetSplineValue(index);
|
||||
for (float x = 0f; x < 1.000001f; x += roadMod)
|
||||
{
|
||||
vector2 = road.spline.GetSplineValue(x);
|
||||
if (Vector3.Distance(vector1, vector2) < earlyDistanceCheckThreshold)
|
||||
{
|
||||
if (!roads.Contains(road))
|
||||
{
|
||||
roads.Add(road);
|
||||
}
|
||||
isEarlyDistanceFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isEarlyDistanceFound)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//See if any end point nodes are on top of each other already since T might not intersect all the time.:
|
||||
List<KeyValuePair<SplineN, SplineN>> keyValuePairs = new List<KeyValuePair<SplineN, SplineN>>();
|
||||
foreach (Road road in roads)
|
||||
{
|
||||
foreach (SplineN intersectionNode1 in _road.spline.nodes)
|
||||
{
|
||||
if (intersectionNode1.isIntersection || !intersectionNode1.IsLegitimate())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (SplineN intersectionNode2 in road.spline.nodes)
|
||||
{
|
||||
if (intersectionNode2.isIntersection || !intersectionNode2.IsLegitimate())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (intersectionNode1.transform.position == intersectionNode2.transform.position)
|
||||
{
|
||||
//Only do T intersections and let the next algorithm handle the +, since T might not intersect all the time.
|
||||
if (intersectionNode1.isEndPoint || intersectionNode2.isEndPoint)
|
||||
{
|
||||
keyValuePairs.Add(new KeyValuePair<SplineN, SplineN>(intersectionNode1, intersectionNode2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (KeyValuePair<SplineN, SplineN> KVP in keyValuePairs)
|
||||
{
|
||||
// Creates fresh intersection
|
||||
//Now create the fucking intersection:
|
||||
GameObject tInter = Intersections.CreateIntersection(KVP.Key, KVP.Value);
|
||||
RoadIntersection roadIntersection = tInter.GetComponent<RoadIntersection>();
|
||||
roadIntersection.intersectionStopType = _iStopType;
|
||||
roadIntersection.roadType = _roadType;
|
||||
}
|
||||
|
||||
//Main algorithm: 2m increments to find actual intersection point:
|
||||
foreach (Road road in roads)
|
||||
{
|
||||
if (_road != road)
|
||||
{
|
||||
//Debug.Log("Checking road: " + xRoad.transform.name);
|
||||
float distanceCheckMeters = 2f;
|
||||
bool isEarlyDistanceFound = false;
|
||||
float tempRoadMod = distanceCheckMeters / _road.spline.distance;
|
||||
float roadMod = distanceCheckMeters / road.spline.distance;
|
||||
Vector3 vector = default(Vector3);
|
||||
Vector2 vector1 = default(Vector2);
|
||||
Vector2 vector2 = default(Vector2);
|
||||
Vector2 xVector1 = default(Vector2);
|
||||
Vector2 xVector2 = default(Vector2);
|
||||
Vector2 intersectPoint2D = default(Vector2);
|
||||
float i2 = 0f;
|
||||
for (float index = 0f; index < 1.0000001f; index += tempRoadMod)
|
||||
{
|
||||
i2 = (index + tempRoadMod);
|
||||
if (i2 > 1f)
|
||||
{
|
||||
i2 = 1f;
|
||||
}
|
||||
vector = _road.spline.GetSplineValue(index);
|
||||
vector1 = new Vector2(vector.x, vector.z);
|
||||
vector = _road.spline.GetSplineValue(i2);
|
||||
vector2 = new Vector2(vector.x, vector.z);
|
||||
|
||||
float x2 = 0f;
|
||||
for (float x = 0f; x < 1.000001f; x += roadMod)
|
||||
{
|
||||
x2 = (x + roadMod);
|
||||
if (x2 > 1f)
|
||||
{
|
||||
x2 = 1f;
|
||||
}
|
||||
vector = road.spline.GetSplineValue(x);
|
||||
xVector1 = new Vector2(vector.x, vector.z);
|
||||
vector = road.spline.GetSplineValue(x2);
|
||||
xVector2 = new Vector2(vector.x, vector.z);
|
||||
|
||||
//Now see if these two lines intersect:
|
||||
if (RootUtils.Intersects2D(ref vector1, ref vector2, ref xVector1, ref xVector2, out intersectPoint2D))
|
||||
{
|
||||
//Get height of intersection on primary road:
|
||||
float height = 0f;
|
||||
float param = _road.spline.GetClosestParam(new Vector3(intersectPoint2D.x, 0f, intersectPoint2D.y));
|
||||
Vector3 paramVector = _road.spline.GetSplineValue(param);
|
||||
height = paramVector.y;
|
||||
|
||||
//if any intersections already within 75m or 100m, dont create intersection here
|
||||
Object[] allInterectionObjects = Object.FindObjectsOfType<RoadIntersection>();
|
||||
foreach (RoadIntersection roadIntersection in allInterectionObjects)
|
||||
{
|
||||
if (Vector2.Distance(new Vector2(roadIntersection.transform.position.x, roadIntersection.transform.position.z), intersectPoint2D) < 100f)
|
||||
{
|
||||
goto NoIntersectionCreation;
|
||||
}
|
||||
}
|
||||
|
||||
SplineN IntersectionNode1 = null;
|
||||
SplineN IntersectionNode2 = null;
|
||||
Vector3 IntersectionPoint3D = new Vector3(intersectPoint2D.x, height, intersectPoint2D.y);
|
||||
//Debug.Log("Instersect found road: " + xRoad.transform.name + " at point: " + IntersectionPoint3D.ToString());
|
||||
|
||||
//Check primary road if any nodes are nearby and usable for intersection
|
||||
foreach (SplineN node in _road.spline.nodes)
|
||||
{
|
||||
if (node.IsLegitimate())
|
||||
{
|
||||
if (Vector2.Distance(new Vector2(node.transform.position.x, node.transform.position.z), intersectPoint2D) < 30f)
|
||||
{
|
||||
IntersectionNode1 = node;
|
||||
IntersectionNode1.transform.position = IntersectionPoint3D;
|
||||
IntersectionNode1.pos = IntersectionPoint3D;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check secondary road if any nodes are nearby and usable for intersection
|
||||
foreach (SplineN node in road.spline.nodes)
|
||||
{
|
||||
if (node.IsLegitimate())
|
||||
{
|
||||
if (Vector2.Distance(new Vector2(node.transform.position.x, node.transform.position.z), intersectPoint2D) < 30f)
|
||||
{
|
||||
IntersectionNode2 = node;
|
||||
IntersectionNode2.transform.position = IntersectionPoint3D;
|
||||
IntersectionNode2.pos = IntersectionPoint3D;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Check if any of the nodes are null. If so, need to insert node. And maybe update it.
|
||||
if (IntersectionNode1 == null)
|
||||
{
|
||||
IntersectionNode1 = InsertNodeProgrammatically(_road, IntersectionPoint3D);
|
||||
}
|
||||
if (IntersectionNode2 == null)
|
||||
{
|
||||
IntersectionNode2 = InsertNodeProgrammatically(road, IntersectionPoint3D);
|
||||
}
|
||||
|
||||
//Now create the fucking intersection:
|
||||
GameObject intersection = Intersections.CreateIntersection(IntersectionNode1, IntersectionNode2);
|
||||
RoadIntersection newRoadIntersection = intersection.GetComponent<RoadIntersection>();
|
||||
newRoadIntersection.intersectionStopType = _iStopType;
|
||||
newRoadIntersection.roadType = _roadType;
|
||||
}
|
||||
|
||||
NoIntersectionCreation:
|
||||
//Gibberish to get rid of warnings:
|
||||
int xxx = 1;
|
||||
if (xxx == 1)
|
||||
{
|
||||
xxx = 2;
|
||||
}
|
||||
}
|
||||
if (isEarlyDistanceFound)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 523098dd722dc844b97837ec5b0e2147
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect.Threading
|
||||
{
|
||||
public class RoadCalcs1 : ThreadedJob
|
||||
{
|
||||
private object handle = new object();
|
||||
private RoadConstructorBufferMaker RCS;
|
||||
private Road road;
|
||||
|
||||
|
||||
public void Setup(ref RoadConstructorBufferMaker _RCS, ref Road _road)
|
||||
{
|
||||
RCS = _RCS;
|
||||
road = _road;
|
||||
}
|
||||
|
||||
|
||||
protected override void ThreadFunction()
|
||||
{
|
||||
try
|
||||
{
|
||||
RoadCreationT.RoadJobPrelim(ref road);
|
||||
RoadCreationT.RoadJob1(ref RCS);
|
||||
}
|
||||
catch (System.Exception exception)
|
||||
{
|
||||
lock (handle)
|
||||
{
|
||||
road.isEditorError = true;
|
||||
road.exceptionError = exception;
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public RoadConstructorBufferMaker GetRCS()
|
||||
{
|
||||
RoadConstructorBufferMaker refrenceRCS;
|
||||
lock (handle)
|
||||
{
|
||||
refrenceRCS = RCS;
|
||||
}
|
||||
return refrenceRCS;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2fe1769db58d224fb3fb9a5da3ef544
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,45 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect.Threading
|
||||
{
|
||||
public class RoadCalcs2 : ThreadedJob
|
||||
{
|
||||
private object handle = new object();
|
||||
private RoadConstructorBufferMaker RCS;
|
||||
|
||||
|
||||
public void Setup(ref RoadConstructorBufferMaker _RCS)
|
||||
{
|
||||
RCS = _RCS;
|
||||
}
|
||||
|
||||
|
||||
protected override void ThreadFunction()
|
||||
{
|
||||
try
|
||||
{
|
||||
RoadCreationT.RoadJob2(ref RCS);
|
||||
}
|
||||
catch (System.Exception exception)
|
||||
{
|
||||
lock (handle)
|
||||
{
|
||||
RCS.road.isEditorError = true;
|
||||
RCS.road.exceptionError = exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public RoadConstructorBufferMaker GetRCS()
|
||||
{
|
||||
RoadConstructorBufferMaker tRCS;
|
||||
lock (handle)
|
||||
{
|
||||
tRCS = RCS;
|
||||
}
|
||||
return tRCS;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44549a173dbacf648a600d8025eb74da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4772360fe19fb6149a45b0fc83bfafa0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 638b1eacf8fad7846adcbe66c990c391
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,127 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public static class RoadEditorUtility
|
||||
{
|
||||
private static string basePath = "";
|
||||
|
||||
private static readonly string[] validFolders =
|
||||
{
|
||||
"Assets/RoadArchitect",
|
||||
"Assets/Tools/RoadArchitect",
|
||||
"Assets/Plugins/RoadArchitect",
|
||||
"Assets/Resources/RoadArchitect"
|
||||
};
|
||||
|
||||
|
||||
/// <summary> Returns the relative base of the RoadArchitect folder </summary>
|
||||
public static string GetBasePath()
|
||||
{
|
||||
if (basePath != "")
|
||||
{
|
||||
return basePath;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string path = AssetDatabase.GUIDToAssetPath("e9c7aa1199abeb64c82a4831b3c7286f");
|
||||
if (path != "" && path.Contains("RoadArchitect.asmdef"))
|
||||
{
|
||||
basePath = Path.GetDirectoryName(path);
|
||||
return basePath;
|
||||
}
|
||||
|
||||
if ('/' != Path.DirectorySeparatorChar && '/' != Path.AltDirectorySeparatorChar)
|
||||
{
|
||||
foreach (string folder in validFolders)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(Environment.CurrentDirectory, folder.Replace('/', Path.DirectorySeparatorChar))))
|
||||
{
|
||||
basePath = folder;
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string folder in validFolders)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(Environment.CurrentDirectory, folder)))
|
||||
{
|
||||
basePath = folder;
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throw new Exception("GUID of RoadArchitect.asmdef was changed. " +
|
||||
"Alternatively RoadArchitect can be placed in one of the valid folders. " +
|
||||
"You can change these suppoted folders by modifiying validFolders on top of this script");
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the relative base of the RoadArchitect folder with OS compatible directory separator </summary>
|
||||
public static string GetBasePathForIO()
|
||||
{
|
||||
string basePath = GetBasePath();
|
||||
if('/' != Path.DirectorySeparatorChar && '/' != Path.AltDirectorySeparatorChar)
|
||||
{
|
||||
return basePath.Replace('/', Path.DirectorySeparatorChar);
|
||||
}
|
||||
return basePath;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Loads _assetPath materials and applies them to _MR.sharedMaterials </summary>
|
||||
public static void SetRoadMaterial(string _assetPath, MeshRenderer _MR, string _assetPath2 = "")
|
||||
{
|
||||
Material material;
|
||||
Material material2;
|
||||
Material[] tMats;
|
||||
|
||||
material = LoadMaterial(_assetPath);
|
||||
|
||||
if (_assetPath2.Length > 0)
|
||||
{
|
||||
material2 = LoadMaterial(_assetPath2);
|
||||
|
||||
tMats = new Material[2];
|
||||
tMats[1] = material2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tMats = new Material[1];
|
||||
}
|
||||
|
||||
tMats[0] = material;
|
||||
|
||||
_MR.sharedMaterials = tMats;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the Material from _assetPath </summary>
|
||||
public static Material LoadMaterial(string _assetPath)
|
||||
{
|
||||
return EngineIntegration.LoadAssetFromPath<Material>(_assetPath);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the PhysicsMaterial from _assetPath </summary>
|
||||
public static PhysicMaterial LoadPhysicsMaterial(string _assetPath)
|
||||
{
|
||||
return EngineIntegration.LoadAssetFromPath<PhysicMaterial>(_assetPath);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35c74acc84102b648bdbb722555e1549
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c73d433cef155941a77da37fec1db37
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,124 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class RoadSystem : MonoBehaviour
|
||||
{
|
||||
#region "Vars"
|
||||
public bool isMultithreaded = true;
|
||||
public bool isSavingMeshes = false;
|
||||
public bool isAllowingRoadUpdates = true;
|
||||
|
||||
public Camera editorPlayCamera = null;
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary> Adds a new road to this RoadSystem </summary>
|
||||
public GameObject AddRoad(bool _isForceSelected = false)
|
||||
{
|
||||
Road[] roads = GetComponentsInChildren<Road>();
|
||||
int newRoadNumber = (roads.Length + 1);
|
||||
|
||||
//Road:
|
||||
GameObject roadObj = new GameObject("Road" + newRoadNumber.ToString());
|
||||
|
||||
EngineIntegration.RegisterUndo(roadObj, "Created road");
|
||||
|
||||
roadObj.transform.parent = transform;
|
||||
Road road = roadObj.AddComponent<Road>();
|
||||
|
||||
//Spline:
|
||||
GameObject splineObj = new GameObject("Spline");
|
||||
splineObj.transform.parent = road.transform;
|
||||
road.spline = splineObj.AddComponent<SplineC>();
|
||||
road.spline.splineRoot = splineObj;
|
||||
road.spline.road = road;
|
||||
road.splineObject = splineObj;
|
||||
road.roadSystem = this;
|
||||
RoadArchitect.RootUtils.SetupUniqueIdentifier(ref road.UID);
|
||||
|
||||
road.ResetTerrainHistory();
|
||||
|
||||
EngineIntegration.SetActiveGameObject(roadObj, _isForceSelected);
|
||||
|
||||
return roadObj;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Sets the editorPlayCamera to the first camera, if it is the only camera in this scene </summary>
|
||||
public void EditorCameraSetSingle()
|
||||
{
|
||||
if (editorPlayCamera == null)
|
||||
{
|
||||
Camera[] editorCams = GameObject.FindObjectsOfType<Camera>();
|
||||
if (editorCams != null && editorCams.Length == 1)
|
||||
{
|
||||
editorPlayCamera = editorCams[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Updates all roads of this RoadSystem </summary>
|
||||
public void UpdateAllRoads()
|
||||
{
|
||||
Road[] allRoadObjs = GetComponentsInChildren<Road>();
|
||||
int roadCount = allRoadObjs.Length;
|
||||
SplineC[] piggys = null;
|
||||
if (roadCount > 1)
|
||||
{
|
||||
piggys = new SplineC[roadCount];
|
||||
for (int i = 0; i < roadCount; i++)
|
||||
{
|
||||
piggys[i] = allRoadObjs[i].spline;
|
||||
}
|
||||
}
|
||||
|
||||
Road road = allRoadObjs[0];
|
||||
if (piggys != null && piggys.Length > 0)
|
||||
{
|
||||
road.PiggyBacks = piggys;
|
||||
}
|
||||
road.UpdateRoad();
|
||||
}
|
||||
|
||||
|
||||
//Workaround for submission rules:
|
||||
/// <summary> Writes isMultithreaded into roads of this system </summary>
|
||||
public void UpdateAllRoadsMultiThreadedOption(bool _isMultithreaded)
|
||||
{
|
||||
Road[] roads = GetComponentsInChildren<Road>();
|
||||
int roadsCount = roads.Length;
|
||||
Road road = null;
|
||||
for (int i = 0; i < roadsCount; i++)
|
||||
{
|
||||
road = roads[i];
|
||||
if (road != null)
|
||||
{
|
||||
road.isUsingMultithreading = _isMultithreaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Workaround for submission rules:
|
||||
/// <summary> Writes isSavingMeshes into roads of this system </summary>
|
||||
public void UpdateAllRoadsSavingMeshesOption(bool _isSavingMeshes)
|
||||
{
|
||||
Road[] roads = GetComponentsInChildren<Road>();
|
||||
int roadsCount = roads.Length;
|
||||
Road road = null;
|
||||
for (int i = 0; i < roadsCount; i++)
|
||||
{
|
||||
road = roads[i];
|
||||
if (road != null)
|
||||
{
|
||||
road.isSavingMeshes = _isSavingMeshes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95a2de324735d2b4cba8308fffecb9a8
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class RoadTerrainInfo
|
||||
{
|
||||
public Rect bounds;
|
||||
public int uID;
|
||||
public int hmWidth;
|
||||
public int hmHeight;
|
||||
public Vector3 pos;
|
||||
public Vector3 size;
|
||||
public float[,] heights;
|
||||
|
||||
|
||||
public static RoadTerrainInfo[] GetRoadTerrainInfos()
|
||||
{
|
||||
Object[] tTerrainsObj = GameObject.FindObjectsOfType<Terrain>();
|
||||
RoadTerrainInfo tInfo;
|
||||
List<RoadTerrainInfo> tInfos = new List<RoadTerrainInfo>();
|
||||
foreach (Terrain tTerrain in tTerrainsObj)
|
||||
{
|
||||
tInfo = new RoadTerrainInfo();
|
||||
tInfo.uID = tTerrain.transform.gameObject.GetComponent<RoadTerrain>().UID;
|
||||
tInfo.bounds = new Rect(tTerrain.transform.position.x, tTerrain.transform.position.z, tTerrain.terrainData.size.x, tTerrain.terrainData.size.z);
|
||||
tInfo.hmWidth = tTerrain.terrainData.heightmapResolution;
|
||||
tInfo.hmHeight = tTerrain.terrainData.heightmapResolution;
|
||||
tInfo.pos = tTerrain.transform.position;
|
||||
tInfo.size = tTerrain.terrainData.size;
|
||||
tInfo.heights = tTerrain.terrainData.GetHeights(0, 0, tInfo.hmWidth, tInfo.hmHeight);
|
||||
tInfos.Add(tInfo);
|
||||
}
|
||||
RoadTerrainInfo[] fInfos = new RoadTerrainInfo[tInfos.Count];
|
||||
int fInfosLength = fInfos.Length;
|
||||
for (int index = 0; index < fInfosLength; index++)
|
||||
{
|
||||
fInfos[index] = tInfos[index];
|
||||
}
|
||||
tInfos = null;
|
||||
return fInfos;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: faeb0a5a9cb63474286df2cedfc2dd43
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,576 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
//Generic http://www.fhwa.dot.gov/bridge/bridgerail/br053504.cfm
|
||||
public enum RailingTypeEnum { None, Generic1, Generic2, K_Rail, WBeam };
|
||||
public enum RailingSubTypeEnum { Both, Left, Right };
|
||||
public enum SignPlacementSubTypeEnum { Center, Left, Right };
|
||||
public enum CenterDividerTypeEnum { None, K_Rail, KRail_Blinds, Wire, Markers };
|
||||
public enum EndCapTypeEnum { None, WBeam, Barrels3Static, Barrels3Rigid, Barrels7Static, Barrels7Rigid };
|
||||
public enum RoadUpdateTypeEnum { Full, Intersection, Railing, CenterDivider, Bridges };
|
||||
public enum AxisTypeEnum { X, Y, Z };
|
||||
|
||||
|
||||
public static class RoadUtility
|
||||
{
|
||||
public const string FileSepString = "\n#### RoadArchitect ####\n";
|
||||
public const string FileSepStringCRLF = "\r\n#### RoadArchitect ####\r\n";
|
||||
|
||||
|
||||
/// <summary> Returns closest terrain to _vect </summary>
|
||||
public static Terrain GetTerrain(Vector3 _vect)
|
||||
{
|
||||
Terrain terrain;
|
||||
//Sphere cast 5m first. Then raycast down 1000m, then up 1000m.
|
||||
Collider[] colliders = Physics.OverlapSphere(_vect, 10f);
|
||||
if (colliders != null)
|
||||
{
|
||||
int collidersLength = colliders.Length;
|
||||
for (int index = 0; index < collidersLength; index++)
|
||||
{
|
||||
terrain = colliders[index].transform.GetComponent<Terrain>();
|
||||
if (terrain)
|
||||
{
|
||||
colliders = null;
|
||||
return terrain;
|
||||
}
|
||||
}
|
||||
colliders = null;
|
||||
}
|
||||
|
||||
RaycastHit[] hits;
|
||||
hits = Physics.RaycastAll(_vect, Vector3.down, 1000f);
|
||||
int hitsLength = 0;
|
||||
if (hits != null)
|
||||
{
|
||||
hitsLength = hits.Length;
|
||||
for (int index = 0; index < hitsLength; index++)
|
||||
{
|
||||
terrain = hits[index].collider.transform.GetComponent<Terrain>();
|
||||
if (terrain)
|
||||
{
|
||||
hits = null;
|
||||
return terrain;
|
||||
}
|
||||
}
|
||||
hits = null;
|
||||
}
|
||||
|
||||
hits = Physics.RaycastAll(_vect, Vector3.up, 1000f);
|
||||
if (hits != null)
|
||||
{
|
||||
hitsLength = hits.Length;
|
||||
for (int i = 0; i < hitsLength; i++)
|
||||
{
|
||||
terrain = hits[i].collider.transform.GetComponent<Terrain>();
|
||||
if (terrain)
|
||||
{
|
||||
hits = null;
|
||||
return terrain;
|
||||
}
|
||||
}
|
||||
hits = null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
#region "Terrain history"
|
||||
public static void ConstructRoadStoreTerrainHistory(ref Road _road)
|
||||
{
|
||||
Object[] allTerrains = GameObject.FindObjectsOfType<RoadTerrain>();
|
||||
|
||||
HashSet<int> terrainIDs = new HashSet<int>();
|
||||
foreach (RoadTerrain terrain in allTerrains)
|
||||
{
|
||||
terrainIDs.Add(terrain.UID);
|
||||
}
|
||||
|
||||
if (_road.TerrainHistory == null)
|
||||
{
|
||||
_road.TerrainHistory = new List<TerrainHistoryMaker>();
|
||||
}
|
||||
|
||||
if (_road.TerrainHistory.Count > 0)
|
||||
{
|
||||
//Delete unnecessary terrain histories:
|
||||
foreach (TerrainHistoryMaker THMaker in _road.TerrainHistory)
|
||||
{
|
||||
if (!terrainIDs.Contains(THMaker.terrainID))
|
||||
{
|
||||
THMaker.Nullify();
|
||||
_road.TerrainHistory.Remove(THMaker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TerrainHistoryMaker TH;
|
||||
RoadTerrain roadTerrain;
|
||||
foreach (Terraforming.TempTerrainData TTD in _road.EditorTTDList)
|
||||
{
|
||||
roadTerrain = null;
|
||||
//Get terrainID:
|
||||
foreach (RoadTerrain terrain in allTerrains)
|
||||
{
|
||||
if (terrain.UID == TTD.uID)
|
||||
{
|
||||
roadTerrain = terrain;
|
||||
}
|
||||
}
|
||||
|
||||
if (roadTerrain == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TH = null;
|
||||
int THCount = _road.TerrainHistory.Count;
|
||||
bool isContainingTID = false;
|
||||
for (int index = 0; index < THCount; index++)
|
||||
{
|
||||
if (_road.TerrainHistory[index].terrainID == roadTerrain.UID)
|
||||
{
|
||||
isContainingTID = true;
|
||||
TH = _road.TerrainHistory[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isContainingTID)
|
||||
{
|
||||
TerrainHistoryMaker THMaker = new TerrainHistoryMaker();
|
||||
THMaker.terrainID = roadTerrain.UID;
|
||||
_road.TerrainHistory.Add(THMaker);
|
||||
TH = THMaker;
|
||||
}
|
||||
|
||||
|
||||
if (TH == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//Heights:
|
||||
if (_road.isHeightModificationEnabled)
|
||||
{
|
||||
if (TTD.cX != null && TTD.cY != null)
|
||||
{
|
||||
TH.x1 = new int[TTD.Count];
|
||||
System.Array.Copy(TTD.cX, 0, TH.x1, 0, TTD.Count);
|
||||
TH.y1 = new int[TTD.Count];
|
||||
System.Array.Copy(TTD.cY, 0, TH.y1, 0, TTD.Count);
|
||||
TH.height = new float[TTD.Count];
|
||||
System.Array.Copy(TTD.oldH, 0, TH.height, 0, TTD.Count);
|
||||
TH.Count = TTD.Count;
|
||||
TH.heightmapResolution = TTD.TerrainMaxIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TH.x1 = null;
|
||||
TH.y1 = null;
|
||||
TH.height = null;
|
||||
TH.Count = 0;
|
||||
}
|
||||
|
||||
//Details:
|
||||
if (_road.isDetailModificationEnabled)
|
||||
{
|
||||
int TotalSize = 0;
|
||||
for (int i = 0; i < TTD.DetailLayersCount; i++)
|
||||
{
|
||||
TotalSize += TTD.detailsCount[i];
|
||||
}
|
||||
|
||||
TH.detailsX = new int[TotalSize];
|
||||
TH.detailsY = new int[TotalSize];
|
||||
TH.detailsOldValue = new int[TotalSize];
|
||||
|
||||
int RunningIndex = 0;
|
||||
int cLength;
|
||||
for (int index = 0; index < TTD.DetailLayersCount; index++)
|
||||
{
|
||||
cLength = TTD.detailsCount[index];
|
||||
if (cLength < 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
System.Array.Copy(TTD.DetailsX[index].ToArray(), 0, TH.detailsX, RunningIndex, cLength);
|
||||
System.Array.Copy(TTD.DetailsY[index].ToArray(), 0, TH.detailsY, RunningIndex, cLength);
|
||||
System.Array.Copy(TTD.OldDetailsValue[index].ToArray(), 0, TH.detailsOldValue, RunningIndex, cLength);
|
||||
RunningIndex += TTD.detailsCount[index];
|
||||
}
|
||||
|
||||
//TH.detailsX = TTD.detailsX;
|
||||
//TH.detailsY = TTD.detailsY;
|
||||
//TH.detailsOldValue = TTD.OldDetailsValue;
|
||||
TH.detailsCount = TTD.detailsCount;
|
||||
TH.detailLayersCount = TTD.DetailLayersCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
TH.detailsX = null;
|
||||
TH.detailsY = null;
|
||||
TH.detailsOldValue = null;
|
||||
TH.detailsCount = null;
|
||||
TH.detailLayersCount = 0;
|
||||
}
|
||||
|
||||
//Trees:
|
||||
if (_road.isTreeModificationEnabled)
|
||||
{
|
||||
if (TTD.TreesOld != null)
|
||||
{
|
||||
TH.MakeRATrees(ref TTD.TreesOld);
|
||||
TTD.TreesOld.Clear();
|
||||
TTD.TreesOld = null;
|
||||
TH.treesCount = TTD.treesCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TH.oldTrees = null;
|
||||
TH.treesCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Clears the terrain history of _road </summary>
|
||||
public static void ResetTerrainHistory(ref Road _road)
|
||||
{
|
||||
if (_road.TerrainHistory != null)
|
||||
{
|
||||
_road.TerrainHistory.Clear();
|
||||
_road.TerrainHistory = null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public static void SaveNodeObjects(ref Splination.SplinatedMeshMaker[] _splinatedObjects, ref EdgeObjects.EdgeObjectMaker[] _edgeObjects, ref WizardObject _wizardObj)
|
||||
{
|
||||
//Splinated objects first:
|
||||
RootUtils.CheckCreateSpecialLibraryDirs();
|
||||
string libraryPath = RootUtils.GetDirLibrary();
|
||||
string filePath = Path.Combine(Path.Combine(libraryPath, "Groups"), _wizardObj.fileName + ".rao");
|
||||
if (_wizardObj.isDefault)
|
||||
{
|
||||
filePath = Path.Combine(Path.Combine(libraryPath, "Groups"), "Default");
|
||||
filePath = Path.Combine(filePath, _wizardObj.fileName + ".rao");
|
||||
}
|
||||
StringBuilder builder = new StringBuilder(32768);
|
||||
|
||||
//Wizard object:
|
||||
builder.Append(_wizardObj.ConvertToString());
|
||||
builder.Append(FileSepString);
|
||||
|
||||
int sCount = _splinatedObjects.Length;
|
||||
Splination.SplinatedMeshMaker SMM = null;
|
||||
for (int index = 0; index < sCount; index++)
|
||||
{
|
||||
SMM = _splinatedObjects[index];
|
||||
builder.Append(SMM.ConvertToString());
|
||||
builder.Append(FileSepString);
|
||||
}
|
||||
|
||||
int eCount = _edgeObjects.Length;
|
||||
EdgeObjects.EdgeObjectMaker EOM = null;
|
||||
for (int index = 0; index < eCount; index++)
|
||||
{
|
||||
EOM = _edgeObjects[index];
|
||||
builder.Append(EOM.ConvertToString());
|
||||
builder.Append(FileSepString);
|
||||
}
|
||||
|
||||
File.WriteAllText(filePath, builder.ToString());
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Loads splinated objects for this _node </summary>
|
||||
public static void LoadNodeObjects(string _fileName, SplineN _node, bool _isDefault = false, bool _isBridge = false)
|
||||
{
|
||||
string filePath;
|
||||
RootUtils.CheckCreateSpecialLibraryDirs();
|
||||
string libraryPath = RootUtils.GetDirLibrary();
|
||||
if (_isDefault)
|
||||
{
|
||||
filePath = Path.Combine(Path.Combine(libraryPath, "Groups"), "Default");
|
||||
filePath = Path.Combine(filePath, _fileName + ".rao");
|
||||
}
|
||||
else
|
||||
{
|
||||
filePath = Path.Combine(Path.Combine(libraryPath, "Groups"), _fileName + ".rao");
|
||||
}
|
||||
|
||||
string fileData = File.ReadAllText(filePath);
|
||||
string[] seperators = new string[2];
|
||||
seperators[0] = FileSepString;
|
||||
seperators[1] = FileSepStringCRLF;
|
||||
string[] fileSplitted = fileData.Split(seperators, System.StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
Splination.SplinatedMeshMaker SMM;
|
||||
Splination.SplinatedMeshMaker.SplinatedMeshLibraryMaker SLM;
|
||||
EdgeObjects.EdgeObjectMaker EOM;
|
||||
EdgeObjects.EdgeObjectMaker.EdgeObjectLibraryMaker ELM;
|
||||
int fileSplitCount = fileSplitted.Length;
|
||||
|
||||
for (int index = 0; index < fileSplitCount; index++)
|
||||
{
|
||||
SLM = Splination.SplinatedMeshMaker.SLMFromData(fileSplitted[index]);
|
||||
if (SLM != null)
|
||||
{
|
||||
SMM = _node.AddSplinatedObject();
|
||||
SMM.LoadFromLibraryBulk(ref SLM);
|
||||
SMM.isToggled = false;
|
||||
if (_isBridge && _node.isBridgeStart && _node.isBridgeMatched && _node.bridgeCounterpartNode != null)
|
||||
{
|
||||
SMM.StartTime = _node.time;
|
||||
SMM.EndTime = _node.bridgeCounterpartNode.time;
|
||||
SMM.StartPos = _node.spline.GetSplineValue(SMM.StartTime);
|
||||
SMM.EndPos = _node.spline.GetSplineValue(SMM.EndTime);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ELM = EdgeObjects.EdgeObjectMaker.ELMFromData(fileSplitted[index]);
|
||||
if (ELM != null)
|
||||
{
|
||||
EOM = _node.AddEdgeObject();
|
||||
EOM.LoadFromLibraryBulk(ref ELM);
|
||||
EOM.isToggled = false;
|
||||
if (!EOM.isSingle && _isBridge && _node.isBridgeStart && _node.isBridgeMatched && _node.bridgeCounterpartNode != null)
|
||||
{
|
||||
EOM.startTime = _node.time;
|
||||
EOM.endTime = _node.bridgeCounterpartNode.time;
|
||||
EOM.startPos = _node.spline.GetSplineValue(EOM.startTime);
|
||||
EOM.endPos = _node.spline.GetSplineValue(EOM.endTime);
|
||||
}
|
||||
else if (EOM.isSingle && _isBridge && _node.bridgeCounterpartNode != null && _node.isBridgeStart)
|
||||
{
|
||||
float tDist = (EOM.singleOnlyBridgePercent * (_node.bridgeCounterpartNode.dist - _node.dist) + _node.dist);
|
||||
EOM.singlePosition = _node.spline.TranslateDistBasedToParam(tDist);
|
||||
EOM.startPos = _node.spline.GetSplineValue(EOM.singlePosition);
|
||||
EOM.endPos = _node.spline.GetSplineValue(EOM.singlePosition);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
_node.SetupSplinatedMeshes();
|
||||
_node.SetupEdgeObjects();
|
||||
}
|
||||
|
||||
|
||||
#region "Splat maps"
|
||||
/// <summary> Returns a splat map texture encoded as png </summary>
|
||||
public static byte[] MakeSplatMap(Terrain _terrain, Color _BG, Color _FG, int _width, int _height, float _splatWidth, bool _isSkippingBridge, bool _isSkippingTunnel, string _roadUID = "")
|
||||
{
|
||||
Texture2D texture = new Texture2D(_width, _height, TextureFormat.RGB24, false);
|
||||
|
||||
//Set background color:
|
||||
Color[] backgroundColors = new Color[_width * _height];
|
||||
int backgroundCount = backgroundColors.Length;
|
||||
for (int i = 0; i < backgroundCount; i++)
|
||||
{
|
||||
backgroundColors[i] = _BG;
|
||||
}
|
||||
texture.SetPixels(0, 0, _width, _height, backgroundColors);
|
||||
|
||||
|
||||
Object[] roadObjects;
|
||||
if (_roadUID != "")
|
||||
{
|
||||
roadObjects = new Object[1];
|
||||
Object[] roads = GameObject.FindObjectsOfType<Road>();
|
||||
foreach (Road road in roads)
|
||||
{
|
||||
if (string.CompareOrdinal(road.UID, _roadUID) == 0)
|
||||
{
|
||||
roadObjects[0] = road;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
roadObjects = GameObject.FindObjectsOfType<Road>();
|
||||
}
|
||||
|
||||
|
||||
Vector3 terrainPos = _terrain.transform.position;
|
||||
Vector3 terrainSize = _terrain.terrainData.size;
|
||||
foreach (Road road in roadObjects)
|
||||
{
|
||||
SplineC spline = road.spline;
|
||||
int tCount = spline.RoadDefKeysArray.Length;
|
||||
|
||||
Vector3 POS1 = default(Vector3);
|
||||
Vector3 POS2 = default(Vector3);
|
||||
|
||||
Vector3 tVect = default(Vector3);
|
||||
Vector3 tVect2 = default(Vector3);
|
||||
Vector3 lVect1 = default(Vector3);
|
||||
Vector3 lVect2 = default(Vector3);
|
||||
Vector3 rVect1 = default(Vector3);
|
||||
Vector3 rVect2 = default(Vector3);
|
||||
|
||||
int x1, y1;
|
||||
int[] tX = new int[4];
|
||||
int[] tY = new int[4];
|
||||
int MinX = -1;
|
||||
int MaxX = -1;
|
||||
int MinY = -1;
|
||||
int MaxY = -1;
|
||||
int xDiff = -1;
|
||||
int yDiff = -1;
|
||||
float p1 = 0f;
|
||||
float p2 = 0f;
|
||||
bool isXBad = false;
|
||||
bool isYBad = false;
|
||||
for (int i = 0; i < (tCount - 1); i++)
|
||||
{
|
||||
isXBad = false;
|
||||
isYBad = false;
|
||||
p1 = spline.TranslateInverseParamToFloat(spline.RoadDefKeysArray[i]);
|
||||
p2 = spline.TranslateInverseParamToFloat(spline.RoadDefKeysArray[i + 1]);
|
||||
|
||||
//Skip bridges:
|
||||
if (_isSkippingBridge)
|
||||
{
|
||||
if (spline.IsInBridgeTerrain(p1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Skip tunnels:
|
||||
if (_isSkippingTunnel)
|
||||
{
|
||||
if (spline.IsInTunnelTerrain(p1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
spline.GetSplineValueBoth(p1, out tVect, out POS1);
|
||||
spline.GetSplineValueBoth(p2, out tVect2, out POS2);
|
||||
lVect1 = (tVect + new Vector3(_splatWidth * -POS1.normalized.z, 0, _splatWidth * POS1.normalized.x));
|
||||
rVect1 = (tVect + new Vector3(_splatWidth * POS1.normalized.z, 0, _splatWidth * -POS1.normalized.x));
|
||||
lVect2 = (tVect2 + new Vector3(_splatWidth * -POS2.normalized.z, 0, _splatWidth * POS2.normalized.x));
|
||||
rVect2 = (tVect2 + new Vector3(_splatWidth * POS2.normalized.z, 0, _splatWidth * -POS2.normalized.x));
|
||||
|
||||
TranslateWorldVectToCustom(_width, _height, lVect1, ref terrainPos, ref terrainSize, out x1, out y1);
|
||||
tX[0] = x1;
|
||||
tY[0] = y1;
|
||||
TranslateWorldVectToCustom(_width, _height, rVect1, ref terrainPos, ref terrainSize, out x1, out y1);
|
||||
tX[1] = x1;
|
||||
tY[1] = y1;
|
||||
TranslateWorldVectToCustom(_width, _height, lVect2, ref terrainPos, ref terrainSize, out x1, out y1);
|
||||
tX[2] = x1;
|
||||
tY[2] = y1;
|
||||
TranslateWorldVectToCustom(_width, _height, rVect2, ref terrainPos, ref terrainSize, out x1, out y1);
|
||||
tX[3] = x1;
|
||||
tY[3] = y1;
|
||||
|
||||
MinX = Mathf.Min(tX);
|
||||
MaxX = Mathf.Max(tX);
|
||||
MinY = Mathf.Min(tY);
|
||||
MaxY = Mathf.Max(tY);
|
||||
|
||||
|
||||
if (MinX < 0)
|
||||
{
|
||||
MinX = 0;
|
||||
isXBad = true;
|
||||
}
|
||||
if (MaxX < 0)
|
||||
{
|
||||
MaxX = 0;
|
||||
isXBad = true;
|
||||
}
|
||||
if (MinY < 0)
|
||||
{
|
||||
MinY = 0;
|
||||
isYBad = true;
|
||||
}
|
||||
if (MaxY < 0)
|
||||
{
|
||||
MaxY = 0;
|
||||
isYBad = true;
|
||||
}
|
||||
|
||||
if (MinX > (_width - 1))
|
||||
{
|
||||
MinX = (_width - 1);
|
||||
isXBad = true;
|
||||
}
|
||||
if (MaxX > (_width - 1))
|
||||
{
|
||||
MaxX = (_width - 1);
|
||||
isXBad = true;
|
||||
}
|
||||
if (MinY > (_height - 1))
|
||||
{
|
||||
MinY = (_height - 1);
|
||||
isYBad = true;
|
||||
}
|
||||
if (MaxY > (_height - 1))
|
||||
{
|
||||
MaxY = (_height - 1);
|
||||
isYBad = true;
|
||||
}
|
||||
|
||||
if (isXBad && isYBad)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
xDiff = MaxX - MinX;
|
||||
yDiff = MaxY - MinY;
|
||||
|
||||
Color[] colors = new Color[xDiff * yDiff];
|
||||
int colorCount = colors.Length;
|
||||
for (int j = 0; j < colorCount; j++)
|
||||
{
|
||||
colors[j] = _FG;
|
||||
}
|
||||
|
||||
if (xDiff > 0 && yDiff > 0)
|
||||
{
|
||||
texture.SetPixels(MinX, MinY, xDiff, yDiff, colors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
texture.Apply();
|
||||
byte[] imageBytes = texture.EncodeToPNG();
|
||||
Object.DestroyImmediate(texture);
|
||||
return imageBytes;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Writes _vect location into _x1 and _y1 relative to the terrain on a 2D map </summary>
|
||||
private static void TranslateWorldVectToCustom(int _width, int _height, Vector3 _vect, ref Vector3 _pos, ref Vector3 _size, out int _x1, out int _y1)
|
||||
{
|
||||
//Get the normalized position of this game object relative to the terrain:
|
||||
_vect -= _pos;
|
||||
|
||||
_vect.x = _vect.x / _size.x;
|
||||
_vect.z = _vect.z / _size.z;
|
||||
|
||||
//Get the position of the terrain heightmap where this game object is:
|
||||
_x1 = (int) (_vect.x * _width);
|
||||
_y1 = (int) (_vect.z * _height);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4da03b820eb32a4e956526da9ed6735
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,432 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public static class RootUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Smooths the input parameter t.
|
||||
/// If less than k1 ir greater than k2, it uses a sin.
|
||||
/// Between k1 and k2 it uses linear interp.
|
||||
/// </summary>
|
||||
public static float Ease(float _t, float _k1, float _k2)
|
||||
{
|
||||
float f;
|
||||
float s;
|
||||
|
||||
|
||||
f = _k1 * 2 / Mathf.PI + _k2 - _k1 + (1.0f - _k2) * 2 / Mathf.PI;
|
||||
|
||||
if (_t < _k1)
|
||||
{
|
||||
s = _k1 * (2 / Mathf.PI) * (Mathf.Sin((_t / _k1) * Mathf.PI / 2 - Mathf.PI / 2) + 1);
|
||||
}
|
||||
else if (_t < _k2)
|
||||
{
|
||||
s = (2 * _k1 / Mathf.PI + _t - _k1);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = 2 * _k1 / Mathf.PI + _k2 - _k1 + ((1 - _k2) * (2 / Mathf.PI)) * Mathf.Sin(((_t - _k2) / (1.0f - _k2)) * Mathf.PI / 2);
|
||||
}
|
||||
|
||||
return (s / f);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns true if the lines intersect, otherwise false. </summary>
|
||||
/// <param name="_line1S">Line 1 start.</param>
|
||||
/// <param name="_line1E">Line 1 end.</param>
|
||||
/// <param name="_line2S">Line 2 start.</param>
|
||||
/// <param name="_line2E">Line 2 end.</param>
|
||||
/// <param name="_intersectionPoint">If the lines intersect, intersectionPoint holds the intersection point.</param>
|
||||
public static bool Intersects2D(ref Vector2 _line1S, ref Vector2 _line1E, ref Vector2 _line2S, ref Vector2 _line2E, out Vector2 _intersectionPoint)
|
||||
{
|
||||
float firstLineSlopeX, firstLineSlopeY, secondLineSlopeX, secondLineSlopeY;
|
||||
|
||||
firstLineSlopeX = _line1E.x - _line1S.x;
|
||||
firstLineSlopeY = _line1E.y - _line1S.y;
|
||||
|
||||
secondLineSlopeX = _line2E.x - _line2S.x;
|
||||
secondLineSlopeY = _line2E.y - _line2S.y;
|
||||
|
||||
float s, t;
|
||||
s = (-firstLineSlopeY * (_line1S.x - _line2S.x) + firstLineSlopeX * (_line1S.y - _line2S.y)) / (-secondLineSlopeX * firstLineSlopeY + firstLineSlopeX * secondLineSlopeY);
|
||||
t = (secondLineSlopeX * (_line1S.y - _line2S.y) - secondLineSlopeY * (_line1S.x - _line2S.x)) / (-secondLineSlopeX * firstLineSlopeY + firstLineSlopeX * secondLineSlopeY);
|
||||
|
||||
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
|
||||
{
|
||||
float intersectionPointX = _line1S.x + (t * firstLineSlopeX);
|
||||
float intersectionPointY = _line1S.y + (t * firstLineSlopeY);
|
||||
|
||||
// Collision detected
|
||||
_intersectionPoint = new Vector2(intersectionPointX, intersectionPointY);
|
||||
return true;
|
||||
}
|
||||
|
||||
_intersectionPoint = Vector2.zero;
|
||||
// No collision
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Gives the asset path to this object </summary>
|
||||
public static string GetPrefabString(GameObject _object)
|
||||
{
|
||||
string path = "";
|
||||
if (_object != null)
|
||||
{
|
||||
path = EngineIntegration.GetAssetPath(_object);
|
||||
if (path == null || path.Length < 1)
|
||||
{
|
||||
Object parentObject;
|
||||
parentObject = EngineIntegration.GetObjectFromSource(_object);
|
||||
path = EngineIntegration.GetAssetPath(parentObject);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
public static void NullifyList<T>(ref System.Collections.Generic.List<T> _list)
|
||||
{
|
||||
if (_list != null)
|
||||
{
|
||||
_list.Clear();
|
||||
_list = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region "Float comparisons"
|
||||
/// <summary> Returns true if _a - _b is smaller then _tolerance </summary>
|
||||
public static bool IsApproximately(float _a, float _b, float _tolerance = 0.01f)
|
||||
{
|
||||
return Mathf.Abs(_a - _b) < _tolerance;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region "XML"
|
||||
public static void CreateXML<T>(ref string _path, object _object)
|
||||
{
|
||||
// New function to write better xml style with utf8 encoding
|
||||
FileStream fs = new FileStream(_path, FileMode.Create);
|
||||
XmlSerializer xs = new XmlSerializer(typeof(T));
|
||||
XmlTextWriter xmlTextWriter = new XmlTextWriter(fs, Encoding.UTF8);
|
||||
xmlTextWriter.Formatting = Formatting.Indented;
|
||||
xs.Serialize(xmlTextWriter, _object);
|
||||
|
||||
fs.Close();
|
||||
}
|
||||
|
||||
|
||||
/// <summary> returns serialization of this object as string </summary>
|
||||
public static string GetString<T>(object _object)
|
||||
{
|
||||
string data = SerializeObject<T>(ref _object);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Loads object from _path as T </summary>
|
||||
public static T LoadXML<T>(ref string _path)
|
||||
{
|
||||
StreamReader reader = File.OpenText(_path);
|
||||
string _info = reader.ReadToEnd();
|
||||
reader.Close();
|
||||
T loadedObject = DeserializeObject<T>(_info);
|
||||
return loadedObject;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Deserializes _info into an object of T </summary>
|
||||
public static T LoadData<T>(ref string _info)
|
||||
{
|
||||
T loadedObject = DeserializeObject<T>(_info);
|
||||
return loadedObject;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Deletes _name from library folder </summary>
|
||||
public static void DeleteLibraryXML(string _name, bool _isExtrusion)
|
||||
{
|
||||
string path;
|
||||
if (_isExtrusion)
|
||||
{
|
||||
path = Application.dataPath + "/RoadArchitect/Library/ESO" + _name + ".rao";
|
||||
}
|
||||
else
|
||||
{
|
||||
path = Application.dataPath + "/RoadArchitect/Library/EOM" + _name + ".rao";
|
||||
}
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static string SerializeObject<T>(ref object _object)
|
||||
{
|
||||
string XmlizedString = null;
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
|
||||
XmlSerializer xs = new XmlSerializer(typeof(T));
|
||||
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
|
||||
xmlTextWriter.Formatting = Formatting.Indented;
|
||||
xs.Serialize(xmlTextWriter, _object);
|
||||
|
||||
memoryStream = (MemoryStream) xmlTextWriter.BaseStream;
|
||||
XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
|
||||
|
||||
return XmlizedString;
|
||||
}
|
||||
|
||||
|
||||
private static T DeserializeObject<T>(string _xmlString)
|
||||
{
|
||||
XmlSerializer xs = new XmlSerializer(typeof(T));
|
||||
MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(_xmlString));
|
||||
return (T)xs.Deserialize(memoryStream);
|
||||
}
|
||||
|
||||
|
||||
private static string UTF8ByteArrayToString(byte[] _characters)
|
||||
{
|
||||
UTF8Encoding encoding = new UTF8Encoding();
|
||||
string constructedString = encoding.GetString(_characters);
|
||||
return (constructedString);
|
||||
}
|
||||
|
||||
|
||||
private static byte[] StringToUTF8ByteArray(string _xmlString)
|
||||
{
|
||||
UTF8Encoding encoding = new UTF8Encoding();
|
||||
byte[] byteArray = encoding.GetBytes(_xmlString);
|
||||
return byteArray;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region "Mesh tangents"
|
||||
//Thread safe because local scope and by val params
|
||||
/// <summary> returns calculated tangents </summary>
|
||||
public static Vector4[] ProcessTangents(int[] _tris, Vector3[] _normals, Vector2[] _uvs, Vector3[] _verts)
|
||||
{
|
||||
int MVL = _verts.Length;
|
||||
if (MVL == 0)
|
||||
{
|
||||
return new Vector4[0];
|
||||
}
|
||||
// mesh.triangles.Length / 3;
|
||||
int triangleCount = _tris.Length;
|
||||
Vector3[] tan1 = new Vector3[MVL];
|
||||
Vector3[] tan2 = new Vector3[MVL];
|
||||
Vector4[] tangents = new Vector4[MVL];
|
||||
int i1, i2, i3;
|
||||
Vector3 v1, v2, v3;
|
||||
Vector2 w1, w2, w3;
|
||||
float x1, x2, y1, y2, z1, z2, s1, s2, t1, t2, r;
|
||||
Vector3 sdir, tdir;
|
||||
float div = 0f;
|
||||
|
||||
|
||||
for (int a = 0; a < triangleCount; a += 3)
|
||||
{
|
||||
i1 = _tris[a + 0];
|
||||
i2 = _tris[a + 1];
|
||||
i3 = _tris[a + 2];
|
||||
|
||||
v1 = _verts[i1];
|
||||
v2 = _verts[i2];
|
||||
v3 = _verts[i3];
|
||||
|
||||
w1 = _uvs[i1];
|
||||
w2 = _uvs[i2];
|
||||
w3 = _uvs[i3];
|
||||
|
||||
x1 = v2.x - v1.x;
|
||||
x2 = v3.x - v1.x;
|
||||
y1 = v2.y - v1.y;
|
||||
y2 = v3.y - v1.y;
|
||||
z1 = v2.z - v1.z;
|
||||
z2 = v3.z - v1.z;
|
||||
|
||||
s1 = w2.x - w1.x;
|
||||
s2 = w3.x - w1.x;
|
||||
t1 = w2.y - w1.y;
|
||||
t2 = w3.y - w1.y;
|
||||
|
||||
//r = 1.0f / (s1 * t2 - s2 * t1);
|
||||
div = (s1 * t2 - s2 * t1);
|
||||
r = div == 0.0f ? 0.0f : 1.0f / div;
|
||||
|
||||
sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
|
||||
tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
|
||||
|
||||
tan1[i1] += sdir;
|
||||
tan1[i2] += sdir;
|
||||
tan1[i3] += sdir;
|
||||
|
||||
tan2[i1] += tdir;
|
||||
tan2[i2] += tdir;
|
||||
tan2[i3] += tdir;
|
||||
}
|
||||
|
||||
|
||||
Vector3 n;
|
||||
Vector3 t;
|
||||
for (int index = 0; index < MVL; index++)
|
||||
{
|
||||
n = _normals[index];
|
||||
t = tan1[index];
|
||||
|
||||
Vector3.OrthoNormalize(ref n, ref t);
|
||||
tangents[index].x = t.x;
|
||||
tangents[index].y = t.y;
|
||||
tangents[index].z = t.z;
|
||||
tangents[index].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[index]) < 0.0f) ? -1.0f : 1.0f;
|
||||
|
||||
//tmp = (t - n * Vector3.Dot(n, t)).normalized;
|
||||
//tangents[i] = new Vector4(tmp.x, tmp.y, tmp.z);
|
||||
//tangents[i].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0f) ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
return tangents;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Get copies of mesh values and assign calculated tangents to mesh </summary>
|
||||
public static void ProcessTangents(ref Mesh _mesh)
|
||||
{
|
||||
Vector3[] vertices = _mesh.vertices;
|
||||
Vector2[] uv = _mesh.uv;
|
||||
Vector3[] normals = _mesh.normals;
|
||||
int[] triangles = _mesh.triangles;
|
||||
|
||||
_mesh.tangents = ProcessTangents(triangles, normals, uv, vertices);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region "Default directory for library etc"
|
||||
/// <summary> Gives the RoadArchitect Library </summary>
|
||||
public static string GetDirLibraryBase()
|
||||
{
|
||||
string libraryPath = Path.Combine(Path.Combine(RoadEditorUtility.GetBasePathForIO(), "Editor"), "Library");
|
||||
return libraryPath;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns relative RoadArchitect/Editor/Library with OS compatible directory separator </summary>
|
||||
public static string GetDirLibrary()
|
||||
{
|
||||
string path = GetDirLibraryBase();
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Checks for the existing Library folders or creates them if not present </summary>
|
||||
public static void CheckCreateSpecialLibraryDirs()
|
||||
{
|
||||
string libraryPath = GetDirLibraryBase();
|
||||
string directoryPath = Path.Combine(libraryPath, "EdgeObjects");
|
||||
if (!Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
directoryPath = Path.Combine(libraryPath, "ExtrudedObjects");
|
||||
if (!Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
directoryPath = Path.Combine(libraryPath, "W");
|
||||
if (!Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
// Creates Groups folder and Default folder in it if they do not exist
|
||||
directoryPath = Path.Combine(Path.Combine(libraryPath, "Groups"), "Default");
|
||||
if (!Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary> Force Unity to free memory </summary>
|
||||
public static void ForceCollection(bool _isWait = false)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
System.GC.Collect();
|
||||
if (_isWait)
|
||||
{
|
||||
System.GC.WaitForPendingFinalizers();
|
||||
}
|
||||
Resources.UnloadUnusedAssets();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Setup a unique ID </summary>
|
||||
public static void SetupUniqueIdentifier(ref string _uID)
|
||||
{
|
||||
if (_uID == null || _uID.Length < 4)
|
||||
{
|
||||
_uID = System.Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region "Profiling"
|
||||
#if UNITY_2018_1_OR_NEWER
|
||||
[Unity.Burst.BurstDiscard]
|
||||
#endif
|
||||
public static void StartProfiling(Road _road, string _profileName)
|
||||
{
|
||||
if (_road.isProfiling)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.BeginSample(_profileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_2018_1_OR_NEWER
|
||||
[Unity.Burst.BurstDiscard]
|
||||
#endif
|
||||
public static void EndProfiling(Road _road)
|
||||
{
|
||||
if (_road.isProfiling)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_2018_1_OR_NEWER
|
||||
[Unity.Burst.BurstDiscard]
|
||||
#endif
|
||||
public static void EndStartProfiling(Road _road, string _profileName)
|
||||
{
|
||||
if (_road.isProfiling)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.EndSample();
|
||||
UnityEngine.Profiling.Profiler.BeginSample(_profileName);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c55b863d2023cc945816805d2662e0fd
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52b86a2db11f88646b5056e7c8947561
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ff0e4d10039dec4cbbfa42615982b7e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22682a161c5c35d40ade7b573c38510d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,448 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class SplineF : MonoBehaviour
|
||||
{
|
||||
#region "Vars"
|
||||
public List<SplinePreviewNode> nodes = new List<SplinePreviewNode>();
|
||||
public bool isClosed = false;
|
||||
public float distance = -1f;
|
||||
public Vector3 mousePos = new Vector3(0f, 0f, 0f);
|
||||
public SplineC spline;
|
||||
// Gizmos
|
||||
public bool isDrawingGizmos = false;
|
||||
private float gizmoDrawMeters = 1f;
|
||||
#endregion
|
||||
|
||||
|
||||
#region "Gizmos"
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (!isDrawingGizmos)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (nodes == null || nodes.Count < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Debug.Log ("lawl2");
|
||||
nodes[nodes.Count - 1].pos = mousePos;
|
||||
//Debug.Log ("lawl23");
|
||||
//Setup_SplineLength();
|
||||
|
||||
float DistanceFromCam = Vector3.Distance(Camera.current.transform.position, nodes[0].pos);
|
||||
|
||||
if (DistanceFromCam > 2048)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (DistanceFromCam > 1024)
|
||||
{
|
||||
gizmoDrawMeters = 32f;
|
||||
}
|
||||
else if (DistanceFromCam > 512)
|
||||
{
|
||||
gizmoDrawMeters = 16f;
|
||||
}
|
||||
else if (DistanceFromCam > 256)
|
||||
{
|
||||
gizmoDrawMeters = 8f;
|
||||
}
|
||||
else if (DistanceFromCam > 128)
|
||||
{
|
||||
gizmoDrawMeters = 2f;
|
||||
}
|
||||
else if (DistanceFromCam > 64)
|
||||
{
|
||||
gizmoDrawMeters = 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
gizmoDrawMeters = 0.1f;
|
||||
}
|
||||
|
||||
|
||||
Vector3 prevPos = nodes[0].pos;
|
||||
Vector3 tempVect = new Vector3(0f, 0f, 0f);
|
||||
//GizmoDrawMeters = 40f;
|
||||
float step = gizmoDrawMeters / distance;
|
||||
step = Mathf.Clamp(step, 0f, 1f);
|
||||
Gizmos.color = new Color(0f, 0f, 1f, 1f);
|
||||
float index = 0f;
|
||||
Vector3 cPos;
|
||||
|
||||
float startI = 0f;
|
||||
if (nodes.Count > 4)
|
||||
{
|
||||
startI = nodes[nodes.Count - 4].time;
|
||||
}
|
||||
|
||||
prevPos = GetSplineValue(startI);
|
||||
for (index = startI; index <= 1f; index += step)
|
||||
{
|
||||
cPos = GetSplineValue(index);
|
||||
Gizmos.DrawLine(prevPos + tempVect, cPos + tempVect);
|
||||
prevPos = cPos;
|
||||
if ((index + step) > 1f)
|
||||
{
|
||||
cPos = GetSplineValue(1f);
|
||||
Gizmos.DrawLine(prevPos + tempVect, cPos + tempVect);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region "Setup"
|
||||
/// <summary> Setup relevant data for preview spline </summary>
|
||||
public void Setup(ref Vector3[] _vects)
|
||||
{
|
||||
//Create spline nodes:
|
||||
SetupNodes(ref _vects);
|
||||
|
||||
//Setup spline length, if more than 1 node:
|
||||
if (GetNodeCount() > 1)
|
||||
{
|
||||
SetupSplineLength();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Setup preview spline and preview nodes </summary>
|
||||
private void SetupNodes(ref Vector3[] _vects)
|
||||
{
|
||||
//Process nodes:
|
||||
int index = 0;
|
||||
if (nodes != null)
|
||||
{
|
||||
nodes.Clear();
|
||||
nodes = null;
|
||||
}
|
||||
|
||||
// Setup preview nodes positions
|
||||
nodes = new List<SplinePreviewNode>();
|
||||
SplinePreviewNode previewNode;
|
||||
for (index = 0; index < _vects.Length; index++)
|
||||
{
|
||||
previewNode = new SplinePreviewNode();
|
||||
previewNode.pos = _vects[index];
|
||||
nodes.Add(previewNode);
|
||||
}
|
||||
|
||||
|
||||
// Setup preview nodes rotations
|
||||
float step;
|
||||
Quaternion rot;
|
||||
step = (isClosed) ? 1f / nodes.Count : 1f / (nodes.Count - 1);
|
||||
for (index = 0; index < nodes.Count; index++)
|
||||
{
|
||||
previewNode = nodes[index];
|
||||
|
||||
rot = Quaternion.identity;
|
||||
|
||||
// if not last node
|
||||
if (index != nodes.Count - 1)
|
||||
{
|
||||
// Only rotate the node if the next node is on a different position
|
||||
if (nodes[index + 1].pos - previewNode.pos == Vector3.zero)
|
||||
{
|
||||
rot = Quaternion.identity;
|
||||
}
|
||||
else
|
||||
{
|
||||
rot = Quaternion.LookRotation(nodes[index + 1].pos - previewNode.pos, transform.up);
|
||||
}
|
||||
|
||||
//rot = Quaternion.LookRotation(mNodes[i+1].pos - previewNode.pos, transform.up);
|
||||
}
|
||||
else if (isClosed)
|
||||
{
|
||||
rot = Quaternion.LookRotation(nodes[0].pos - previewNode.pos, transform.up);
|
||||
}
|
||||
else
|
||||
{
|
||||
rot = Quaternion.identity;
|
||||
}
|
||||
|
||||
previewNode.Setup(previewNode.pos, rot, new Vector2(0, 1), step * index, "pNode");
|
||||
}
|
||||
|
||||
|
||||
previewNode = null;
|
||||
_vects = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> I think this is the general Length of the Spline? </summary>
|
||||
private void SetupSplineLength()
|
||||
{
|
||||
//First lets get the general distance, node to node:
|
||||
nodes[0].time = 0f;
|
||||
nodes[nodes.Count - 1].time = 1f;
|
||||
Vector3 tVect1 = new Vector3(0f, 0f, 0f);
|
||||
Vector3 tVect2 = new Vector3(0f, 0f, 0f);
|
||||
float mDistance = 0f;
|
||||
float mDistance_NoMod = 0f;
|
||||
for (int j = 0; j < nodes.Count; j++)
|
||||
{
|
||||
tVect2 = nodes[j].pos;
|
||||
if (j > 0)
|
||||
{
|
||||
mDistance += Vector3.Distance(tVect1, tVect2);
|
||||
}
|
||||
tVect1 = tVect2;
|
||||
}
|
||||
mDistance_NoMod = mDistance;
|
||||
mDistance = mDistance * 1.05f;
|
||||
// float step = 0.1f / mDistance;
|
||||
|
||||
//Get a slightly more accurate portrayal of the time:
|
||||
float tTime = 0f;
|
||||
for (int j = 0; j < (nodes.Count - 1); j++)
|
||||
{
|
||||
tVect2 = nodes[j].pos;
|
||||
if (j > 0)
|
||||
{
|
||||
tTime += (Vector3.Distance(tVect1, tVect2) / mDistance_NoMod);
|
||||
nodes[j].time = tTime;
|
||||
}
|
||||
tVect1 = tVect2;
|
||||
}
|
||||
distance = mDistance_NoMod;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
if (RootUtils.IsApproximately(_value, 0f, 0.00001f))
|
||||
{
|
||||
if (_isTangent)
|
||||
{
|
||||
return mNodes[0].tangent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mNodes[0].pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (RootUtils.IsApproximately(_value, 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;
|
||||
}
|
||||
// }
|
||||
|
||||
float param = (_value - nodes[idx].time) / (nodes[idx + 1].time - nodes[idx].time);
|
||||
param = RoadArchitect.RootUtils.Ease(param, nodes[idx].easeIO.x, nodes[idx].easeIO.y);
|
||||
return GetHermiteInternal(idx, param, _isTangent);
|
||||
}
|
||||
|
||||
|
||||
public Vector3 GetSplineValueSkipOpt(float _value, bool _isTangent = false)
|
||||
{
|
||||
int i;
|
||||
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 (i = 1; i < nodes.Count; i++)
|
||||
{
|
||||
if (i == nodes.Count - 1)
|
||||
{
|
||||
idx = i - 1;
|
||||
break;
|
||||
}
|
||||
if (nodes[i].time >= _value)
|
||||
{
|
||||
idx = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx < 0)
|
||||
{
|
||||
idx = 0;
|
||||
}
|
||||
// }
|
||||
|
||||
float param = (_value - nodes[idx].time) / (nodes[idx + 1].time - nodes[idx].time);
|
||||
param = RoadArchitect.RootUtils.Ease(param, nodes[idx].easeIO.x, nodes[idx].easeIO.y);
|
||||
return GetHermiteInternal(idx, param, _isTangent);
|
||||
}
|
||||
|
||||
|
||||
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:
|
||||
// 0.5 equivale a catmull-rom
|
||||
tension = 0.5f;
|
||||
|
||||
//Tangents:
|
||||
P2 = (P1 - P2) * tension;
|
||||
P3 = (P3 - P0) * tension;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
return BL0 * P0 + BL1 * P1 + BL2 * P2 + BL3 * P3;
|
||||
}
|
||||
|
||||
|
||||
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 (isClosed)
|
||||
{
|
||||
return (NGITI % nodes.Count + nodes.Count) % nodes.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Mathf.Clamp(NGITI, 0, nodes.Count - 1);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public int GetNodeCount()
|
||||
{
|
||||
return nodes.Count;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f26cfb64f42345341bfd8b8be2374f73
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,451 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class SplineI : MonoBehaviour
|
||||
{
|
||||
#region "Vars"
|
||||
public List<SplinePreviewNode> nodes = new List<SplinePreviewNode>();
|
||||
public bool isClosed = false;
|
||||
public float distance = -1f;
|
||||
public Vector3 mousePos = new Vector3(0f, 0f, 0f);
|
||||
public SplineC spline;
|
||||
public SplinePreviewNode actionNode;
|
||||
// Gizmos
|
||||
public bool isDrawingGizmos = false;
|
||||
private float gizmoDrawMeters = 1f;
|
||||
#endregion
|
||||
|
||||
|
||||
public void DetermineInsertNodes()
|
||||
{
|
||||
int nodesCount = spline.nodes.Count;
|
||||
if (nodesCount < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SplinePreviewNode previewNode;
|
||||
SplineN xNode;
|
||||
nodes.Clear();
|
||||
float tParam = spline.GetClosestParam(mousePos, false, true);
|
||||
bool bEndInsert = false;
|
||||
bool bZeroInsert = false;
|
||||
int iStart = 0;
|
||||
if (Mathf.Approximately(tParam, 0f))
|
||||
{
|
||||
bZeroInsert = true;
|
||||
iStart = 0;
|
||||
}
|
||||
else if (Mathf.Approximately(tParam, 1f))
|
||||
{
|
||||
bEndInsert = true;
|
||||
}
|
||||
|
||||
for (int index = 0; index < nodesCount; index++)
|
||||
{
|
||||
xNode = spline.nodes[index];
|
||||
previewNode = new SplinePreviewNode();
|
||||
previewNode.pos = xNode.pos;
|
||||
previewNode.idOnSpline = xNode.idOnSpline;
|
||||
previewNode.time = xNode.time;
|
||||
if (!bZeroInsert && !bEndInsert)
|
||||
{
|
||||
if (tParam > previewNode.time)
|
||||
{
|
||||
iStart = previewNode.idOnSpline + 1;
|
||||
}
|
||||
}
|
||||
nodes.Add(previewNode);
|
||||
}
|
||||
|
||||
nodes.Sort(CompareListByName);
|
||||
int cCount = nodes.Count;
|
||||
if (bEndInsert)
|
||||
{
|
||||
iStart = cCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = iStart; index < cCount; index++)
|
||||
{
|
||||
nodes[index].idOnSpline += 1;
|
||||
}
|
||||
}
|
||||
previewNode = new SplinePreviewNode();
|
||||
previewNode.pos = mousePos;
|
||||
previewNode.idOnSpline = iStart;
|
||||
previewNode.time = tParam;
|
||||
previewNode.isPreviewNode = true;
|
||||
if (bEndInsert)
|
||||
{
|
||||
nodes.Add(previewNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodes.Insert(iStart, previewNode);
|
||||
}
|
||||
SetupSplineLength();
|
||||
actionNode = previewNode;
|
||||
}
|
||||
|
||||
|
||||
private int CompareListByName(SplinePreviewNode _i1, SplinePreviewNode _i2)
|
||||
{
|
||||
return _i1.idOnSpline.CompareTo(_i2.idOnSpline);
|
||||
}
|
||||
|
||||
|
||||
public void UpdateActionNode()
|
||||
{
|
||||
if (actionNode != null)
|
||||
{
|
||||
actionNode.pos = mousePos;
|
||||
}
|
||||
DetermineInsertNodes();
|
||||
}
|
||||
|
||||
|
||||
#region "Gizmos"
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (!isDrawingGizmos)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (actionNode == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (nodes == null || nodes.Count < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//Debug.Log ("lawl2");
|
||||
//mNodes[mNodes.Count-1].pos = MousePos;
|
||||
//Debug.Log ("lawl23");
|
||||
//Setup_SplineLength();
|
||||
|
||||
float DistanceFromCam = Vector3.Distance(Camera.current.transform.position, nodes[0].pos);
|
||||
|
||||
if (DistanceFromCam > 2048)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (DistanceFromCam > 1024)
|
||||
{
|
||||
gizmoDrawMeters = 32f;
|
||||
}
|
||||
else if (DistanceFromCam > 512)
|
||||
{
|
||||
gizmoDrawMeters = 16f;
|
||||
}
|
||||
else if (DistanceFromCam > 256)
|
||||
{
|
||||
gizmoDrawMeters = 8f;
|
||||
}
|
||||
else if (DistanceFromCam > 128)
|
||||
{
|
||||
gizmoDrawMeters = 2f;
|
||||
}
|
||||
else if (DistanceFromCam > 64)
|
||||
{
|
||||
gizmoDrawMeters = 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
gizmoDrawMeters = 0.1f;
|
||||
}
|
||||
gizmoDrawMeters = 0.1f;
|
||||
|
||||
Vector3 prevPos = nodes[0].pos;
|
||||
Vector3 tempVect = new Vector3(0f, 0f, 0f);
|
||||
//GizmoDrawMeters = 40f;
|
||||
float step = gizmoDrawMeters / distance;
|
||||
step = Mathf.Clamp(step, 0f, 1f);
|
||||
Gizmos.color = new Color(0f, 0f, 1f, 1f);
|
||||
float i = 0f;
|
||||
Vector3 cPos;
|
||||
|
||||
float startI = 0f;
|
||||
float endI = 1f;
|
||||
if (actionNode.idOnSpline > 3)
|
||||
{
|
||||
startI = nodes[actionNode.idOnSpline - 2].time;
|
||||
}
|
||||
if (actionNode.idOnSpline < (nodes.Count - 3))
|
||||
{
|
||||
endI = nodes[actionNode.idOnSpline + 2].time;
|
||||
}
|
||||
|
||||
prevPos = GetSplineValue(startI);
|
||||
for (i = startI; i <= endI; i += step)
|
||||
{
|
||||
cPos = GetSplineValue(i);
|
||||
Gizmos.DrawLine(prevPos + tempVect, cPos + tempVect);
|
||||
prevPos = cPos;
|
||||
if ((i + step) > 1f)
|
||||
{
|
||||
cPos = GetSplineValue(1f);
|
||||
Gizmos.DrawLine(prevPos + tempVect, cPos + tempVect);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region "Setup"
|
||||
private void SetupSplineLength()
|
||||
{
|
||||
//First lets get the general distance, node to node:
|
||||
nodes[0].time = 0f;
|
||||
nodes[nodes.Count - 1].time = 1f;
|
||||
Vector3 tVect1 = new Vector3(0f, 0f, 0f);
|
||||
Vector3 tVect2 = new Vector3(0f, 0f, 0f);
|
||||
float mDistance = 0f;
|
||||
float mDistance_NoMod = 0f;
|
||||
for (int j = 0; j < nodes.Count; j++)
|
||||
{
|
||||
tVect2 = nodes[j].pos;
|
||||
if (j > 0)
|
||||
{
|
||||
mDistance += Vector3.Distance(tVect1, tVect2);
|
||||
}
|
||||
tVect1 = tVect2;
|
||||
}
|
||||
mDistance_NoMod = mDistance;
|
||||
mDistance = mDistance * 1.05f;
|
||||
//float step = 0.1f / mDistance;
|
||||
|
||||
//Get a slightly more accurate portrayal of the time:
|
||||
float tTime = 0f;
|
||||
for (int j = 0; j < (nodes.Count - 1); j++)
|
||||
{
|
||||
tVect2 = nodes[j].pos;
|
||||
if (j > 0)
|
||||
{
|
||||
tTime += (Vector3.Distance(tVect1, tVect2) / mDistance_NoMod);
|
||||
nodes[j].time = tTime;
|
||||
}
|
||||
tVect1 = tVect2;
|
||||
}
|
||||
distance = mDistance_NoMod;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region "Hermite math"
|
||||
/// <summary> Gets the spline value. </summary>
|
||||
/// <param name='f'> 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;
|
||||
}
|
||||
|
||||
|
||||
// FH / Do note, that someone outcommented stuff here, for whatever Reason, but why?
|
||||
/*
|
||||
if (RootUtils.IsApproximately(_value, 0f, 0.00001f))
|
||||
{
|
||||
if (_isTangent)
|
||||
{
|
||||
return mNodes[0].tangent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mNodes[0].pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (RootUtils.IsApproximately(_value, 1f, 0.00001f) || _value > 1f)
|
||||
{
|
||||
if (_isTangent)
|
||||
{
|
||||
return mNodes[mNodes.Count - 1].tangent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mNodes[mNodes.Count - 1].pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*/
|
||||
// FH / Do note, that someone outcommented stuff here, for whatever Reason, but why?
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
//} // FH/ Do note, that someone outcommented stuff here, for whatever Reason, but why?
|
||||
|
||||
float param = (_value - nodes[idx].time) / (nodes[idx + 1].time - nodes[idx].time);
|
||||
param = RoadArchitect.RootUtils.Ease(param, nodes[idx].easeIO.x, nodes[idx].easeIO.y);
|
||||
return GetHermiteInternal(idx, param, _isTangent);
|
||||
}
|
||||
|
||||
|
||||
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(_value,0f,0.00001f)){
|
||||
// if(_isTangent){
|
||||
// return mNodes[0].tangent;
|
||||
// }else{
|
||||
// return mNodes[0].pos;
|
||||
// }
|
||||
// }else
|
||||
// if(RootUtils.IsApproximately(_value,1f,0.00001f) || _value > 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;
|
||||
}
|
||||
// }
|
||||
|
||||
float param = (_value - nodes[idx].time) / (nodes[idx + 1].time - nodes[idx].time);
|
||||
param = RoadArchitect.RootUtils.Ease(param, nodes[idx].easeIO.x, nodes[idx].easeIO.y);
|
||||
return GetHermiteInternal(idx, param, _isTangent);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
//Necessary for 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:
|
||||
// 0.5 equivale a catmull-rom
|
||||
tension = 0.5f;
|
||||
|
||||
//Tangents:
|
||||
P2 = (P1 - P2) * tension;
|
||||
P3 = (P3 - P0) * tension;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
return BL0 * P0 + BL1 * P1 + BL2 * P2 + BL3 * P3;
|
||||
}
|
||||
|
||||
|
||||
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 (isClosed)
|
||||
{
|
||||
return (NGITI % nodes.Count + nodes.Count) % nodes.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Mathf.Clamp(NGITI, 0, nodes.Count - 1);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public int GetNodeCount()
|
||||
{
|
||||
return nodes.Count;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab2ad73e5dbe50c4c8414b8efd183edf
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16ac6886edfc6d44cb1aa025800086f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,40 @@
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public class SplinePreviewNode
|
||||
{
|
||||
#region "Vars"
|
||||
public Vector3 pos;
|
||||
public Quaternion rot;
|
||||
public Vector3 tangent;
|
||||
public Vector2 easeIO;
|
||||
public float time = 0f;
|
||||
public float oldTime = 0f;
|
||||
|
||||
public string name = "Node-1";
|
||||
|
||||
public bool isTempTime = false;
|
||||
|
||||
public float tempSegmentTime = 0f;
|
||||
public float tempMinDistance = 5000f;
|
||||
public float tempMinTime = 0f;
|
||||
|
||||
public int idOnSpline;
|
||||
public SplineC spline;
|
||||
public bool isDestroyed = false;
|
||||
public bool isPreviewNode = false;
|
||||
#endregion
|
||||
|
||||
|
||||
public void Setup(Vector3 _p, Quaternion _q, Vector2 _io, float _time, string _name)
|
||||
{
|
||||
pos = _p;
|
||||
rot = _q;
|
||||
easeIO = _io;
|
||||
time = _time;
|
||||
name = _name;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eee159a043b9b974799d1c4661130432
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f0c7b62fc158f445ab043d2f4c65757
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,106 @@
|
||||
#region "Imports"
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
#endregion
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
public class RoadTerrain : MonoBehaviour
|
||||
{
|
||||
#region "Vars"
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private int uID = -1;
|
||||
|
||||
public int UID { get { return uID; } }
|
||||
|
||||
[HideInInspector]
|
||||
public Terrain terrain;
|
||||
|
||||
//Splat map:
|
||||
public int splatResoWidth = 1024;
|
||||
public int splatResoHeight = 1024;
|
||||
public Color splatBackground = new Color(0f, 0f, 0f, 1f);
|
||||
public Color splatForeground = new Color(1f, 1f, 1f, 1f);
|
||||
public float splatWidth = 30f;
|
||||
public bool isSplatSkipBridges = false;
|
||||
public bool isSplatSkipTunnels = false;
|
||||
public bool isSplatSingleRoad = false;
|
||||
public int splatSingleChoiceIndex = 0;
|
||||
public string roadSingleChoiceUID = "";
|
||||
#endregion
|
||||
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
CheckID();
|
||||
if (!terrain)
|
||||
{
|
||||
terrain = transform.gameObject.GetComponent<Terrain>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Check for unique id and assign terrain </summary>
|
||||
public void CheckID()
|
||||
{
|
||||
if (uID < 0)
|
||||
{
|
||||
uID = GetNewID();
|
||||
}
|
||||
if (!terrain)
|
||||
{
|
||||
terrain = transform.gameObject.GetComponent<Terrain>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Return new id preventing terrain id duplication </summary>
|
||||
private int GetNewID()
|
||||
{
|
||||
Object[] allTerrainObjs = GameObject.FindObjectsOfType<RoadTerrain>();
|
||||
List<int> allIDS = new List<int>(allTerrainObjs.Length);
|
||||
foreach (RoadTerrain Terrain in allTerrainObjs)
|
||||
{
|
||||
if (Terrain.UID > 0)
|
||||
{
|
||||
allIDS.Add(Terrain.UID);
|
||||
}
|
||||
}
|
||||
|
||||
bool isNotDone = true;
|
||||
int spamChecker = 0;
|
||||
int spamCheckerMax = allIDS.Count + 64;
|
||||
int random;
|
||||
while (isNotDone)
|
||||
{
|
||||
if (spamChecker > spamCheckerMax)
|
||||
{
|
||||
Debug.LogError("Failed to generate terrainID");
|
||||
break;
|
||||
}
|
||||
random = Random.Range(1, 2000000000);
|
||||
if (!allIDS.Contains(random))
|
||||
{
|
||||
isNotDone = false;
|
||||
return random;
|
||||
}
|
||||
spamChecker += 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
CheckID();
|
||||
if (!terrain)
|
||||
{
|
||||
terrain = transform.gameObject.GetComponent<Terrain>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81fe195562332d04ead2688a86c7325b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,645 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RoadArchitect
|
||||
{
|
||||
public static class Terraforming
|
||||
{
|
||||
public class TempTerrainData
|
||||
{
|
||||
public int HM;
|
||||
public int HMHeight;
|
||||
public float[,] heights;
|
||||
public bool[,] tHeights;
|
||||
|
||||
public float HMRatio;
|
||||
public float MetersPerHM = 0f;
|
||||
|
||||
//Heights:
|
||||
public ushort[] cX;
|
||||
public ushort[] cY;
|
||||
public float[] cH;
|
||||
public float[] oldH;
|
||||
public int Count = 0;
|
||||
public int TerrainMaxIndex;
|
||||
|
||||
//Details:
|
||||
public int DetailLayersCount;
|
||||
|
||||
public List<ushort> MainDetailsX;
|
||||
public List<ushort> MainDetailsY;
|
||||
|
||||
public List<List<ushort>> DetailsX;
|
||||
public List<List<ushort>> DetailsY;
|
||||
public List<List<ushort>> OldDetailsValue;
|
||||
//public Dictionary<int,int[,]> DetailValues;
|
||||
public int[] detailsCount;
|
||||
public float DetailToHeightRatio;
|
||||
|
||||
|
||||
//public Dictionary<int,bool[,]> DetailHasProcessed;
|
||||
|
||||
public HashSet<int> DetailHasProcessed;
|
||||
|
||||
//public List<List<bool>> OldDetailsValue;
|
||||
|
||||
public int DetailMaxIndex;
|
||||
public HashSet<int> DetailLayersSkip;
|
||||
|
||||
//Trees
|
||||
public List<TreeInstance> TreesCurrent;
|
||||
public List<TreeInstance> TreesOld;
|
||||
public int treesCount;
|
||||
public int TreeSize;
|
||||
|
||||
public Vector3 TerrainSize;
|
||||
public Vector3 TerrainPos;
|
||||
public int uID;
|
||||
|
||||
|
||||
public void Nullify()
|
||||
{
|
||||
heights = null;
|
||||
tHeights = null;
|
||||
cX = null;
|
||||
cY = null;
|
||||
cH = null;
|
||||
oldH = null;
|
||||
//DetailsX = null;
|
||||
//DetailsY = null;
|
||||
//DetailValues = null;
|
||||
OldDetailsValue = null;
|
||||
//DetailsI = null;
|
||||
TreesCurrent = null;
|
||||
TreesOld = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Checks all terrains and adds RoadTerrain if necessary </summary>
|
||||
private static void CheckAllTerrains()
|
||||
{
|
||||
Object[] allTerrains = GameObject.FindObjectsOfType<Terrain>();
|
||||
RoadTerrain TID;
|
||||
GameObject terrainObj;
|
||||
foreach (Terrain terrain in allTerrains)
|
||||
{
|
||||
terrainObj = terrain.transform.gameObject;
|
||||
TID = terrainObj.GetComponent<RoadTerrain>();
|
||||
if (TID == null)
|
||||
{
|
||||
TID = terrainObj.AddComponent<RoadTerrain>();
|
||||
}
|
||||
TID.CheckID();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Checks if every Terrain uses a RoadTerrain script and set it to 0 on y </summary>
|
||||
public static void CheckAllTerrainsHeight0()
|
||||
{
|
||||
CheckAllTerrains();
|
||||
Object[] allTerrains = GameObject.FindObjectsOfType<Terrain>();
|
||||
foreach (Terrain terrain in allTerrains)
|
||||
{
|
||||
if (!RootUtils.IsApproximately(terrain.transform.position.y, 0f, 0.0001f))
|
||||
{
|
||||
Vector3 tVect = terrain.transform.position;
|
||||
tVect.y = 0f;
|
||||
terrain.transform.position = tVect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Stores terrain infos and starts terrain calculations </summary>
|
||||
public static void ProcessRoadTerrainHook1(SplineC _spline, Road _road, bool _isMultithreaded = true)
|
||||
{
|
||||
ProcessRoadTerrainHook1Do(ref _spline, ref _road, _isMultithreaded);
|
||||
}
|
||||
|
||||
|
||||
private static void ProcessRoadTerrainHook1Do(ref SplineC _spline, ref Road _road, bool _isMultithreaded)
|
||||
{
|
||||
RootUtils.StartProfiling(_road, "ProcessRoadTerrainHook1");
|
||||
//First lets make sure all terrains have a RoadTerrain script:
|
||||
CheckAllTerrains();
|
||||
|
||||
//Reset the terrain:
|
||||
RootUtils.StartProfiling(_road, "TerrainsReset");
|
||||
TerrainsReset(_road);
|
||||
RootUtils.EndProfiling(_road);
|
||||
|
||||
float heightDistance = _road.matchHeightsDistance;
|
||||
//float treeDistance = _road.clearTreesDistance;
|
||||
float detailDistance = _road.clearDetailsDistance;
|
||||
|
||||
Dictionary<Terrain, TempTerrainData> TempTerrainDict = new Dictionary<Terrain, TempTerrainData>();
|
||||
//Populate dictionary:
|
||||
Object[] allTerrains = GameObject.FindObjectsOfType<Terrain>();
|
||||
RoadTerrain TID;
|
||||
int aSize = 0;
|
||||
int dSize = 0;
|
||||
TempTerrainData TTD;
|
||||
bool isContaining = false;
|
||||
Construction2DRect tRect = null;
|
||||
//Construction2DRect rRect = null;
|
||||
|
||||
|
||||
foreach (Terrain terrain in allTerrains)
|
||||
{
|
||||
if (terrain.terrainData == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tRect = GetTerrainBounds(terrain);
|
||||
isContaining = false;
|
||||
//Debug.Log(terrain.transform.name + " bounds: " + tRect.ToStringRA());
|
||||
//Debug.Log(" Road bounds: " + tSpline.RoadV0 + "," + tSpline.RoadV1 + "," + tSpline.RoadV2 + "," + tSpline.RoadV3);
|
||||
|
||||
// Check if the terrain overlaps with a part of the spline
|
||||
if (isContaining != true && tRect.Contains(ref _spline.RoadV0))
|
||||
{
|
||||
isContaining = true;
|
||||
}
|
||||
else if (isContaining != true && tRect.Contains(ref _spline.RoadV1))
|
||||
{
|
||||
isContaining = true;
|
||||
}
|
||||
else if (isContaining != true && tRect.Contains(ref _spline.RoadV2))
|
||||
{
|
||||
isContaining = true;
|
||||
}
|
||||
else if (isContaining != true && tRect.Contains(ref _spline.RoadV3))
|
||||
{
|
||||
isContaining = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int nodeCount = _road.spline.GetNodeCount();
|
||||
Vector2 tVect2D_321 = default(Vector2);
|
||||
for (int index = 0; index < nodeCount; index++)
|
||||
{
|
||||
tVect2D_321 = new Vector2(_road.spline.nodes[index].pos.x, _road.spline.nodes[index].pos.z);
|
||||
if (tRect.Contains(ref tVect2D_321))
|
||||
{
|
||||
isContaining = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isContaining)
|
||||
{
|
||||
float tDef = 5f / _spline.distance;
|
||||
Vector2 x2D = default(Vector2);
|
||||
Vector3 x3D = default(Vector3);
|
||||
for (float index = 0f; index <= 1f; index += tDef)
|
||||
{
|
||||
x3D = _spline.GetSplineValue(index);
|
||||
x2D = new Vector2(x3D.x, x3D.z);
|
||||
if (tRect.Contains(ref x2D))
|
||||
{
|
||||
isContaining = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//rRect = new RoadUtility.Construction2DRect(tSpline.RoadV0,tSpline.RoadV1,tSpline.RoadV2,tSpline.RoadV3);
|
||||
|
||||
|
||||
if (isContaining && !TempTerrainDict.ContainsKey(terrain))
|
||||
{
|
||||
TTD = new TempTerrainData();
|
||||
TTD.HM = terrain.terrainData.heightmapResolution;
|
||||
TTD.HMHeight = terrain.terrainData.heightmapResolution;
|
||||
TTD.heights = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapResolution, terrain.terrainData.heightmapResolution);
|
||||
TTD.HMRatio = TTD.HM / terrain.terrainData.size.x;
|
||||
TTD.MetersPerHM = terrain.terrainData.size.x / terrain.terrainData.heightmapResolution;
|
||||
float DetailRatio = terrain.terrainData.detailResolution / terrain.terrainData.size.x;
|
||||
|
||||
//Heights:
|
||||
RootUtils.StartProfiling(_road, "Heights");
|
||||
if (_road.isHeightModificationEnabled)
|
||||
{
|
||||
aSize = (int)_spline.distance * ((int)(heightDistance * 1.65f * TTD.HMRatio) + 2);
|
||||
if (aSize > (terrain.terrainData.heightmapResolution * terrain.terrainData.heightmapResolution))
|
||||
{
|
||||
aSize = terrain.terrainData.heightmapResolution * terrain.terrainData.heightmapResolution;
|
||||
}
|
||||
TTD.cX = new ushort[aSize];
|
||||
TTD.cY = new ushort[aSize];
|
||||
TTD.oldH = new float[aSize];
|
||||
TTD.cH = new float[aSize];
|
||||
TTD.Count = 0;
|
||||
TTD.TerrainMaxIndex = terrain.terrainData.heightmapResolution;
|
||||
TTD.TerrainSize = terrain.terrainData.size;
|
||||
TTD.TerrainPos = terrain.transform.position;
|
||||
TTD.tHeights = new bool[terrain.terrainData.heightmapResolution, terrain.terrainData.heightmapResolution];
|
||||
TID = terrain.transform.gameObject.GetComponent<RoadTerrain>();
|
||||
if (TID != null)
|
||||
{
|
||||
TTD.uID = TID.UID;
|
||||
TempTerrainDict.Add(terrain, TTD);
|
||||
}
|
||||
}
|
||||
|
||||
//Details:
|
||||
RootUtils.EndStartProfiling(_road, "Details");
|
||||
if (_road.isDetailModificationEnabled)
|
||||
{
|
||||
//TTD.DetailValues = new Dictionary<int, int[,]>();
|
||||
TTD.DetailLayersCount = terrain.terrainData.detailPrototypes.Length;
|
||||
//TTD.DetailHasProcessed = new Dictionary<int, bool[,]>();
|
||||
TTD.DetailHasProcessed = new HashSet<int>();
|
||||
TTD.MainDetailsX = new List<ushort>();
|
||||
TTD.MainDetailsY = new List<ushort>();
|
||||
TTD.detailsCount = new int[TTD.DetailLayersCount];
|
||||
TTD.DetailToHeightRatio = (float)((float)terrain.terrainData.detailResolution) / ((float)terrain.terrainData.heightmapResolution);
|
||||
TTD.DetailMaxIndex = terrain.terrainData.detailResolution;
|
||||
TTD.DetailLayersSkip = new HashSet<int>();
|
||||
|
||||
// Get all of layer zero.
|
||||
//int[] mMinMaxDetailEntryCount = new int[TTD.DetailLayersCount];
|
||||
//RootUtils.StartProfiling(_road, "DetailValues");
|
||||
//Vector3 bVect = default(Vector3);
|
||||
//Vector2 bVect2D = default(Vector2);
|
||||
//int DetailRes = tTerrain.terrainData.detailResolution;
|
||||
//for(int i=0;i<TTD.DetailLayersCount;i++)
|
||||
//{
|
||||
// int[,] tInts = tTerrain.terrainData.GetDetailLayer(0,0,tTerrain.terrainData.detailWidth,tTerrain.terrainData.detailHeight,i);
|
||||
// int Length1 = tInts.GetLength(0);
|
||||
// int Length2 = tInts.GetLength(1);
|
||||
// for (int y=0;y < Length1;y++)
|
||||
// {
|
||||
// for (int x=0;x < Length2;x++)
|
||||
// {
|
||||
// if(tInts[x,y] > 0)
|
||||
// {
|
||||
// bVect = new Vector3(((float)y/(float)DetailRes) * TTD.TerrainSize.x,0f,((float)x/(float)DetailRes) * TTD.TerrainSize.z);
|
||||
// bVect = tTerrain.transform.TransformPoint(bVect);
|
||||
// bVect2D = new Vector2(bVect.z,bVect.x);
|
||||
// if(rRect.Contains(ref bVect2D))
|
||||
// {
|
||||
// mMinMaxDetailEntryCount[i] += 1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if(mMinMaxDetailEntryCount[i] < 1)
|
||||
// {
|
||||
// TTD.DetailLayersSkip.Add(i);
|
||||
// tInts = null;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// TTD.DetailValues.Add(i,tInts);
|
||||
// TTD.DetailHasProcessed.Add(i,new bool[tTerrain.terrainData.detailWidth,tTerrain.terrainData.detailHeight]);
|
||||
// }
|
||||
// }
|
||||
//RootUtils.EndProfiling(_road);
|
||||
|
||||
|
||||
dSize = (int)_spline.distance * ((int)(detailDistance * 3f * DetailRatio) + 2);
|
||||
if (dSize > (terrain.terrainData.detailResolution * terrain.terrainData.detailResolution))
|
||||
{
|
||||
dSize = terrain.terrainData.detailResolution * terrain.terrainData.detailResolution;
|
||||
}
|
||||
|
||||
//TTD.DetailsX = new List<ushort[]>();
|
||||
//TTD.DetailsY = new List<ushort[]>();
|
||||
//TTD.OldDetailsValue = new List<ushort[]>();
|
||||
TTD.DetailsX = new List<List<ushort>>();
|
||||
TTD.DetailsY = new List<List<ushort>>();
|
||||
TTD.OldDetailsValue = new List<List<ushort>>();
|
||||
//TTD.DetailHasProcessed = new List<List<bool>>();
|
||||
|
||||
for (int index = 0; index < TTD.DetailLayersCount; index++)
|
||||
{
|
||||
//if(TTD.DetailLayersSkip.Contains(index))
|
||||
//{
|
||||
// TTD.DetailsX.Add(new ushort[0]);
|
||||
// TTD.DetailsY.Add(new ushort[0]);
|
||||
// TTD.OldDetailsValue.Add(new ushort[0]);
|
||||
// continue;
|
||||
//}
|
||||
//int detailentrycount = (int)((float)mMinMaxDetailEntryCount[index] * 1.5f);
|
||||
//int d_temp_Size = dSize;
|
||||
//if(d_temp_Size > detailentrycount)
|
||||
//{
|
||||
// d_temp_Size = detailentrycount;
|
||||
//}
|
||||
//if(d_temp_Size < 1)
|
||||
//{
|
||||
// d_temp_Size = 1;
|
||||
//}
|
||||
//if(d_temp_Size > (tTerrain.terrainData.detailResolution * tTerrain.terrainData.detailResolution))
|
||||
//{
|
||||
// d_temp_Size = tTerrain.terrainData.detailResolution * tTerrain.terrainData.detailResolution;
|
||||
//}
|
||||
//
|
||||
//TTD.DetailsX.Add(new ushort[d_temp_Size]);
|
||||
//TTD.DetailsY.Add(new ushort[d_temp_Size]);
|
||||
//TTD.OldDetailsValue.Add(new ushort[d_temp_Size]);
|
||||
|
||||
TTD.DetailsX.Add(new List<ushort>());
|
||||
TTD.DetailsY.Add(new List<ushort>());
|
||||
TTD.OldDetailsValue.Add(new List<ushort>());
|
||||
}
|
||||
|
||||
|
||||
//TTD.DetailsX = new ushort[TTD.DetailLayersCount,dSize];
|
||||
//TTD.DetailsY = new ushort[TTD.DetailLayersCount,dSize];
|
||||
//TTD.OldDetailsValue = new ushort[TTD.DetailLayersCount,dSize];
|
||||
}
|
||||
|
||||
//Trees:
|
||||
RootUtils.EndStartProfiling(_road, "Trees");
|
||||
if (_road.isTreeModificationEnabled)
|
||||
{
|
||||
TTD.TreesCurrent = new List<TreeInstance>(terrain.terrainData.treeInstances);
|
||||
TTD.TreeSize = TTD.TreesCurrent.Count;
|
||||
TTD.treesCount = 0;
|
||||
TTD.TreesOld = new List<TreeInstance>();
|
||||
}
|
||||
RootUtils.EndProfiling(_road);
|
||||
}
|
||||
}
|
||||
|
||||
//Figure out relevant TTD to spline:
|
||||
List<TempTerrainData> EditorTTDList = new List<TempTerrainData>();
|
||||
if (TempTerrainDict != null)
|
||||
{
|
||||
foreach (Terrain tTerrain in allTerrains)
|
||||
{
|
||||
if (TempTerrainDict.ContainsKey(tTerrain))
|
||||
{
|
||||
EditorTTDList.Add(TempTerrainDict[tTerrain]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RootUtils.EndProfiling(_road);
|
||||
|
||||
//Start job now, for each relevant TTD:
|
||||
_road.SetEditorTerrainCalcs(ref EditorTTDList);
|
||||
if (_isMultithreaded)
|
||||
{
|
||||
Threading.TerrainCalcs terrainJob = new Threading.TerrainCalcs();
|
||||
terrainJob.Setup(ref EditorTTDList, _spline, _road);
|
||||
_road.TerrainCalcsJob = terrainJob;
|
||||
terrainJob.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Threading.TerrainCalcsStatic.RunMe(ref EditorTTDList, _spline, _road);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns an 2D rect of the terrain </summary>
|
||||
public static Construction2DRect GetTerrainBounds(Terrain _terrain)
|
||||
{
|
||||
float terrainWidth = _terrain.terrainData.size.x;
|
||||
float terrainLength = _terrain.terrainData.size.z;
|
||||
//Vector3 tPos = tTerrain.transform.TransformPoint(tTerrain.transform.position);
|
||||
|
||||
Vector3 X0 = new Vector3(0f, 0f, 0f);
|
||||
Vector3 X1 = new Vector3(terrainWidth, 0f, 0f);
|
||||
Vector3 X2 = new Vector3(terrainWidth, 0f, terrainLength);
|
||||
Vector3 X3 = new Vector3(0f, 0f, terrainLength);
|
||||
|
||||
X0 = _terrain.transform.TransformPoint(X0);
|
||||
X1 = _terrain.transform.TransformPoint(X1);
|
||||
X2 = _terrain.transform.TransformPoint(X2);
|
||||
X3 = _terrain.transform.TransformPoint(X3);
|
||||
|
||||
Vector2 P0 = new Vector2(X0.x, X0.z);
|
||||
Vector2 P1 = new Vector2(X1.x, X1.z);
|
||||
Vector2 P2 = new Vector2(X2.x, X2.z);
|
||||
Vector2 P3 = new Vector2(X3.x, X3.z);
|
||||
|
||||
|
||||
//OLD CODE:
|
||||
//Vector2 P0 = new Vector2(0f, 0f);
|
||||
//Vector2 P1 = new Vector2(terrainWidth, 0f);
|
||||
//Vector2 P2 = new Vector2(terrainWidth, terrainLength);
|
||||
//Vector2 P3 = new Vector2(0f, terrainLength);
|
||||
|
||||
//P0 = tTerrain.transform.TransformPoint(P0);
|
||||
//P1 = tTerrain.transform.TransformPoint(P1);
|
||||
//P2 = tTerrain.transform.TransformPoint(P2);
|
||||
//P3 = tTerrain.transform.TransformPoint(P3);
|
||||
|
||||
return new Construction2DRect(P0, P1, P2, P3, _terrain.transform.position.y);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Assign calculated values to terrains </summary>
|
||||
public static void ProcessRoadTerrainHook2(SplineC _spline, ref List<TempTerrainData> _TTDList)
|
||||
{
|
||||
RootUtils.StartProfiling(_spline.road, "ProcessRoadTerrainHook2");
|
||||
ProcessRoadTerrainHook2Do(ref _spline, ref _TTDList);
|
||||
RootUtils.EndProfiling(_spline.road);
|
||||
}
|
||||
|
||||
|
||||
private static void ProcessRoadTerrainHook2Do(ref SplineC _spline, ref List<TempTerrainData> _TTDList)
|
||||
{
|
||||
if (!_spline.road.isTreeModificationEnabled && !_spline.road.isHeightModificationEnabled && !_spline.road.isDetailModificationEnabled)
|
||||
{
|
||||
//Exit if no mod taking place.
|
||||
return;
|
||||
}
|
||||
Object[] TIDs = GameObject.FindObjectsOfType<RoadTerrain>();
|
||||
Terrain terrain;
|
||||
int[,] tDetails = null;
|
||||
int IntBufferX = 0;
|
||||
int IntBufferY = 0;
|
||||
int tVal = 0;
|
||||
foreach (TempTerrainData TTD in _TTDList)
|
||||
{
|
||||
foreach (RoadTerrain TID in TIDs)
|
||||
{
|
||||
if (TID.UID != TTD.uID)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
terrain = TID.transform.gameObject.GetComponent<Terrain>();
|
||||
if (terrain == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//Details:
|
||||
if (_spline.road.isDetailModificationEnabled)
|
||||
{
|
||||
for (int index = 0; index < TTD.DetailLayersCount; index++)
|
||||
{
|
||||
//if(TTD.DetailLayersSkip.Contains(i) || TTD.DetailValues[i] == null)
|
||||
//{
|
||||
// continue;
|
||||
//}
|
||||
//if(TTD.DetailsI[i] > 0)
|
||||
//{
|
||||
// tTerrain.terrainData.SetDetailLayer(0, 0, i, TTD.DetailValues[i]);
|
||||
//}
|
||||
|
||||
if (TTD.DetailLayersSkip.Contains(index) || TTD.MainDetailsX == null || TTD.MainDetailsX.Count < 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tDetails = terrain.terrainData.GetDetailLayer(0, 0, TTD.DetailMaxIndex, TTD.DetailMaxIndex, index);
|
||||
|
||||
int MaxCount = TTD.MainDetailsX.Count;
|
||||
for (int j = 0; j < MaxCount; j++)
|
||||
{
|
||||
IntBufferX = TTD.MainDetailsX[j];
|
||||
IntBufferY = TTD.MainDetailsY[j];
|
||||
tVal = tDetails[IntBufferX, IntBufferY];
|
||||
if (tVal > 0)
|
||||
{
|
||||
TTD.DetailsX[index].Add((ushort)IntBufferX);
|
||||
TTD.DetailsY[index].Add((ushort)IntBufferY);
|
||||
TTD.OldDetailsValue[index].Add((ushort)tVal);
|
||||
tDetails[IntBufferX, IntBufferY] = 0;
|
||||
}
|
||||
}
|
||||
TTD.detailsCount[index] = TTD.DetailsX[index].Count;
|
||||
|
||||
terrain.terrainData.SetDetailLayer(0, 0, index, tDetails);
|
||||
tDetails = null;
|
||||
TTD.DetailHasProcessed = null;
|
||||
}
|
||||
TTD.MainDetailsX = null;
|
||||
TTD.MainDetailsY = null;
|
||||
System.GC.Collect();
|
||||
}
|
||||
//Trees:
|
||||
if (_spline.road.isTreeModificationEnabled && TTD.TreesCurrent != null && TTD.treesCount > 0)
|
||||
{
|
||||
terrain.terrainData.treeInstances = TTD.TreesCurrent.ToArray();
|
||||
}
|
||||
//Heights:
|
||||
if (_spline.road.isHeightModificationEnabled && TTD.heights != null && TTD.Count > 0)
|
||||
{
|
||||
//Do heights last to trigger collisions and stuff properly:
|
||||
terrain.terrainData.SetHeights(0, 0, TTD.heights);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void TerrainsReset(Road _road)
|
||||
{
|
||||
if (_road.TerrainHistory == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_road.TerrainHistory.Count < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Object[] TIDs = GameObject.FindObjectsOfType<RoadTerrain>();
|
||||
float[,] heights;
|
||||
int[,] tDetails;
|
||||
int ArrayCount;
|
||||
foreach (TerrainHistoryMaker TH in _road.TerrainHistory)
|
||||
{
|
||||
Terrain terrain = null;
|
||||
foreach (RoadTerrain TID in TIDs)
|
||||
{
|
||||
if (TID.UID == TH.terrainID)
|
||||
{
|
||||
terrain = TID.terrain;
|
||||
}
|
||||
}
|
||||
if (!terrain)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TH.heightmapResolution != terrain.terrainData.heightmapResolution)
|
||||
{
|
||||
TH.Nullify();
|
||||
continue;
|
||||
}
|
||||
|
||||
//Heights:
|
||||
if (TH.x1 != null)
|
||||
{
|
||||
heights = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapResolution, terrain.terrainData.heightmapResolution);
|
||||
ArrayCount = TH.Count;
|
||||
for (int index = 0; index < ArrayCount; index++)
|
||||
{
|
||||
heights[TH.x1[index], TH.y1[index]] = TH.height[index];
|
||||
}
|
||||
terrain.terrainData.SetHeights(0, 0, heights);
|
||||
}
|
||||
//Details:
|
||||
if (TH.detailsCount != null && TH.detailsX != null && TH.detailsY != null && TH.detailsOldValue != null)
|
||||
{
|
||||
int RealLayerCount = terrain.terrainData.detailPrototypes.Length;
|
||||
int StartIndex = 0;
|
||||
int EndIndex = 0;
|
||||
for (int index = 0; index < TH.detailLayersCount; index++)
|
||||
{
|
||||
if (index >= RealLayerCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (TH.detailsX.Length <= index)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (TH.detailsY.Length <= index)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (TH.detailsX.Length < 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tDetails = terrain.terrainData.GetDetailLayer(0, 0, terrain.terrainData.detailWidth, terrain.terrainData.detailHeight, index);
|
||||
ArrayCount = TH.detailsCount[index];
|
||||
if (ArrayCount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
EndIndex += ArrayCount;
|
||||
for (int j = StartIndex; j < EndIndex; j++)
|
||||
{
|
||||
tDetails[TH.detailsX[j], TH.detailsY[j]] = TH.detailsOldValue[j];
|
||||
}
|
||||
StartIndex = EndIndex;
|
||||
terrain.terrainData.SetDetailLayer(0, 0, index, tDetails);
|
||||
tDetails = null;
|
||||
}
|
||||
}
|
||||
//Trees:
|
||||
TreeInstance[] xTress = TH.MakeTrees();
|
||||
if (xTress != null)
|
||||
{
|
||||
ArrayCount = xTress.Length;
|
||||
if (ArrayCount > 0 && TH.oldTrees != null)
|
||||
{
|
||||
int TerrainTreeCount = terrain.terrainData.treeInstances.Length;
|
||||
TreeInstance[] tTrees = new TreeInstance[ArrayCount + TerrainTreeCount];
|
||||
System.Array.Copy(terrain.terrainData.treeInstances, 0, tTrees, 0, TerrainTreeCount);
|
||||
System.Array.Copy(xTress, 0, tTrees, TerrainTreeCount, ArrayCount);
|
||||
terrain.terrainData.treeInstances = tTrees;
|
||||
}
|
||||
xTress = null;
|
||||
}
|
||||
}
|
||||
System.GC.Collect();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89d78ed7b18900c4cb9d282113f97801
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: efa1287a39e985c40892386ab840d9e5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,104 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace RoadArchitect.Threading
|
||||
{
|
||||
public class TerrainCalcs : ThreadedJob
|
||||
{
|
||||
private object handle = new object();
|
||||
private List<Terraforming.TempTerrainData> TTDList;
|
||||
private SplineC spline;
|
||||
private Road road;
|
||||
|
||||
|
||||
public void Setup(ref List<Terraforming.TempTerrainData> _TTDList, SplineC _tSpline, Road _tRoad)
|
||||
{
|
||||
TTDList = _TTDList;
|
||||
spline = _tSpline;
|
||||
road = _tRoad;
|
||||
}
|
||||
|
||||
|
||||
protected override void ThreadFunction()
|
||||
{
|
||||
float Step = (road.roadDefinition * 0.4f) / spline.distance;
|
||||
if (Step > 2f)
|
||||
{
|
||||
Step = 2f;
|
||||
}
|
||||
if (Step < 1f)
|
||||
{
|
||||
Step = 1f;
|
||||
}
|
||||
//float tDistance = tRoad.RoadWidth()*2f;
|
||||
|
||||
//Vector3 tVect, POS;
|
||||
foreach (Terraforming.TempTerrainData TTD in TTDList)
|
||||
{
|
||||
//float PrevHeight = 0f;
|
||||
//float FinalMax = 1f;
|
||||
//float StartMin = 0f;
|
||||
//if(tSpline.bSpecialEndControlNode)
|
||||
//{
|
||||
// FinalMax = tSpline.mNodes[tSpline.GetNodeCount()-2].tTime;
|
||||
//}
|
||||
//if(tSpline.bSpecialStartControlNode)
|
||||
//{
|
||||
// StartMin = tSpline.mNodes[1].tTime;
|
||||
//}
|
||||
|
||||
//if(tRoad.opt_MatchTerrain)
|
||||
//{
|
||||
try
|
||||
{
|
||||
TerraformingThreaded.DoRects(spline, TTD);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
lock (handle)
|
||||
{
|
||||
road.isEditorError = true;
|
||||
road.exceptionError = e;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// for(float i=StartMin;i<=FinalMax;i+=Step)
|
||||
// {
|
||||
// if(tSpline.IsInBridgeTerrain(i))
|
||||
// {
|
||||
// float tFloat = tSpline.GetBridgeEnd(i);
|
||||
// if(IsApproximately(tFloat,1f,0.00001f) || tFloat > 1f)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// if(tFloat < 0f)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// i = tFloat;
|
||||
// }
|
||||
// tSpline.GetSplineValue_Both(i,out tVect,out POS);
|
||||
// PrevHeight = TerraformingThreaded.ProcessLineHeights(tSpline,ref tVect,ref POS,tDistance,TTD,PrevHeight);
|
||||
// tSpline.HeightHistory.Add(new KeyValuePair<float,float>(i,PrevHeight*TTD.TerrainSize.y));
|
||||
// }
|
||||
//
|
||||
// for(int i=0;i<TTD.cI;i++)
|
||||
// {
|
||||
// TTD.heights[TTD.cX[i],TTD.cY[i]] = TTD.cH[i];
|
||||
// }
|
||||
//}
|
||||
}
|
||||
spline.HeightHistory.Sort(CompareKeys);
|
||||
IsDone = true;
|
||||
}
|
||||
|
||||
|
||||
private int CompareKeys(KeyValuePair<float, float> _a, KeyValuePair<float, float> _b)
|
||||
{
|
||||
return _a.Key.CompareTo(_b.Key);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c40fe5dd65fa96c44b2efba54af237e5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,80 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace RoadArchitect.Threading
|
||||
{
|
||||
public static class TerrainCalcsStatic
|
||||
{
|
||||
public static void RunMe(ref List<Terraforming.TempTerrainData> _TTDList, SplineC _spline, Road _road)
|
||||
{
|
||||
float Step = (_road.roadDefinition * 0.4f) / _spline.distance;
|
||||
if (Step > 2f)
|
||||
{
|
||||
Step = 2f;
|
||||
}
|
||||
if (Step < 1f)
|
||||
{
|
||||
Step = 1f;
|
||||
}
|
||||
//float tDistance = tRoad.RoadWidth()*2f;
|
||||
|
||||
//Vector3 tVect,POS;
|
||||
|
||||
foreach (Terraforming.TempTerrainData TTD in _TTDList)
|
||||
{
|
||||
//float PrevHeight = 0f;
|
||||
//float FinalMax = 1f;
|
||||
//float StartMin = 0f;
|
||||
//if(tSpline.bSpecialEndControlNode)
|
||||
//{
|
||||
// FinalMax = tSpline.mNodes[tSpline.GetNodeCount()-2].tTime;
|
||||
//}
|
||||
//if(tSpline.bSpecialStartControlNode)
|
||||
//{
|
||||
// StartMin = tSpline.mNodes[1].tTime;
|
||||
//}
|
||||
|
||||
//if(tRoad.opt_MatchTerrain)
|
||||
//{
|
||||
RootUtils.StartProfiling(_road, "DoRects");
|
||||
TerraformingThreaded.DoRects(_spline, TTD);
|
||||
RootUtils.EndProfiling(_road);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// for(float i=StartMin;i<=FinalMax;i+=Step)
|
||||
// {
|
||||
// if(tSpline.IsInBridgeTerrain(i))
|
||||
// {
|
||||
// float tFloat = tSpline.GetBridgeEnd(i);
|
||||
// if(IsApproximately(tFloat,1f,0.00001f) || tFloat > 1f)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// if(tFloat < 0f)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// i = tFloat;
|
||||
// }
|
||||
// tSpline.GetSplineValue_Both(i,out tVect,out POS);
|
||||
// PrevHeight = TerraformingThreaded.ProcessLineHeights(tSpline,ref tVect,ref POS,tDistance,TTD,PrevHeight);
|
||||
// tSpline.HeightHistory.Add(new KeyValuePair<float,float>(i,PrevHeight*TTD.TerrainSize.y));
|
||||
// }
|
||||
//
|
||||
// for(int i=0;i<TTD.cI;i++)
|
||||
// {
|
||||
// TTD.heights[TTD.cX[i],TTD.cY[i]] = TTD.cH[i];
|
||||
// }
|
||||
//}
|
||||
}
|
||||
_spline.HeightHistory.Sort(Compare1);
|
||||
}
|
||||
|
||||
|
||||
private static int Compare1(KeyValuePair<float, float> _a, KeyValuePair<float, float> _b)
|
||||
{
|
||||
return _a.Key.CompareTo(_b.Key);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user