Add Unity3D projects

This commit is contained in:
2024-03-09 19:25:59 +00:00
parent 1c71f24fab
commit 829289c881
4626 changed files with 441247 additions and 0 deletions

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3decfdad53e38bd45a25cb1252e303cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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());
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9128cfbedf3667e4d94db8f6382e15c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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());
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76e9419cbeed3894195e9636c89bcf07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2602b8c6977a4bd4a8774de9e0c51159
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8907a34bd7695644eb4ed18dedfc90ad
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 09c94f91ab2d0d34c9c464536e8633e9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 36611904ee7524a45afea00db85e5039
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f40193a8b5e9ed84f9597bbaefb0635d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 83a696e187e0572418fa95bc2a0cecd0
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cd895ddcc671fce4897906afe978103f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f2d67b50d8bfdea40b03587f29096fa8
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: db6a849a9f10a2b4c947989f1bbf77a5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d698b63effc4b914e93d9826206bee51
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d228ad4f1407c8439cc06fbea6105f3
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d58d1757d440da4d888627fc1b0d993
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1dd0c2cac8820d84ea773a41ba7cde2f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6ca689edb7ae35b43b50bd73fc934e96
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0b18264adf12b2e4fad840ded7592b28
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0a8e0720738e16f42b9c3590ab753d23
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4ed90bd0a0e8eca4788cb2fc0a693239
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 60c818167efdaca42b821a9783d2499b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 218998ce7cd01014e93378848b026646
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8844514acb92ad846b0b50ee865c0437
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57589cfe9e0ca704790ba51de619e96e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb1cc4328f3ca5c4cbca5e1b8ee3cbab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1235a2ff9a78ead43a82b06cef257a4b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}
*/
}
}

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b135db4f64080a94690da38eededc382
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}
}
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 523098dd722dc844b97837ec5b0e2147
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f2fe1769db58d224fb3fb9a5da3ef544
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44549a173dbacf648a600d8025eb74da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4772360fe19fb6149a45b0fc83bfafa0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 638b1eacf8fad7846adcbe66c990c391
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 35c74acc84102b648bdbb722555e1549
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2c73d433cef155941a77da37fec1db37
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 95a2de324735d2b4cba8308fffecb9a8
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: faeb0a5a9cb63474286df2cedfc2dd43
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a4da03b820eb32a4e956526da9ed6735
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c55b863d2023cc945816805d2662e0fd
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 52b86a2db11f88646b5056e7c8947561
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4ff0e4d10039dec4cbbfa42615982b7e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 22682a161c5c35d40ade7b573c38510d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f26cfb64f42345341bfd8b8be2374f73
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ab2ad73e5dbe50c4c8414b8efd183edf
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 16ac6886edfc6d44cb1aa025800086f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eee159a043b9b974799d1c4661130432
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3f0c7b62fc158f445ab043d2f4c65757
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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>();
}
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 81fe195562332d04ead2688a86c7325b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 89d78ed7b18900c4cb9d282113f97801
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: efa1287a39e985c40892386ab840d9e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c40fe5dd65fa96c44b2efba54af237e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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