This repository has been archived by the owner on Jun 26, 2021. It is now read-only.
forked from MicroGSD/RoadArchitect
-
Notifications
You must be signed in to change notification settings - Fork 2
/
GSDRoadAutomation.cs
382 lines (343 loc) · 18.9 KB
/
GSDRoadAutomation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
#region "Imports"
using UnityEngine;
using System.Collections.Generic;
//using System.Collections; // Unused
//using System.IO; // Unused
//using System.Text; // Unused
//using System.Runtime.Serialization.Formatters.Binary; // Unused
//using System.Runtime.Serialization; // Unused
#endregion
namespace GSD.Roads
{
#if UNITY_EDITOR
/* Proper automation flow:
* 1. Make sure opt_bAllowRoadUpdates in the scene's GSDRoadSystem is set to FALSE.
* 2. Create your roads programmatically via CreateRoad_Programmatically (pass it the road, and then the points in a list)
* a. Optionally you can do it via CreateNode_Programmatically and InsertNode_Programmatically
* 3. Call CreateIntersections_ProgrammaticallyForRoad for each road to create intersections automatically at intersection points.
* 4. Set opt_bAllowRoadUpdates in the scene's GSDRoadSystem is set to TRUE.
* 5. Call GSDRoadSystem.UpdateAllRoads();
* 6. Call GSDRoadSystem.UpdateAllRoads(); after step #5 completes.
*
* See "GSDUnitTests.cs" for an example on automation (ignore unit test #3).
*/
public static class GSDRoadAutomation
{
/// <summary>
/// Use this to create nodes via coding while in editor mode. Make sure opt_bAllowRoadUpdates is set to false in RS.GSDRS.opt_bAllowRoadUpdates.
/// </summary>
/// <param name="RS">The road system to create nodes on.</param>
/// <param name="NodeLocation">The location of the newly created node.</param>
/// <returns></returns>
public static GSDRoad CreateRoad_Programmatically(GSDRoadSystem GSDRS, ref List<Vector3> tLocs)
{
GameObject tRoadObj = GSDRS.AddRoad(false);
GSDRoad tRoad = tRoadObj.GetComponent<GSDRoad>();
int hCount = tLocs.Count;
for (int index = 0; index < hCount; index++)
{
CreateNode_Programmatically(tRoad, tLocs[index]);
}
return tRoad;
}
/// <summary>
/// Use this to create nodes via coding while in editor mode. Make sure opt_bAllowRoadUpdates is set to false in RS.GSDRS.opt_bAllowRoadUpdates.
/// </summary>
/// <param name="RS">The road system to create nodes on.</param>
/// <param name="NodeLocation">The location of the newly created node.</param>
/// <returns></returns>
public static GSDSplineN CreateNode_Programmatically(GSDRoad tRoad, Vector3 NodeLocation)
{
int SplineChildCount = tRoad.GSDSpline.transform.childCount;
GameObject tNodeObj = new GameObject("Node" + (SplineChildCount + 1).ToString());
GSDSplineN tNode = tNodeObj.AddComponent<GSDSplineN>(); //Add the node component.
//Set node location:
if (NodeLocation.y < 0.03f)
{
NodeLocation.y = 0.03f;
} //Make sure it doesn't try to create a node below 0 height.
tNodeObj.transform.position = NodeLocation;
//Set the node's parent:
tNodeObj.transform.parent = tRoad.GSDSplineObj.transform;
//Set the idOnSpline:
tNode.idOnSpline = (SplineChildCount + 1);
tNode.GSDSpline = tRoad.GSDSpline;
//Make sure opt_bAllowRoadUpdates is set to false in RS.GSDRS.opt_bAllowRoadUpdates
tRoad.UpdateRoad();
return tNode;
}
/// <summary>
/// Use this to insert nodes via coding while in editor mode. Make sure opt_bAllowRoadUpdates is set to false in RS.GSDRS.opt_bAllowRoadUpdates.
/// </summary>
/// <param name="RS">The road system to insert nodes in.</param>
/// <param name="NodeLocation">The location of the newly inserted node.</param>
/// <returns></returns>
public static GSDSplineN InsertNode_Programmatically(GSDRoad RS, Vector3 NodeLocation)
{
GameObject tNodeObj;
Object[] tWorldNodeCount = GameObject.FindObjectsOfType(typeof(GSDSplineN));
tNodeObj = new GameObject("Node" + tWorldNodeCount.Length.ToString());
//Set node location:
if (NodeLocation.y < 0.03f)
{
NodeLocation.y = 0.03f; //Make sure it doesn't try to create a node below 0 height.
}
tNodeObj.transform.position = NodeLocation;
//Set the node's parent:
tNodeObj.transform.parent = RS.GSDSplineObj.transform;
int cCount = RS.GSDSpline.mNodes.Count;
//Get the closet param on spline:
float tParam = RS.GSDSpline.GetClosestParam(NodeLocation, false, true);
bool bEndInsert = false;
bool bZeroInsert = false;
int iStart = 0;
if (GSDRootUtil.IsApproximately(tParam, 0f, 0.0001f))
{
bZeroInsert = true;
iStart = 0;
}
else if (GSDRootUtil.IsApproximately(tParam, 1f, 0.0001f))
{
//Inserted at end, switch to create node instead:
Object.DestroyImmediate(tNodeObj);
return CreateNode_Programmatically(RS, NodeLocation);
}
//Figure out where to insert the node:
for (int index = 0; index < cCount; index++)
{
GSDSplineN xNode = RS.GSDSpline.mNodes[index];
if (!bZeroInsert && !bEndInsert)
{
if (tParam > xNode.tTime)
{
iStart = xNode.idOnSpline + 1;
}
}
}
for (int index = iStart; index < cCount; index++)
{
RS.GSDSpline.mNodes[index].idOnSpline += 1;
}
GSDSplineN tNode = tNodeObj.AddComponent<GSDSplineN>();
tNode.GSDSpline = RS.GSDSpline;
tNode.idOnSpline = iStart;
tNode.pos = NodeLocation;
RS.GSDSpline.mNodes.Insert(iStart, tNode);
//Make sure opt_bAllowRoadUpdates is set to false in RS.GSDRS.opt_bAllowRoadUpdates
RS.UpdateRoad();
return tNode;
}
/// <summary>
/// Creates intersections where this road intersects with other roads.
/// </summary>
/// <param name="tRoad">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="rType">Intersection type: No turn lane, left turn lane or both turn lanes. Defaults to no turn lane.</param>
public static void CreateIntersections_ProgrammaticallyForRoad(GSDRoad tRoad, GSDRoadIntersection.iStopTypeEnum iStopType = GSDRoadIntersection.iStopTypeEnum.None, GSDRoadIntersection.RoadTypeEnum rType = GSDRoadIntersection.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[] GSDRoadObjs = Object.FindObjectsOfType<GSDRoad>();
//20m increments to gather collection of which roads intersect
List<GSDRoad> xRoads = new List<GSDRoad>();
foreach (GSDRoad xRoad in GSDRoadObjs)
{
if (tRoad != xRoad)
{
float EarlyDistanceCheckMeters = 10f;
float EarlyDistanceCheckThreshold = 50f;
bool EarlyDistanceFound = false;
float tRoadMod = EarlyDistanceCheckMeters / tRoad.GSDSpline.distance;
float xRoadMod = EarlyDistanceCheckMeters / xRoad.GSDSpline.distance;
Vector3 tVect1 = default(Vector3);
Vector3 tVect2 = default(Vector3);
for (float index = 0f; index < 1.0000001f; index += tRoadMod)
{
tVect1 = tRoad.GSDSpline.GetSplineValue(index);
for (float x = 0f; x < 1.000001f; x += xRoadMod)
{
tVect2 = xRoad.GSDSpline.GetSplineValue(x);
if (Vector3.Distance(tVect1, tVect2) < EarlyDistanceCheckThreshold)
{
if (!xRoads.Contains(xRoad))
{
xRoads.Add(xRoad);
}
EarlyDistanceFound = true;
break;
}
}
if (EarlyDistanceFound)
{
break;
}
}
}
}
//See if any end point nodes are on top of each other already since T might not intersect all the time.:
List<KeyValuePair<GSDSplineN, GSDSplineN>> tKVP = new List<KeyValuePair<GSDSplineN, GSDSplineN>>();
foreach (GSDRoad xRoad in xRoads)
{
foreach (GSDSplineN IntersectionNode1 in tRoad.GSDSpline.mNodes)
{
if (IntersectionNode1.bIsIntersection || !IntersectionNode1.IsLegitimate())
{
continue;
}
foreach (GSDSplineN IntersectionNode2 in xRoad.GSDSpline.mNodes)
{
if (IntersectionNode2.bIsIntersection || !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.bIsEndPoint || IntersectionNode2.bIsEndPoint)
{
tKVP.Add(new KeyValuePair<GSDSplineN, GSDSplineN>(IntersectionNode1, IntersectionNode2));
}
}
}
}
}
foreach (KeyValuePair<GSDSplineN, GSDSplineN> KVP in tKVP)
{
//Now create the fucking intersection:
GameObject tInter = GSD.Roads.GSDIntersections.CreateIntersection(KVP.Key, KVP.Value);
GSDRoadIntersection GSDRI_JustCreated = tInter.GetComponent<GSDRoadIntersection>();
GSDRI_JustCreated.iStopType = iStopType;
GSDRI_JustCreated.rType = rType;
}
//Main algorithm: 2m increments to find actual intersection point:
foreach (GSDRoad xRoad in xRoads)
{
if (tRoad != xRoad)
{
//Debug.Log("Checking road: " + xRoad.transform.name);
float DistanceCheckMeters = 2f;
bool EarlyDistanceFound = false;
float tRoadMod = DistanceCheckMeters / tRoad.GSDSpline.distance;
float xRoadMod = DistanceCheckMeters / xRoad.GSDSpline.distance;
Vector3 tVect = default(Vector3);
Vector2 iVect1 = default(Vector2);
Vector2 iVect2 = default(Vector2);
Vector2 xVect1 = default(Vector2);
Vector2 xVect2 = default(Vector2);
Vector2 IntersectPoint2D = default(Vector2);
float i2 = 0f;
for (float index = 0f; index < 1.0000001f; index += tRoadMod)
{
i2 = (index + tRoadMod);
if (i2 > 1f)
{
i2 = 1f;
}
tVect = tRoad.GSDSpline.GetSplineValue(index);
iVect1 = new Vector2(tVect.x, tVect.z);
tVect = tRoad.GSDSpline.GetSplineValue(i2);
iVect2 = new Vector2(tVect.x, tVect.z);
float x2 = 0f;
for (float x = 0f; x < 1.000001f; x += xRoadMod)
{
x2 = (x + xRoadMod);
if (x2 > 1f)
{
x2 = 1f;
}
tVect = xRoad.GSDSpline.GetSplineValue(x);
xVect1 = new Vector2(tVect.x, tVect.z);
tVect = xRoad.GSDSpline.GetSplineValue(x2);
xVect2 = new Vector2(tVect.x, tVect.z);
//Now see if these two lines intersect:
if (GSD.GSDRootUtil.Intersects2D(ref iVect1, ref iVect2, ref xVect1, ref xVect2, out IntersectPoint2D))
{
//Get height of intersection on primary road:
float tHeight = 0f;
float hParam = tRoad.GSDSpline.GetClosestParam(new Vector3(IntersectPoint2D.x, 0f, IntersectPoint2D.y));
Vector3 hVect = tRoad.GSDSpline.GetSplineValue(hParam);
tHeight = hVect.y;
//if any intersections already within 75m or 100m, dont create intersection here
Object[] AllInterectionObjects = Object.FindObjectsOfType<GSDRoadIntersection>();
foreach (GSDRoadIntersection GSDRI in AllInterectionObjects)
{
if (Vector2.Distance(new Vector2(GSDRI.transform.position.x, GSDRI.transform.position.z), IntersectPoint2D) < 100f)
{
goto NoIntersectionCreation;
}
}
GSDSplineN IntersectionNode1 = null;
GSDSplineN IntersectionNode2 = null;
Vector3 IntersectionPoint3D = new Vector3(IntersectPoint2D.x, tHeight, 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 (GSDSplineN tNode in tRoad.GSDSpline.mNodes)
{
if (tNode.IsLegitimate())
{
if (Vector2.Distance(new Vector2(tNode.transform.position.x, tNode.transform.position.z), IntersectPoint2D) < 30f)
{
IntersectionNode1 = tNode;
IntersectionNode1.transform.position = IntersectionPoint3D;
IntersectionNode1.pos = IntersectionPoint3D;
break;
}
}
}
//Check secondary road if any nodes are nearby and usable for intersection
foreach (GSDSplineN tNode in xRoad.GSDSpline.mNodes)
{
if (tNode.IsLegitimate())
{
if (Vector2.Distance(new Vector2(tNode.transform.position.x, tNode.transform.position.z), IntersectPoint2D) < 30f)
{
IntersectionNode2 = tNode;
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 = InsertNode_Programmatically(tRoad, IntersectionPoint3D);
}
if (IntersectionNode2 == null)
{
IntersectionNode2 = InsertNode_Programmatically(xRoad, IntersectionPoint3D);
}
//Now create the fucking intersection:
GameObject tInter = GSD.Roads.GSDIntersections.CreateIntersection(IntersectionNode1, IntersectionNode2);
GSDRoadIntersection GSDRI_JustCreated = tInter.GetComponent<GSDRoadIntersection>();
GSDRI_JustCreated.iStopType = iStopType;
GSDRI_JustCreated.rType = rType;
}
NoIntersectionCreation:
//Gibberish to get rid of warnings:
int xxx = 1;
if (xxx == 1)
{
xxx = 2;
}
}
if (EarlyDistanceFound)
{
break;
}
}
}
}
}
}
#endif
}