TrackLine.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. //-----------------------------------------------------------------------------
  2. // TrackLine.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using Microsoft.Xna.Framework;
  8. using Microsoft.Xna.Framework.Graphics;
  9. using Microsoft.Xna.Framework.Input;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Text;
  13. using RacingGame.Graphics;
  14. using RacingGame.Helpers;
  15. using RacingGame.Landscapes;
  16. namespace RacingGame.Tracks
  17. {
  18. /// <summary>
  19. /// Track line
  20. /// </summary>
  21. public class TrackLine
  22. {
  23. /// <summary>
  24. /// Number of iterations we will produce from the input points to
  25. /// get our line positions. All data is generated with help of
  26. /// CattmullRom splines.
  27. /// </summary>
  28. protected const int NumberOfIterationsPer100Meters = 40;
  29. /// <summary>
  30. /// Curve factor, 1.0 will make all curves the same as the rotations are.
  31. /// Basically we just have to drive ahead. Reduce this value to make
  32. /// the track harder!
  33. /// </summary>
  34. const float CurveFactor = 0.25f;
  35. /// <summary>
  36. /// Correct the road to be up each step. This is important to
  37. /// make our road face always up except where we have no choice (loopings).
  38. /// </summary>
  39. const float UpFactorCorrector = 0.6f;
  40. /// <summary>
  41. /// Factor for streching the road texture, smaller values will the texture
  42. /// strech more.
  43. /// </summary>
  44. const float RoadTextureStrechFactor = 0.125f;
  45. /// <summary>
  46. /// Number of values we put into our second pass up vector smoothing
  47. /// alsorithm below.
  48. /// </summary>
  49. const int NumberOfUpSmoothValues = 10;
  50. /// <summary>
  51. /// Minimium distance of the road track to the landscape.
  52. /// </summary>
  53. const float MinimumLandscapeDistance = 2.0f;
  54. /// <summary>
  55. /// Looping points for generating smooth loops.
  56. /// See TrackLine constructor for details.
  57. /// </summary>
  58. static readonly Vector3[] LoopingPoints =
  59. new Vector3[]
  60. {
  61. new Vector3(0, 0, 0),
  62. new Vector3(0, 0.353553f, 0.146447f),
  63. new Vector3(0, 0.5f, 0.5f),
  64. new Vector3(0, 0.353553f, 1.0f-0.146447f),
  65. new Vector3(0, 0, 1.0f),
  66. new Vector3(0, -0.353553f, 1.0f-0.146447f),
  67. new Vector3(0, -0.5f, 0.5f),
  68. new Vector3(0, -0.353553f, 0.146447f),
  69. new Vector3(0, 0, 0),
  70. };
  71. /// <summary>
  72. /// Points of this track (middle line), generated from the input points
  73. /// in the constructor using <see="NumberOfIterationsForInputPoints" />.
  74. /// <para></para>
  75. /// Each point has also the following 3 vectors:
  76. /// Right vector: Makes it easier to create the road vertices. Can also be
  77. /// calculated by just crossing up and dir vectors! See Unit Test below.
  78. /// Up vector: This are the calculated up vectors after the second pass.
  79. /// We also work the CurveFactor and UpFactorCorrector in here!
  80. /// Dir vector: Not really used, can be ignored externally.
  81. /// But useful for generation and checking distances.
  82. /// </summary>
  83. protected List<TrackVertex> points = new List<TrackVertex>();
  84. /// <summary>
  85. /// Road helper position
  86. /// </summary>
  87. public class RoadHelperPosition
  88. {
  89. /// <summary>
  90. /// Type
  91. /// </summary>
  92. public TrackData.RoadHelper.HelperType type;
  93. /// <summary>
  94. /// Start and end road segment num for our tunnel/palms/laterns.
  95. /// </summary>
  96. public int startNum, endNum;
  97. /// <summary>
  98. /// Create road helper position
  99. /// </summary>
  100. /// <param name="setStartNum">Set start number</param>
  101. /// <param name="setEndNum">Set end number</param>
  102. public RoadHelperPosition(TrackData.RoadHelper.HelperType setType,
  103. int setStartNum, int setEndNum)
  104. {
  105. type = setType;
  106. startNum = setStartNum;
  107. endNum = setEndNum;
  108. }
  109. }
  110. /// <summary>
  111. /// Remember tunnel positions, used in the Track class for the
  112. /// tunnel generation. Usually we don't have much tunnels.
  113. /// </summary>
  114. protected List<RoadHelperPosition> helperPositions =
  115. new List<RoadHelperPosition>();
  116. /// <summary>
  117. /// Create track line
  118. /// </summary>
  119. /// <param name="inputPoints">Input Points, will form a closed curve
  120. /// </param>
  121. public TrackLine(Vector3[] inputPoints,
  122. List<TrackData.WidthHelper> widthHelpers,
  123. List<TrackData.RoadHelper> roadHelpers,
  124. List<TrackData.NeutralObject> neutralObjects,
  125. Landscape landscape)
  126. {
  127. Load(inputPoints, widthHelpers, roadHelpers, neutralObjects, landscape);
  128. }
  129. #if DEBUG
  130. /// <summary>
  131. /// Create track line.
  132. /// This constructor is only used for unit tests.
  133. /// </summary>
  134. /// <param name="inputPoints">Input points</param>
  135. public TrackLine(Vector3[] inputPoints)
  136. : this(inputPoints,
  137. new List<TrackData.WidthHelper>(),
  138. new List<TrackData.RoadHelper>(),
  139. new List<TrackData.NeutralObject>(),
  140. null)
  141. {
  142. }
  143. #endif
  144. /// <summary>
  145. /// Create track line
  146. /// </summary>
  147. /// <param name="inputPointsFromTrack">
  148. /// Input points from track</param>
  149. public TrackLine(TrackData inputPointsFromTrack,
  150. Landscape landscape)
  151. : this(inputPointsFromTrack.TrackPoints.ToArray(),
  152. inputPointsFromTrack.WidthHelpers,
  153. inputPointsFromTrack.RoadHelpers,
  154. inputPointsFromTrack.NeutralsObjects,
  155. landscape)
  156. {
  157. }
  158. /// <summary>
  159. /// Load
  160. /// </summary>
  161. /// <param name="inputPoints">Input points</param>
  162. /// <param name="widthHelpers">Width helpers</param>
  163. /// <param name="roadHelpers">Road helpers</param>
  164. /// <param name="neutralObjects">Neutral objects</param>
  165. /// <param name="landscape">Landscape</param>
  166. protected void Load(Vector3[] inputPoints,
  167. List<TrackData.WidthHelper> widthHelpers,
  168. List<TrackData.RoadHelper> roadHelpers,
  169. List<TrackData.NeutralObject> neutralObjects,
  170. Landscape landscape)
  171. {
  172. points.Clear();
  173. helperPositions.Clear();
  174. // Kill all loaded objects
  175. if (landscape != null)
  176. landscape.KillAllLoadedObjects();
  177. if (inputPoints == null ||
  178. inputPoints.Length < 3)
  179. throw new ArgumentException("inputPoints is invalid, we need at " +
  180. "least 3 valid input points to generate a TrackLine.");
  181. if (landscape != null)
  182. {
  183. // Go through all spline points
  184. for (int num = 0; num < inputPoints.Length; num++)
  185. {
  186. // Get landscape height here
  187. float landscapeHeight = landscape.GetMapHeight(
  188. inputPoints[num].X, inputPoints[num].Y) +
  189. // add little to fix ground errors
  190. MinimumLandscapeDistance * 2.25f;
  191. // And make sure we are always above it!
  192. if (inputPoints[num].Z < landscapeHeight)
  193. inputPoints[num].Z = landscapeHeight;
  194. }
  195. // Second pass, check 24 interpolation points between all inputPoints
  196. for (int num = 0; num < inputPoints.Length; num++)
  197. for (int iter = 1; iter < 25; iter++)
  198. {
  199. float iterPercent = iter / 25.0f;
  200. float iterHeight = inputPoints[num].Z * (1 - iterPercent) +
  201. inputPoints[(num + 1) % inputPoints.Length].Z * iterPercent;
  202. // Check 2x2 points (in all directions) to make sure
  203. // we don't go through the landscape at the sides
  204. for (int x = 0; x < 2; x++)
  205. for (int y = 0; y < 2; y++)
  206. {
  207. // Also get height at middle to next pos
  208. float landscapeHeight = landscape.GetMapHeight(
  209. -5.0f + 10.0f * x +
  210. inputPoints[num].X * (1 - iterPercent) +
  211. inputPoints[(num + 1) % inputPoints.Length].X
  212. * iterPercent, -5.0f + 10.0f * y +
  213. inputPoints[num].Y * (1 - iterPercent) +
  214. inputPoints[(num + 1) % inputPoints.Length].Y
  215. * iterPercent) +
  216. // add little to fix ground errors
  217. MinimumLandscapeDistance * 1.6f;
  218. // Increase both positions if this point
  219. // is under the landscape
  220. if (iterHeight < landscapeHeight)
  221. {
  222. float increaseHeight = landscapeHeight - iterHeight;
  223. inputPoints[num].Z += increaseHeight;
  224. inputPoints[(num + 1) % inputPoints.Length].Z +=
  225. increaseHeight;
  226. }
  227. }
  228. }
  229. }
  230. // Go through all spline points (ignore first and last 3, this
  231. // makes it easier to remove points and add new ones).
  232. for (int num = 1; num < inputPoints.Length - 3; num++)
  233. {
  234. // X/Y distance has to be 4 times smaller than Z distance
  235. Vector3 distVec = inputPoints[num + 1] - inputPoints[num];
  236. float xyDist = (float)Math.Sqrt(
  237. distVec.X * distVec.X + distVec.Y * distVec.Y);
  238. float zDist = Math.Abs(distVec.Z);
  239. // Also check if next point is down again.
  240. Vector3 distVec2 = inputPoints[num + 2] - inputPoints[num + 1];
  241. if (zDist / 2 > xyDist &&
  242. Math.Abs(distVec.Z + distVec2.Z) < zDist / 2)
  243. {
  244. // Find out which direction we are going
  245. Vector3 dir = inputPoints[num] - inputPoints[num - 1];
  246. dir.Normalize();
  247. Vector3 upVec = new Vector3(0, 0, 1);
  248. Vector3 rightVec = Vector3.Cross(dir, upVec);
  249. // Matrix build helper matrix to rotate our looping points
  250. Matrix rotMatrix = new Matrix(
  251. rightVec.X, rightVec.Y, rightVec.Z, 0,
  252. dir.X, dir.Y, dir.Z, 0,
  253. upVec.X, upVec.Y, upVec.Z, 0,
  254. 0, 0, 0, 1);
  255. // Ok do a looping with zDist as height.
  256. // Start with the current point, loop around and end with the
  257. // point after the looping. We will remove the current and the
  258. // next 2 points, but add 9 new points instead for our smooth loop.
  259. // See LoopingPoints for the looping itself.
  260. Vector3 startLoopPos = inputPoints[num];
  261. Vector3 endLoopPos = inputPoints[num + 2];
  262. // Insert 7 new points (9 new points, but we reuse
  263. // start, middle and end points which are num, num+1 and num+2,
  264. // plus an additional point after the looping to keep the road
  265. // straight!)
  266. Vector3[] remInputPoints = (Vector3[])inputPoints.Clone();
  267. inputPoints = new Vector3[inputPoints.Length + 7];
  268. // Copy everything over
  269. for (int copyNum = 0; copyNum < remInputPoints.Length; copyNum++)
  270. if (copyNum < num)
  271. inputPoints[copyNum] = remInputPoints[copyNum];
  272. else
  273. inputPoints[copyNum + 7] = remInputPoints[copyNum];
  274. // Ok, now we can add our loop
  275. for (int loopNum = 0; loopNum < LoopingPoints.Length; loopNum++)
  276. {
  277. // Interpolate between start and end pos to land at the end pos!
  278. float loopPercent = loopNum / (float)(LoopingPoints.Length - 1);
  279. inputPoints[num + loopNum] =
  280. startLoopPos * (1 - loopPercent) +
  281. endLoopPos * loopPercent +
  282. zDist * Vector3.Transform(LoopingPoints[loopNum], rotMatrix);
  283. }
  284. // Add extra point to keep the road straight
  285. Vector3 newRoadDir =
  286. inputPoints[num + 10] - inputPoints[num + 8];
  287. // Don't go more than zDist * 2 units away!
  288. if (newRoadDir.Length() > zDist * 2)
  289. {
  290. newRoadDir.Normalize();
  291. newRoadDir = newRoadDir * zDist;
  292. inputPoints[num + 9] = inputPoints[num + 8] + newRoadDir;
  293. }
  294. else
  295. // Just add an interpolation point
  296. inputPoints[num + 9] =
  297. (inputPoints[num + 8] + inputPoints[num + 10]) / 2.0f;
  298. // Advance 10 points until we check for the next loop
  299. num += 10;
  300. // That's it, good work everyone ^^
  301. }
  302. }
  303. // Generate all points with help of catmull rom splines
  304. for (int num = 0; num < inputPoints.Length; num++)
  305. {
  306. // Get the 4 required points for the catmull rom spline
  307. Vector3 p1 = inputPoints[num - 1 < 0 ? inputPoints.Length - 1 : num - 1];
  308. Vector3 p2 = inputPoints[num];
  309. Vector3 p3 = inputPoints[(num + 1) % inputPoints.Length];
  310. Vector3 p4 = inputPoints[(num + 2) % inputPoints.Length];
  311. // Calculate number of iterations we use here based
  312. // on the distance of the 2 points we generate new points from.
  313. float distance = Vector3.Distance(p2, p3);
  314. int numberOfIterations =
  315. (int)(NumberOfIterationsPer100Meters * (distance / 100.0f));
  316. if (numberOfIterations <= 0)
  317. numberOfIterations = 1;
  318. for (int iter = 0; iter < numberOfIterations; iter++)
  319. {
  320. TrackVertex newVertex = new TrackVertex(
  321. Vector3.CatmullRom(p1, p2, p3, p4,
  322. iter / (float)numberOfIterations));
  323. points.Add(newVertex);
  324. }
  325. }
  326. // Pre up vectors are used to first generate all optimal up vectors
  327. // for the track, but this is not useful for driving because we need
  328. // the road to point up always except for loopings.
  329. List<Vector3> preUpVectors = new List<Vector3>();
  330. // Now generate all up vectors, first pass does optimal up vectors.
  331. Vector3 defaultUpVec = new Vector3(0, 0, 1);
  332. Vector3 lastUpVec = defaultUpVec;
  333. for (int num = 0; num < points.Count; num++)
  334. {
  335. // Get direction we are driving in at this point,
  336. // interpolate with help of last and next points.
  337. Vector3 dir = points[(num + 1) % points.Count].pos -
  338. points[num - 1 < 0 ? points.Count - 1 : num - 1].pos;
  339. dir.Normalize();
  340. // Now calculate the optimal up vector for this point
  341. Vector3 middlePoint = (points[(num + 1) % points.Count].pos +
  342. points[num - 1 < 0 ? points.Count - 1 : num - 1].pos) / 2.0f;
  343. Vector3 optimalUpVector = middlePoint - points[num].pos;
  344. if (optimalUpVector.Length() < 0.0001f)
  345. optimalUpVector = lastUpVec;
  346. optimalUpVector.Normalize();
  347. // Store the optimalUpVectors in the preUpVectors list
  348. preUpVectors.Add(optimalUpVector);
  349. // Also save dir vector
  350. points[num].dir = dir;
  351. // And remember the last upVec in case the road is going straight ahead
  352. lastUpVec = optimalUpVector;
  353. }
  354. // Interpolate the first up vector for a smoother road at the start pos
  355. preUpVectors[0] = preUpVectors[preUpVectors.Count - 1] + preUpVectors[1];
  356. preUpVectors[0].Normalize();
  357. // Second pass, interpolated precalced values and apply our logic :)
  358. //preUpVectors[0] =
  359. lastUpVec = Vector3.Lerp(defaultUpVec, preUpVectors[0],
  360. 1.5f * CurveFactor * UpFactorCorrector);
  361. //lastUpVec = preUpVectors[0];
  362. Vector3 lastUpVecUnmodified = lastUpVec;// defaultUpVec;
  363. for (int num = 0; num < points.Count; num++)
  364. {
  365. // Grab dir vector (could be calculated here too)
  366. Vector3 dir = points[num].dir;
  367. // First of all interpolate the preUpVectors
  368. Vector3 upVec =
  369. //single input: preUpVectors[num];
  370. Vector3.Zero;
  371. for (int smoothNum = -NumberOfUpSmoothValues / 2;
  372. smoothNum <= NumberOfUpSmoothValues / 2; smoothNum++)
  373. upVec +=
  374. preUpVectors[(num + points.Count + smoothNum) % points.Count];
  375. upVec.Normalize();
  376. // Find out if this road piece is upside down and if we are
  377. // moving up or down. This is VERY important for catching loopings.
  378. bool upsideDown = upVec.Z < -0.25f &&
  379. lastUpVecUnmodified.Z < -0.05f;
  380. bool movingUp = dir.Z > 0.75f;
  381. bool movingDown = dir.Z < -0.75f;
  382. // Mix in the last vector to make curves weaker
  383. upVec = Vector3.Lerp(lastUpVec, upVec, CurveFactor);
  384. upVec.Normalize();
  385. // Store the last value to check for loopings.
  386. lastUpVecUnmodified = upVec;
  387. // Don't mix in default up if we head up or are upside down!
  388. // Its very useful to know if we move up or down to fix the
  389. // problematic areas at loopings by pointing stuff correct right away.
  390. if (movingUp)
  391. lastUpVec = Vector3.Lerp(upVec, -defaultUpVec, UpFactorCorrector);
  392. else if (movingDown)
  393. lastUpVec = Vector3.Lerp(upVec, defaultUpVec, UpFactorCorrector);
  394. else if (upsideDown)
  395. lastUpVec = Vector3.Lerp(upVec, -defaultUpVec, UpFactorCorrector);
  396. else
  397. lastUpVec = Vector3.Lerp(upVec, defaultUpVec, UpFactorCorrector);
  398. // If we are very close to the ground, make the road point up more!
  399. if (//upsideDown == false &&
  400. landscape != null)
  401. {
  402. // Get landscape height here
  403. float landscapeHeight = landscape.GetMapHeight(
  404. points[num].pos.X, points[num].pos.Y);
  405. // If point is close to the landscape, let everything point up more
  406. if (points[num].pos.Z - landscapeHeight <
  407. MinimumLandscapeDistance * 4)
  408. lastUpVec = Vector3.Lerp(upVec, defaultUpVec,
  409. 1.75f * UpFactorCorrector);
  410. }
  411. // And finally calculate rightVectors with just a cross product.
  412. // Used to render the track later.
  413. Vector3 rightVec = Vector3.Cross(dir, upVec);
  414. rightVec.Normalize();
  415. points[num].right = rightVec;
  416. // Recalculate up vector with help of right and dir.
  417. // This makes the up vector to always point up 90 degrees.
  418. upVec = Vector3.Cross(rightVec, dir);
  419. upVec.Normalize();
  420. points[num].up = upVec;
  421. }
  422. lastUpVec = points[0].up;
  423. for (int num = 0; num < points.Count; num++)
  424. preUpVectors[num] = points[num].up;
  425. for (int num = 0; num < points.Count; num++)
  426. {
  427. // Interpolate up vectors again
  428. Vector3 upVec = Vector3.Zero;
  429. for (int smoothNum = -NumberOfUpSmoothValues;
  430. smoothNum <= NumberOfUpSmoothValues; smoothNum++)
  431. {
  432. upVec +=
  433. preUpVectors[(num + points.Count + smoothNum) % points.Count];
  434. }
  435. upVec.Normalize();
  436. points[num].up = upVec;
  437. // Also rebuild right vector
  438. Vector3 dir = points[num].dir;
  439. points[num].right = Vector3.Cross(dir, upVec);
  440. }
  441. AdjustRoadWidths(widthHelpers);
  442. GenerateUTextureCoordinates();
  443. GenerateTunnelsAndLandscapeObjects(
  444. roadHelpers, neutralObjects, landscape);
  445. }
  446. /// <summary>
  447. /// Load
  448. /// </summary>
  449. /// <param name="trackData">track</param>
  450. /// <param name="landscape">Landscape</param>
  451. protected void Load(TrackData trackData, Landscape landscape)
  452. {
  453. Load(trackData.TrackPoints.ToArray(),
  454. trackData.WidthHelpers,
  455. trackData.RoadHelpers,
  456. trackData.NeutralsObjects,
  457. landscape);
  458. }
  459. private void AdjustRoadWidths(
  460. List<TrackData.WidthHelper> widthHelpers)
  461. {
  462. // Go through all width helpers along the road,
  463. // everything close enough (<25) is interessting for us.
  464. float currentWidth = TrackVertex.DefaultRoadWidth;
  465. float widthInfluence = currentWidth;
  466. for (int num = 0; num < points.Count; num++)
  467. {
  468. Vector3 pos = points[num].pos;
  469. foreach (TrackData.WidthHelper widthHelper in widthHelpers)
  470. {
  471. float dist = Vector3.Distance(widthHelper.pos, pos);
  472. if (dist < 25.0f)
  473. {
  474. float influence = (1 - (dist / 25.0f));
  475. widthInfluence =
  476. (1 - influence) * widthInfluence +
  477. influence * widthHelper.scale;
  478. }
  479. }
  480. // Use 90% the old width and 10% the new width.
  481. currentWidth =
  482. currentWidth * 0.9f +
  483. widthInfluence * 0.1f;
  484. // At the end of the road, mix in the staring road width (loop)
  485. if (num > points.Count - 7)
  486. {
  487. float influence =
  488. num == (points.Count - 1) ? 0.75f :
  489. num == (points.Count - 2) ? 0.5f :
  490. num == (points.Count - 2) ? 0.25f : 0.175f;
  491. currentWidth =
  492. influence * points[0].roadWidth +
  493. (1 - influence) * currentWidth;
  494. }
  495. if (currentWidth < TrackVertex.MinRoadWidth)
  496. currentWidth = TrackVertex.MinRoadWidth;
  497. if (currentWidth > TrackVertex.MaxRoadWidth)
  498. currentWidth = TrackVertex.MaxRoadWidth;
  499. points[num].roadWidth = currentWidth;
  500. }
  501. }
  502. private void GenerateUTextureCoordinates()
  503. {
  504. float currentRoadUTexValue = 0.0f;
  505. for (int num = 0; num < points.Count; num++)
  506. {
  507. // Assign u texture coordinate
  508. points[num].uv.X = currentRoadUTexValue;
  509. // Uniform calculation of the texture coordinates for the roadway,
  510. // so it doesn't matter if there is a gap of 2 or 200 m
  511. currentRoadUTexValue += RoadTextureStrechFactor *
  512. (points[(num + 1) % points.Count].pos -
  513. points[num % points.Count].pos).Length();
  514. }
  515. // Now we got a problem, for the polygons between the last and the first
  516. // points (last road block) we might have very different texture
  517. // coordinates, which may look very wrong. To fix this we generate a new
  518. // point by just duplicating the first point and applying another set of
  519. // texture coordinates!
  520. points.Add(new TrackVertex(
  521. points[0].pos,
  522. points[0].right,
  523. points[0].up,
  524. points[0].dir,
  525. new Vector2(currentRoadUTexValue, 0),
  526. points[0].roadWidth));
  527. }
  528. private void GenerateTunnelsAndLandscapeObjects(
  529. List<TrackData.RoadHelper> roadHelpers,
  530. List<TrackData.NeutralObject> neutralObjects,
  531. Landscape landscape)
  532. {
  533. // Go through all tunnel helpers along the road,
  534. // everything close enough (<25) is interessting for us.
  535. int helperStartedNum = -1;
  536. TrackData.RoadHelper.HelperType remType =
  537. TrackData.RoadHelper.HelperType.Reset;
  538. for (int num = 0; num < points.Count; num++)
  539. {
  540. Vector3 pos = points[num].pos;
  541. foreach (TrackData.RoadHelper roadHelper in roadHelpers)
  542. {
  543. float dist = Vector3.Distance(roadHelper.pos, pos);
  544. if (dist < 25.0f)
  545. {
  546. if (helperStartedNum >= 0)
  547. {
  548. helperPositions.Add(new RoadHelperPosition(
  549. remType, helperStartedNum, num));
  550. // Reset?
  551. if (roadHelper.type == TrackData.RoadHelper.HelperType.Reset)
  552. helperStartedNum = -1;
  553. else
  554. {
  555. // Start new part
  556. helperStartedNum = num;
  557. remType = roadHelper.type;
  558. }
  559. }
  560. else
  561. {
  562. helperStartedNum = num;
  563. remType = roadHelper.type;
  564. }
  565. // Remove this roadHelper (don't use it again)!
  566. roadHelpers.Remove(roadHelper);
  567. break;
  568. }
  569. }
  570. }
  571. // Still a helper open? Then close it close before the end!
  572. if (helperStartedNum > 0)
  573. helperPositions.Add(new RoadHelperPosition(
  574. remType, helperStartedNum, points.Count - 3));
  575. if (landscape != null)
  576. {
  577. for (int num = 0; num < neutralObjects.Count; num++)
  578. {
  579. TrackData.NeutralObject obj = neutralObjects[num];
  580. landscape.AddObjectToRender(obj.modelName, obj.matrix, false);
  581. }
  582. }
  583. }
  584. }
  585. }