Track.cs 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410
  1. //-----------------------------------------------------------------------------
  2. // Track.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 System;
  10. using System.Collections.Generic;
  11. using System.Text;
  12. using RacingGame.Graphics;
  13. using RacingGame.Helpers;
  14. using RacingGame.Landscapes;
  15. using RacingGame.Shaders;
  16. using Model = RacingGame.Graphics.Model;
  17. using RacingGame.GameLogic;
  18. namespace RacingGame.Tracks
  19. {
  20. /// <summary>
  21. /// Track for our road in the game. Generated with help of the TrackLine
  22. /// class, which is loaded by a couple of spline points we get exported
  23. /// from 3ds max.
  24. /// </summary>
  25. public class Track : TrackLine, IDisposable
  26. {
  27. /// <summary>
  28. /// Factor for streching the width of the road back texture, smaller
  29. /// values will strech the texture more. 1.0f means we use the same as
  30. /// the road, which is defined in TrackVertex.RoadWidthScale.
  31. /// </summary>
  32. const float RoadBackHullTextureWidthFactor = 1.0f;
  33. /// <summary>
  34. /// Factor for streching the width of the road tunnel texture, smaller
  35. /// values will strech the texture more. 1.0f means we use the same as
  36. /// the road, which is defined in TrackVertex.RoadWidthScale.
  37. /// </summary>
  38. const float RoadTunnelTextureWidthFactor = 0.25f;
  39. /// <summary>
  40. /// Factor for texels we use from the roadBack texture for the sides.
  41. /// Most of the texture is used for the back side, but the sides
  42. /// are also at the top and bottom of the texture, use this factor
  43. /// to find out how much it is in the texture.
  44. /// </summary>
  45. const float RoadBackSideTextureHeight = 0.135f;
  46. /// <summary>
  47. /// Same as RoadBackSideTextureHeight, but we have more space for
  48. /// the sides in the tunnel texture!
  49. /// </summary>
  50. const float RoadTunnelSideTextureHeight = 0.235f;
  51. /// <summary>
  52. /// Palm and latern gap for the autogeneration.
  53. /// </summary>
  54. const float PalmAndLaternGap = 20.0f;
  55. /// <summary>
  56. /// Put a checkpoint every 500m
  57. /// </summary>
  58. const float CheckpointGap = 500.0f;
  59. /// <summary>
  60. /// Gap for signs, don't put them closer than this together.
  61. /// </summary>
  62. const float SignGap = 24;
  63. /// <summary>
  64. /// Road material for the top of the road.
  65. /// </summary>
  66. Material roadMaterial = new Material(
  67. "Road", "RoadNormal");
  68. /// <summary>
  69. /// Road back material, for the other side of the road and the sides.
  70. /// </summary>
  71. Material roadBackMaterial = new Material(
  72. "RoadBack", "RoadBackNormal");
  73. /// <summary>
  74. /// Road tunnel material, used whereever we got tunnels.
  75. /// </summary>
  76. Material roadTunnelMaterial = new Material(
  77. // Use mainly ambient color (tunnel uses lightmaps)
  78. new Color(182, 182, 182),
  79. new Color(80, 80, 80),
  80. new Color(64, 64, 64),
  81. "RoadTunnel", "RoadTunnelNormal", "", "");
  82. /// <summary>
  83. /// Road cement material, used for the columns the road is staying on.
  84. /// </summary>
  85. Material roadCementMaterial = new Material(
  86. "RoadCement", "RoadCementNormal");
  87. /// <summary>
  88. /// Guard rail material, used for the left and right guard rails.
  89. /// It is also used for the guard rail
  90. /// </summary>
  91. Material guardRailMaterial = new Material(
  92. new Color(72, 72, 72),
  93. new Color(182, 182, 182),
  94. new Color(225, 225, 225),
  95. "Leitplanke", "LeitplankeNormal", "", "");
  96. /// <summary>
  97. /// Vertices for the road itself.
  98. /// </summary>
  99. TangentVertex[] roadVertices = null;
  100. /// <summary>
  101. /// Vertex buffer for the road.
  102. /// </summary>
  103. VertexBuffer roadVb = null;
  104. /// <summary>
  105. /// Index buffer for the road.
  106. /// </summary>
  107. IndexBuffer roadIb = null;
  108. /// <summary>
  109. /// Vertices for the road back (hull, bottom side, sides).
  110. /// </summary>
  111. TangentVertex[] roadBackVertices = null;
  112. /// <summary>
  113. /// Vertex buffer for the road back (has different texture coordinates)
  114. /// </summary>
  115. VertexBuffer roadBackVb = null;
  116. /// <summary>
  117. /// Index buffer for the road back.
  118. /// </summary>
  119. IndexBuffer roadBackIb = null;
  120. /// <summary>
  121. /// Vertices for the road tunnels (sides and top).
  122. /// </summary>
  123. TangentVertex[] roadTunnelVertices = null;
  124. /// <summary>
  125. /// Remember road tunnel indices because determinating the count is
  126. /// not so easy (we can have multiple tunnels in here).
  127. /// </summary>
  128. int[] roadTunnelIndices = null;
  129. /// <summary>
  130. /// Vertex buffer for the road back (has different texture coordinates)
  131. /// </summary>
  132. VertexBuffer roadTunnelVb = null;
  133. /// <summary>
  134. /// Index buffer for the road back.
  135. /// </summary>
  136. IndexBuffer roadTunnelIb = null;
  137. /// <summary>
  138. /// Left and right guard rails.
  139. /// Renders the autogenerated rails and the helper piles to backup the
  140. /// gruard rail (leitplanke_pfahl.x model file, which is generated
  141. /// seperately).
  142. /// </summary>
  143. GuardRail leftRail = null,
  144. rightRail = null;
  145. /// <summary>
  146. /// Track columns, used for the columns the road is staying on.
  147. /// Renders autogenerated column objects and a helper segment for the
  148. /// ground (TrackColumnSegment.x model file, which is generated
  149. /// seperately).
  150. /// </summary>
  151. TrackColumns columns = null;
  152. /// <summary>
  153. /// Remember checkpoint segment positions for easier checkpoint checking.
  154. /// </summary>
  155. List<int> checkpointSegmentPositions = new List<int>();
  156. /// <summary>
  157. /// Start position
  158. /// </summary>
  159. /// <returns>Vector 3</returns>
  160. public Vector3 StartPosition
  161. {
  162. get
  163. {
  164. return points[0].pos;
  165. }
  166. }
  167. /// <summary>
  168. /// Start direction
  169. /// </summary>
  170. /// <returns>Vector 3</returns>
  171. public Vector3 StartDirection
  172. {
  173. get
  174. {
  175. return points[0].dir;
  176. }
  177. }
  178. /// <summary>
  179. /// Start up vector
  180. /// </summary>
  181. /// <returns>Vector 3</returns>
  182. public Vector3 StartUpVector
  183. {
  184. get
  185. {
  186. return points[0].up;
  187. }
  188. }
  189. /// <summary>
  190. /// Length
  191. /// </summary>
  192. /// <returns>Float</returns>
  193. public float Length
  194. {
  195. get
  196. {
  197. return points.Count * 100.0f / (float)NumberOfIterationsPer100Meters;
  198. }
  199. }
  200. /// <summary>
  201. /// Number of segments
  202. /// </summary>
  203. /// <returns>int</returns>
  204. public int NumberOfSegments
  205. {
  206. get
  207. {
  208. return points.Count;
  209. }
  210. }
  211. /// <summary>
  212. /// Remember checkpoint segment positions for easier checkpoint checking.
  213. /// </summary>
  214. public List<int> CheckpointSegmentPositions
  215. {
  216. get
  217. {
  218. return checkpointSegmentPositions;
  219. }
  220. }
  221. /// <summary>
  222. /// Create track
  223. /// </summary>
  224. /// <param name="setTrackName">Track name to load</param>
  225. /// <param name="landscape">Landscape to check if we are above it</param>
  226. public Track(string setTrackName, Landscape landscape)
  227. : base(TrackData.Load(setTrackName), landscape)
  228. {
  229. GenerateVerticesAndObjects(landscape);
  230. }
  231. /// <summary>
  232. /// Reload
  233. /// </summary>
  234. /// <param name="setTrackName">Track name</param>
  235. /// <param name="landscape">Landscape</param>
  236. public void Reload(string setTrackName, Landscape landscape)
  237. {
  238. // Do we need to load the base track again?
  239. // Always reload! Else we might mess up the checkpoints, etc.
  240. base.Load(TrackData.Load(setTrackName), landscape);
  241. GenerateVerticesAndObjects(landscape);
  242. }
  243. /// <summary>
  244. /// Generate vertices and objects
  245. /// </summary>
  246. private void GenerateVerticesAndObjects(Landscape landscape)
  247. {
  248. // Each road segment gets 5 points:
  249. // left, left middle, middle, right middle, right.
  250. // The reason for this is that we would bad triangle errors if the
  251. // road gets wider and wider. This happens because we need to render
  252. // quad, but we can only render triangles, which often have different
  253. // orientations, which makes the road very bumpy. This still happens
  254. // with 8 polygons instead of 2, but it is much better this way.
  255. // Another trick is not to do so much iterations in TrackLine, which
  256. // causes this problem. Better to have a not so round track, but at
  257. // least the road up/down itself is smooth.
  258. // The last point is duplicated (see TrackLine) because we have 2 sets
  259. // of texture coordinates for it (begin block, end block).
  260. // So for the index buffer we only use points.Count-1 blocks.
  261. roadVertices = new TangentVertex[points.Count * 5];
  262. // Current texture coordinate for the roadway (in direction of movement)
  263. for (int num = 0; num < points.Count; num++)
  264. {
  265. // Get vertices with help of the properties in the TrackVertex class.
  266. // For the road itself we only need vertices for the left and right
  267. // side, which are vertex number 0 and 1.
  268. roadVertices[num * 5 + 0] = points[num].RightTangentVertex;
  269. roadVertices[num * 5 + 1] = points[num].MiddleRightTangentVertex;
  270. roadVertices[num * 5 + 2] = points[num].MiddleTangentVertex;
  271. roadVertices[num * 5 + 3] = points[num].MiddleLeftTangentVertex;
  272. roadVertices[num * 5 + 4] = points[num].LeftTangentVertex;
  273. }
  274. // fix
  275. //roadVb = new VertexBuffer(
  276. // BaseGame.Device,
  277. // typeof(TangentVertex),
  278. // roadVertices.Length,
  279. // ResourceUsage.WriteOnly,
  280. // ResourceManagementMode.Automatic);
  281. roadVb = new VertexBuffer(
  282. BaseGame.Device,
  283. typeof(TangentVertex),
  284. roadVertices.Length,
  285. BufferUsage.WriteOnly);
  286. roadVb.SetData(roadVertices);
  287. // Also calculate all indices, we have 8 polygons for each segment with
  288. // 3 vertices each. We got 1 segment less than points because the
  289. // last point is duplicated (different tex coords).
  290. int[] indices = new int[(points.Count - 1) * 8 * 3];
  291. int vertexIndex = 0;
  292. for (int num = 0; num < points.Count - 1; num++)
  293. {
  294. // We only use 3 vertices (and the next 3 vertices),
  295. // but we have to construct all 24 indices for our 4 polygons.
  296. for (int sideNum = 0; sideNum < 4; sideNum++)
  297. {
  298. // Each side needs 2 polygons.
  299. // 1. Polygon
  300. indices[num * 24 + 6 * sideNum + 0] =
  301. vertexIndex + sideNum;
  302. indices[num * 24 + 6 * sideNum + 1] =
  303. vertexIndex + 5 + 1 + sideNum;
  304. indices[num * 24 + 6 * sideNum + 2] =
  305. vertexIndex + 5 + sideNum;
  306. // 2. Polygon
  307. indices[num * 24 + 6 * sideNum + 3] =
  308. vertexIndex + 5 + 1 + sideNum;
  309. indices[num * 24 + 6 * sideNum + 4] =
  310. vertexIndex + sideNum;
  311. indices[num * 24 + 6 * sideNum + 5] =
  312. vertexIndex + 1 + sideNum;
  313. }
  314. // Go to the next 5 vertices
  315. vertexIndex += 5;
  316. }
  317. // Set road back index buffer
  318. // fix
  319. //roadIb = new IndexBuffer(
  320. // BaseGame.Device,
  321. // typeof(int),
  322. // indices.Length,
  323. // ResourceUsage.WriteOnly,
  324. // ResourceManagementMode.Automatic);
  325. roadIb = new IndexBuffer(
  326. BaseGame.Device,
  327. typeof(int),
  328. indices.Length,
  329. BufferUsage.WriteOnly);
  330. roadIb.SetData(indices);
  331. // We need 4 vertices per cross-section edge of the road back hull
  332. roadBackVertices = new TangentVertex[points.Count * 4];
  333. for (int num = 0; num < points.Count; num++)
  334. {
  335. // Left side of the road
  336. roadBackVertices[num * 4 + 0] =
  337. points[num].LeftTangentVertex;
  338. roadBackVertices[num * 4 + 0].uv = new Vector2(
  339. roadBackVertices[num * 4 + 0].U * RoadBackHullTextureWidthFactor,
  340. 0.0f);
  341. // Left lower side of the road
  342. roadBackVertices[num * 4 + 1] =
  343. points[num].BottomLeftSideTangentVertex;
  344. roadBackVertices[num * 4 + 1].uv = new Vector2(
  345. roadBackVertices[num * 4 + 0].U * RoadBackHullTextureWidthFactor,
  346. RoadBackSideTextureHeight);
  347. // Right lower side of the road
  348. roadBackVertices[num * 4 + 2] =
  349. points[num].BottomRightSideTangentVertex;
  350. roadBackVertices[num * 4 + 2].uv = new Vector2(
  351. roadBackVertices[num * 4 + 0].U * RoadBackHullTextureWidthFactor,
  352. 1.0f - RoadBackSideTextureHeight);
  353. // Right side of the road
  354. roadBackVertices[num * 4 + 3] =
  355. points[num].RightTangentVertex;
  356. roadBackVertices[num * 4 + 3].uv = new Vector2(
  357. roadBackVertices[num * 4 + 3].U * RoadBackHullTextureWidthFactor,
  358. 1.0f);
  359. }
  360. // Set road back vertex buffer
  361. // fix
  362. //roadBackVb = new VertexBuffer(
  363. // BaseGame.Device,
  364. // typeof(TangentVertex),
  365. // roadBackVertices.Length,
  366. // ResourceUsage.WriteOnly,
  367. // ResourceManagementMode.Automatic);
  368. roadBackVb = new VertexBuffer(
  369. BaseGame.Device,
  370. typeof(TangentVertex),
  371. roadBackVertices.Length,
  372. BufferUsage.WriteOnly);
  373. roadBackVb.SetData(roadBackVertices);
  374. // Also calculate all indices, we have 6 polygons for each segment with
  375. // 3 vertices each. We got 1 segment less than points because the
  376. // last point is duplicated (different tex coords).
  377. int[] backIndices = new int[(points.Count - 1) * 6 * 3];
  378. vertexIndex = 0;
  379. for (int num = 0; num < points.Count - 1; num++)
  380. {
  381. // We only use 4 vertices (and the next 4 vertices),
  382. // but we have to construct all 18 indices for our 6 polygons.
  383. for (int sideNum = 0; sideNum < 3; sideNum++)
  384. {
  385. // Each side needs 2 polygons.
  386. // 1. Polygon
  387. backIndices[num * 18 + 6 * sideNum + 0] =
  388. vertexIndex + sideNum;
  389. backIndices[num * 18 + 6 * sideNum + 1] =
  390. vertexIndex + 5 + sideNum;
  391. backIndices[num * 18 + 6 * sideNum + 2] =
  392. vertexIndex + 4 + sideNum;
  393. // 2. Polygon
  394. backIndices[num * 18 + 6 * sideNum + 3] =
  395. vertexIndex + 5 + sideNum;
  396. backIndices[num * 18 + 6 * sideNum + 4] =
  397. vertexIndex + sideNum;
  398. backIndices[num * 18 + 6 * sideNum + 5] =
  399. vertexIndex + 1 + sideNum;
  400. }
  401. // Go to the next 4 vertices
  402. vertexIndex += 4;
  403. }
  404. // Set road back index buffer
  405. // fix
  406. //roadBackIb = new IndexBuffer(
  407. // BaseGame.Device,
  408. // typeof(int),
  409. // backIndices.Length,
  410. // ResourceUsage.WriteOnly,
  411. // ResourceManagementMode.Automatic);
  412. roadBackIb = new IndexBuffer(
  413. BaseGame.Device,
  414. typeof(int),
  415. backIndices.Length,
  416. BufferUsage.WriteOnly);
  417. roadBackIb.SetData(backIndices);
  418. // Only generate tunnels for the parts were we want to have tunnels for.
  419. int totalTunnelLength = 0;
  420. foreach (RoadHelperPosition tunnelPos in helperPositions)
  421. if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel)
  422. totalTunnelLength += 1 + (tunnelPos.endNum - tunnelPos.startNum);
  423. // Lets use 4 vertices per segment, we could improve that later
  424. // by adding more vertices for a round tunnel.
  425. roadTunnelVertices = new TangentVertex[totalTunnelLength * 4];
  426. vertexIndex = 0;
  427. foreach (RoadHelperPosition tunnelPos in helperPositions)
  428. if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel)
  429. for (int num = tunnelPos.startNum; num <= tunnelPos.endNum; num++)
  430. {
  431. // Left side of the road
  432. roadTunnelVertices[vertexIndex + 0] =
  433. points[num].LeftTangentVertex;
  434. roadTunnelVertices[vertexIndex + 0].uv = new Vector2(
  435. roadTunnelVertices[vertexIndex + 0].U
  436. * RoadTunnelTextureWidthFactor, 0.0f);
  437. // Left top side of the road
  438. roadTunnelVertices[vertexIndex + 1] =
  439. points[num].TunnelTopLeftSideTangentVertex;
  440. roadTunnelVertices[vertexIndex + 1].uv = new Vector2(
  441. roadTunnelVertices[vertexIndex + 1].U *
  442. RoadTunnelTextureWidthFactor, RoadTunnelSideTextureHeight);
  443. // Right top side of the road
  444. roadTunnelVertices[vertexIndex + 2] =
  445. points[num].TunnelTopRightSideTangentVertex;
  446. roadTunnelVertices[vertexIndex + 2].uv = new Vector2(
  447. roadTunnelVertices[vertexIndex + 2].U *
  448. RoadTunnelTextureWidthFactor,
  449. 1.0f - RoadTunnelSideTextureHeight);
  450. // Right side of the road
  451. roadTunnelVertices[vertexIndex + 3] =
  452. points[num].RightTangentVertex;
  453. roadTunnelVertices[vertexIndex + 3].uv = new Vector2(
  454. roadTunnelVertices[vertexIndex + 3].U *
  455. RoadTunnelTextureWidthFactor, 1.0f);
  456. // Adjust normals for the 2 lower points
  457. roadTunnelVertices[vertexIndex + 0].normal *= -1;
  458. roadTunnelVertices[vertexIndex + 3].normal *= -1;
  459. roadTunnelVertices[vertexIndex + 0].tangent *= -1;
  460. roadTunnelVertices[vertexIndex + 3].tangent *= -1;
  461. vertexIndex += 4;
  462. }
  463. // Set road back vertex buffer
  464. if (roadTunnelVertices.Length > 0)
  465. {
  466. // fix
  467. //roadTunnelVb = new VertexBuffer(
  468. // BaseGame.Device,
  469. // typeof(TangentVertex),
  470. // roadTunnelVertices.Length,
  471. // ResourceUsage.WriteOnly,
  472. // ResourceManagementMode.Automatic);
  473. roadTunnelVb = new VertexBuffer(
  474. BaseGame.Device,
  475. typeof(TangentVertex),
  476. roadTunnelVertices.Length,
  477. BufferUsage.WriteOnly);
  478. roadTunnelVb.SetData(roadTunnelVertices);
  479. // Also calculate all indices, we have 6 polygons for each segment with
  480. // 3 vertices each. We got 1 segment less than points because the
  481. // last point is duplicated (different tex coords).
  482. int totalIndices = 0;
  483. foreach (RoadHelperPosition tunnelPos in helperPositions)
  484. if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel)
  485. totalIndices += (tunnelPos.endNum - tunnelPos.startNum);
  486. roadTunnelIndices = new int[totalIndices * 6 * 3];
  487. vertexIndex = 0;
  488. int tunnelIndex = 0;
  489. foreach (RoadHelperPosition tunnelPos in helperPositions)
  490. if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel)
  491. {
  492. for (int num = tunnelPos.startNum; num < tunnelPos.endNum; num++)
  493. {
  494. // We only use 4 vertices (and the next 4 vertices),
  495. // but we have to construct all 18 indices for our 6 polygons.
  496. for (int sideNum = 0; sideNum < 3; sideNum++)
  497. {
  498. // Each side needs 2 polygons.
  499. // Note: This polygons are rendered with culling off because
  500. // we want to see the inside and outside of the tunnel.
  501. // 1. Polygon
  502. roadTunnelIndices[tunnelIndex + 0] =
  503. vertexIndex + sideNum;
  504. roadTunnelIndices[tunnelIndex + 2] =
  505. vertexIndex + 4 + sideNum;
  506. roadTunnelIndices[tunnelIndex + 1] =
  507. vertexIndex + 5 + sideNum;
  508. // 2. Polygon
  509. roadTunnelIndices[tunnelIndex + 3] =
  510. vertexIndex + 5 + sideNum;
  511. roadTunnelIndices[tunnelIndex + 5] =
  512. vertexIndex + 1 + sideNum;
  513. roadTunnelIndices[tunnelIndex + 4] =
  514. vertexIndex + sideNum;
  515. tunnelIndex += 6;
  516. }
  517. // Go to the next 4 vertices
  518. vertexIndex += 4;
  519. }
  520. // Skip 4 vertices till the next tunnel
  521. vertexIndex += 4;
  522. }
  523. // Set road back index buffer
  524. // fix
  525. //roadTunnelIb = new IndexBuffer(
  526. // BaseGame.Device,
  527. // typeof(int),
  528. // roadTunnelIndices.Length,
  529. // ResourceUsage.WriteOnly,
  530. // ResourceManagementMode.Automatic);
  531. roadTunnelIb = new IndexBuffer(
  532. BaseGame.Device,
  533. typeof(int),
  534. roadTunnelIndices.Length,
  535. BufferUsage.WriteOnly);
  536. roadTunnelIb.SetData(roadTunnelIndices);
  537. }
  538. leftRail = new GuardRail(points, GuardRail.Modes.Left, landscape);
  539. rightRail = new GuardRail(points, GuardRail.Modes.Right, landscape);
  540. columns = new TrackColumns(points, landscape);
  541. GenerateObjectsForTrack(landscape);
  542. }
  543. private void GenerateObjectsForTrack(Landscape landscape)
  544. {
  545. // Auto generate palms and laterns at the side of our road.
  546. float lastGap = 0;//PalmAndLaternGap;
  547. int generatedNum = 0;
  548. for (int num = 0; num < points.Count; num++)
  549. {
  550. bool palms = false;
  551. bool laterns = false;
  552. // Check if there are any palms or laterns here
  553. foreach (RoadHelperPosition helper in helperPositions)
  554. if (num >= helper.startNum &&
  555. num <= helper.endNum)
  556. {
  557. if (helper.type == TrackData.RoadHelper.HelperType.Palms)
  558. palms = true;
  559. else if (helper.type == TrackData.RoadHelper.HelperType.Laterns)
  560. laterns = true;
  561. }
  562. // No palms or laterns here?
  563. if (palms == false &&
  564. laterns == false)
  565. // Then skip
  566. continue;
  567. // Distance of the current position to the next position
  568. float distance = Vector3.Distance(
  569. points[(num + 1) % points.Count].pos, points[num].pos);
  570. // Have we reach or go over the holder gap ?
  571. if (lastGap - distance <= 0)
  572. {
  573. // The unit vectors for our local point space
  574. Vector3 right = points[num].right;
  575. Vector3 dir = points[num].dir;
  576. Vector3 up = points[num].up;
  577. // Find out if this is a looping
  578. bool upsideDown = up.Z < +0.05f;
  579. bool movingUp = dir.Z > 0.65f;
  580. bool movingDown = dir.Z < -0.65f;
  581. if (upsideDown || movingUp || movingDown)
  582. // Skip generation here!
  583. continue;
  584. // Create the coordinate system for the current point by the 3 unit
  585. // vectors.
  586. Matrix pointSpace = Matrix.Identity;
  587. pointSpace.Right = right;
  588. pointSpace.Up = dir;
  589. pointSpace.Forward = -up;
  590. // Catmull interpolation, instead the linear interpolation, for a
  591. // better position calculation, especially in curves.
  592. Vector3 p1 = points[num - 1 < 0 ? points.Count - 1 : num - 1].pos;
  593. Vector3 p2 = points[num].pos;
  594. Vector3 p3 = points[(num + 1) % points.Count].pos;
  595. Vector3 p4 = points[(num + 2) % points.Count].pos;
  596. Vector3 objPoint = Vector3.CatmullRom(p1, p2, p3, p4,
  597. lastGap / distance);
  598. generatedNum++;
  599. // Store the calculated render matrix for this holder pile object
  600. if (landscape != null)
  601. {
  602. if (palms)
  603. {
  604. // Check height,
  605. // skip palm generation if height is more than 11m
  606. if (objPoint.Z -
  607. landscape.GetMapHeight(objPoint.X, objPoint.Y) < 11)
  608. {
  609. int randomNum = RandomHelper.GetRandomInt(4);
  610. // Less propability for small palm
  611. if (randomNum == 3)
  612. randomNum = RandomHelper.GetRandomInt(4);
  613. landscape.AddObjectToRender(
  614. // Random palms
  615. randomNum == 0 ? "AlphaPalm" :
  616. randomNum == 1 ? "AlphaPalm2" :
  617. randomNum == 2 ? "AlphaPalm3" : "AlphaPalmSmall",
  618. // Scale them up a little
  619. Matrix.CreateScale(1.25f) *
  620. // Randomly rotate palms
  621. Matrix.CreateRotationZ(
  622. RandomHelper.GetRandomFloat(0, MathHelper.Pi * 2)) *
  623. // Put left/right
  624. Matrix.CreateTranslation(right *
  625. (generatedNum % 2 == 0 ? 0.6f : -0.6f) *
  626. points[num].roadWidth * TrackVertex.RoadWidthScale)
  627. // Put below the landscape to make it stick
  628. // to the ground
  629. * Matrix.CreateTranslation(new Vector3(0, 0, -50)) *
  630. // And finally we calculate to correct position where
  631. // the palm reaches exactly the holder gap
  632. Matrix.CreateTranslation(objPoint),
  633. // Enable this for shadow map generation
  634. true);
  635. }
  636. }
  637. else
  638. {
  639. landscape.AddObjectToRender(
  640. // Random palms or laterns?
  641. "Laterne",
  642. // Rotate laterns fixed left/right
  643. Matrix.CreateRotationZ(
  644. generatedNum % 2 == 0 ? MathHelper.Pi : 0.0f) *
  645. // Put left/right
  646. Matrix.CreateTranslation(new Vector3(
  647. (generatedNum % 2 == 0 ? 0.5f : -0.5f) *
  648. points[num].roadWidth *
  649. TrackVertex.RoadWidthScale - 0.35f, 0, -0.2f)) *
  650. // the ordinary transformation to the
  651. // current point space
  652. pointSpace *
  653. // At last we calculate to correct position where the
  654. // latern reaches exactly the holder gap
  655. Matrix.CreateTranslation(objPoint),
  656. // Enable this for shadow map generation
  657. true);
  658. }
  659. }
  660. // We have just set a pile, the next pile will be set after
  661. // reaching the next holder gap.
  662. lastGap += PalmAndLaternGap;
  663. }
  664. // The distance we have to cover until the next position.
  665. // We subtract our current distance from the remaining gap distance,
  666. // which will then be checked in the next loop.
  667. lastGap -= distance;
  668. }
  669. // Add the goal and start light models always!
  670. if (landscape != null)
  671. {
  672. Vector3 startRight = points[0].right;
  673. Vector3 startDir = points[0].dir;
  674. Vector3 startUp = points[0].up;
  675. Matrix startPointSpace = Matrix.Identity;
  676. startPointSpace.Right = startRight;
  677. startPointSpace.Up = startDir;
  678. startPointSpace.Forward = -startUp;
  679. landscape.AddObjectToRender(
  680. // Use RacingGame banners
  681. "Banner6",
  682. // Scale them up to fit on the road
  683. Matrix.CreateScale(points[0].roadWidth) *
  684. // Scale up 1.1, but compensate 1.2f done in landscape.AddObject
  685. Matrix.CreateScale(1.051f) *
  686. Matrix.CreateTranslation(new Vector3(0, -5.1f, 0)) *
  687. // the ordinary transformation to the current point space
  688. startPointSpace *
  689. // Add the correct position where the goal is
  690. Matrix.CreateTranslation(points[0].pos),
  691. // Enable this for shadow map generation
  692. true);
  693. landscape.AddObjectToRender(
  694. // All 3 modes are handled and updated in BasePlayer class
  695. "StartLight3",
  696. Matrix.CreateScale(1.1f) *
  697. // Put startlight 6 meters away, and on the right road side!
  698. Matrix.CreateTranslation(new Vector3(
  699. points[0].roadWidth * TrackVertex.RoadWidthScale * 0.50f - 0.3f,
  700. 6, -0.2f)) *
  701. // the ordinary transformation to the current point space
  702. startPointSpace *
  703. // Add the correct position where the goal is
  704. Matrix.CreateTranslation(points[0].pos),
  705. // Enable this for shadow map generation
  706. true);
  707. }
  708. // Make sure we don't reuse any of the old checkpoint positions.
  709. checkpointSegmentPositions.Clear();
  710. // Auto generate checkpoints every 500 meters.
  711. lastGap = CheckpointGap;
  712. float signGap = SignGap;
  713. // Don't add another one near the end!
  714. for (int num = 0; num < points.Count - 24; num++)
  715. {
  716. // Distance of the current position to the next position
  717. float distance = Vector3.Distance(
  718. points[(num + 1) % points.Count].pos, points[num].pos);
  719. // The unit vectors for our local point space
  720. Vector3 right = points[num].right;
  721. Vector3 dir = points[num].dir;
  722. Vector3 up = points[num].up;
  723. // Find out if this is a looping
  724. bool upsideDown = up.Z < +0.05f;
  725. bool movingUp = dir.Z > 0.65f;
  726. bool movingDown = dir.Z < -0.65f;
  727. if (upsideDown || movingUp || movingDown)
  728. // Skip generation here!
  729. continue;
  730. // Create the coordinate system for the current point by the 3 unit
  731. // vectors.
  732. Matrix pointSpace = Matrix.Identity;
  733. pointSpace.Right = right;
  734. pointSpace.Up = dir;
  735. pointSpace.Forward = -up;
  736. // Catmull interpolation, instead the linear interpolation, for a
  737. // better position calculation, especially in curves.
  738. Vector3 p1 = points[num - 1 < 0 ? points.Count - 1 : num - 1].pos;
  739. Vector3 p2 = points[num].pos;
  740. Vector3 p3 = points[(num + 1) % points.Count].pos;
  741. Vector3 p4 = points[(num + 2) % points.Count].pos;
  742. // Have we reach or go over the holder gap ?
  743. if (lastGap - distance <= 0 &&
  744. landscape != null)
  745. {
  746. Vector3 objPoint = Vector3.CatmullRom(p1, p2, p3, p4,
  747. lastGap / distance);
  748. // Store the calculated render matrix for this holder pile object
  749. int randomNum = RandomHelper.GetRandomInt(6);
  750. landscape.AddObjectToRender(
  751. // Random banners
  752. randomNum == 0 ? "Banner" :
  753. randomNum == 1 ? "Banner2" :
  754. randomNum == 2 ? "Banner3" :
  755. randomNum == 3 ? "Banner4" :
  756. randomNum == 4 ? "Banner5" : "Banner6",
  757. // Scale them up to fit on the road
  758. Matrix.CreateScale(points[num].roadWidth) *
  759. Matrix.CreateTranslation(new Vector3(0, 0, -0.1f)) *
  760. // the ordinary transformation to the current point space
  761. pointSpace *
  762. // And finally we calculate to correct position where the palm
  763. // reaches exactly the holder gap
  764. Matrix.CreateTranslation(objPoint),
  765. // Enable this for shadow map generation
  766. true);
  767. // Remember this segment for easier checking later.
  768. checkpointSegmentPositions.Add(num);
  769. // We have just set a pile, the next pile will be set after
  770. // reaching the next holder gap.
  771. lastGap += CheckpointGap;
  772. }
  773. else if (signGap - distance <= 0 &&
  774. num >= 25 &&
  775. landscape != null)
  776. {
  777. Vector3 objPoint = Vector3.CatmullRom(p1, p2, p3, p4,
  778. signGap / distance);
  779. // Find out how curvy this point is by going back 25 points.
  780. Vector3 backPos = points[(num - 25) % points.Count].pos;
  781. // Calculate virtualBackPos as if the road were straight
  782. bool loopingAhead = points[(num + 60) % points.Count].up.Z < 0.15f;
  783. // Calc angle
  784. Vector3 angleVec = Vector3.Normalize(backPos - points[num].pos);
  785. float roadAngle = Vector3Helper.GetAngleBetweenVectors(
  786. angleVec,
  787. Vector3.Normalize(-points[num].dir));
  788. // If road goes to the left, use negative angle value!
  789. if (Vector3.Distance(points[num].right, angleVec) <
  790. Vector3.Distance(-points[num].right, angleVec))
  791. roadAngle = -roadAngle;
  792. // Now compare, if the backPos is more than 12 meters down,
  793. // add a warning sign.
  794. if (loopingAhead)//backPos.Z > virtualBackPos.Z + 20)
  795. {
  796. landscape.AddObjectToRender(
  797. "SignWarning",
  798. //Matrix.CreateRotationZ(-MathHelper.Pi/2.0f) *
  799. // Put it on the right side
  800. Matrix.CreateTranslation(new Vector3(
  801. points[num].roadWidth *
  802. TrackVertex.RoadWidthScale * 0.5f - 0.1f, 0, -0.25f)) *
  803. // the ordinary transformation to the current point space
  804. pointSpace *
  805. // And finally we calculate to correct position where the obj
  806. // reaches exactly the holder gap
  807. Matrix.CreateTranslation(objPoint),
  808. // Enable this for shadow map generation
  809. true);
  810. }
  811. // Else check if the angle less than -24 degrees (pi/7.5)
  812. else if (roadAngle < -MathHelper.Pi / 7.5f)
  813. {
  814. // Show right road sign
  815. landscape.AddObjectToRender(
  816. "SignCurveRight",
  817. Matrix.CreateRotationZ(MathHelper.Pi / 2.0f) *
  818. // Put it on the left side
  819. Matrix.CreateTranslation(new Vector3(
  820. -points[num].roadWidth * TrackVertex.RoadWidthScale
  821. * 0.5f - 0.15f, 0, -0.25f)) *
  822. // the ordinary transformation to the current point space
  823. pointSpace *
  824. // And finally we calculate to correct position where the obj
  825. // reaches exactly the holder gap
  826. Matrix.CreateTranslation(objPoint),
  827. // Enable this for shadow map generation
  828. true);
  829. }
  830. // Same for other side
  831. else if (roadAngle > MathHelper.Pi / 7.5f)
  832. {
  833. // Show right road sign
  834. landscape.AddObjectToRender(
  835. "SignCurveLeft",
  836. Matrix.CreateRotationZ(-MathHelper.Pi / 2.0f) *
  837. // Put it on the right side
  838. Matrix.CreateTranslation(new Vector3(
  839. points[num].roadWidth * TrackVertex.RoadWidthScale
  840. * 0.5f - 0.15f, 0, -0.25f)) *
  841. // the ordinary transformation to the current point space
  842. pointSpace *
  843. // And finally we calculate to correct position where the obj
  844. // reaches exactly the holder gap
  845. Matrix.CreateTranslation(objPoint),
  846. // Enable this for shadow map generation
  847. true);
  848. }
  849. // Also generate banner signs if roadAngle is at least 18 degrees
  850. else if (roadAngle < -MathHelper.Pi / 10.0f ||
  851. roadAngle > MathHelper.Pi / 10.0f ||
  852. // Randomly generate sign
  853. RandomHelper.GetRandomInt(9) == 4)
  854. {
  855. // Also mix in random curve signs, this is still a curve
  856. int rndValue = RandomHelper.GetRandomInt(3);
  857. // Randomize again if not that curvy here
  858. if (rndValue == 0 &&
  859. Math.Abs(roadAngle) < MathHelper.Pi / 24)
  860. rndValue = RandomHelper.GetRandomInt(3);
  861. else if (Math.Abs(roadAngle) < MathHelper.Pi / 20 &&
  862. RandomHelper.GetRandomInt(2) == 1)
  863. roadAngle *= -1;
  864. // Show right road sign
  865. landscape.AddObjectToRender(
  866. rndValue == 0 ?
  867. (roadAngle > 0 ? "SignCurveLeft" : "SignCurveRight") :
  868. (rndValue == 1 ? "Sign" : "Sign2"),
  869. Matrix.CreateRotationZ(
  870. (roadAngle > 0 ? -1 : 1) * MathHelper.Pi / 2.0f) *
  871. // Put it on the left side
  872. Matrix.CreateTranslation(new Vector3(
  873. (roadAngle > 0 ? 1 : -1) *
  874. points[num].roadWidth * TrackVertex.RoadWidthScale * 0.5f -
  875. (rndValue == 0 ? 0.15f : 0.005f),
  876. 0, -0.25f)) *
  877. // the ordinary transformation to the current point space
  878. pointSpace *
  879. // And finally we calculate to correct position where the obj
  880. // reaches exactly the holder gap
  881. Matrix.CreateTranslation(objPoint),
  882. // Enable this for shadow map generation
  883. true);
  884. }
  885. // We have just set a sign (or not), check for next sign after gap.
  886. signGap += SignGap;
  887. }
  888. // The distance we have to cover until the next position.
  889. // We subtract our current distance from the remaining gap distance,
  890. // which will then be checked in the next loop.
  891. lastGap -= distance;
  892. signGap -= distance;
  893. }
  894. // Randomly generate, but don't collide with existing objects
  895. // or the track!
  896. for (int num = 0; num < points.Count; num += 2)
  897. {
  898. if (landscape != null)
  899. {
  900. // Get landscape height here
  901. float landscapeHeight =
  902. landscape.GetMapHeight(points[num].pos.X, points[num].pos.Y);
  903. // Skip object generation at great heights!
  904. if (points[num].pos.Z - landscapeHeight > 60.0f)
  905. continue;
  906. }
  907. // The unit vectors for our local point space
  908. Vector3 right = points[num].right;
  909. Vector3 dir = points[num].dir;
  910. Vector3 up = points[num].up;
  911. // Find out if this is a looping
  912. bool upsideDown = up.Z < +0.05f;
  913. bool movingUp = dir.Z > 0.65f;
  914. bool movingDown = dir.Z < -0.65f;
  915. if (upsideDown || movingUp || movingDown)
  916. // Skip generation here!
  917. continue;
  918. // Show twice as many objects in high details mode
  919. int randomMaxPropability;
  920. if (BaseGame.HighDetail)
  921. randomMaxPropability = 5;
  922. else
  923. randomMaxPropability = 10;
  924. // Generate stuff in 20% of the cases
  925. if (RandomHelper.GetRandomInt(randomMaxPropability) == 0 &&
  926. landscape != null)
  927. {
  928. // Get random name
  929. int randomObjNum = RandomHelper.GetRandomInt(
  930. landscape.autoGenerationNames.Length);
  931. // If above 6, generate again
  932. if (randomObjNum >= 6)
  933. randomObjNum = RandomHelper.GetRandomInt(
  934. landscape.autoGenerationNames.Length);
  935. // Don't generate so many casinos
  936. if (randomObjNum == landscape.autoGenerationNames.Length - 1 &&
  937. RandomHelper.GetRandomInt(3) < 2)
  938. randomObjNum = RandomHelper.GetRandomInt(
  939. landscape.autoGenerationNames.Length);
  940. // Ok, generate
  941. float distance = RandomHelper.GetRandomFloat(26, 88);
  942. // For casinos make sure the object is far enough away.
  943. if (randomObjNum == landscape.autoGenerationNames.Length - 1)
  944. distance += 20;
  945. bool side = RandomHelper.GetRandomInt(2) == 0;
  946. float rotation = RandomHelper.GetRandomFloat(0, MathHelper.Pi * 2);
  947. landscape.AddObjectToRender(
  948. landscape.autoGenerationNames[randomObjNum],
  949. rotation,
  950. points[num].pos,
  951. points[num].right, distance * (side ? 1 : -1));
  952. }
  953. }
  954. }
  955. /// <summary>
  956. /// Dispose
  957. /// </summary>
  958. public void Dispose()
  959. {
  960. Dispose(true);
  961. GC.SuppressFinalize(this);
  962. }
  963. /// <summary>
  964. /// Dispose
  965. /// </summary>
  966. /// <param name="disposing">Disposing</param>
  967. protected virtual void Dispose(bool disposing)
  968. {
  969. if (disposing)
  970. {
  971. roadMaterial.Dispose();
  972. roadBackMaterial.Dispose();
  973. roadTunnelMaterial.Dispose();
  974. roadCementMaterial.Dispose();
  975. guardRailMaterial.Dispose();
  976. roadVb.Dispose();
  977. roadIb.Dispose();
  978. roadBackVb.Dispose();
  979. roadBackIb.Dispose();
  980. roadTunnelVb.Dispose();
  981. roadTunnelIb.Dispose();
  982. leftRail.Dispose();
  983. rightRail.Dispose();
  984. columns.Dispose();
  985. }
  986. }
  987. /// <summary>
  988. /// Render
  989. /// </summary>
  990. public void Render()
  991. {
  992. // We use tangent vertices for everything here
  993. // Restore the world matrix
  994. BaseGame.WorldMatrix = Matrix.Identity;
  995. // Make sure Anisotropic filtering is enabled for the road
  996. BaseGame.Device.SamplerStates[0] = SamplerState.AnisotropicWrap;
  997. //BaseGame.Device.SamplerStates[0].MinFilter = TextureFilter.Anisotropic;
  998. //BaseGame.Device.SamplerStates[0].MagFilter = TextureFilter.Anisotropic;
  999. //BaseGame.Device.SamplerStates[0].MipFilter = TextureFilter.Linear;
  1000. //BaseGame.Device.SamplerStates[0].MaxAnisotropy = 8; // 8 is enough, isn't it?
  1001. // Render the road itself
  1002. ShaderEffect.normalMapping.Render(
  1003. roadMaterial,
  1004. // Use antrisopic filtering only if we have a fast GPU
  1005. BaseGame.HighDetail ?
  1006. "SpecularRoad20" :
  1007. "Specular20",
  1008. new BaseGame.RenderHandler(RenderRoadVertices));
  1009. // Render the road back hull
  1010. ShaderEffect.normalMapping.Render(
  1011. roadBackMaterial,
  1012. // Use antrisopic filtering only if we have a fast GPU
  1013. BaseGame.HighDetail ?
  1014. "SpecularRoad20" :
  1015. "Specular20",
  1016. new BaseGame.RenderHandler(RenderRoadBackVertices));
  1017. // Render all tunnels (culling off)
  1018. if (roadTunnelVb != null)
  1019. {
  1020. ShaderEffect.normalMapping.Render(
  1021. roadTunnelMaterial,
  1022. "Diffuse20",
  1023. new BaseGame.RenderHandler(RenderRoadTunnelVertices));
  1024. }
  1025. // Render all guard rails
  1026. leftRail.Render(guardRailMaterial);
  1027. rightRail.Render(guardRailMaterial);
  1028. // Render all columns
  1029. columns.Render(roadCementMaterial);
  1030. }
  1031. /// <summary>
  1032. /// Render road vertices
  1033. /// </summary>
  1034. private void RenderRoadVertices()
  1035. {
  1036. BaseGame.Device.SetVertexBuffer(roadVb);
  1037. BaseGame.Device.Indices = roadIb;
  1038. BaseGame.Device.DrawIndexedPrimitives(PrimitiveType.TriangleList,
  1039. 0, 0, (points.Count - 1) * 8);
  1040. }
  1041. /// <summary>
  1042. /// Render road back vertices
  1043. /// </summary>
  1044. private void RenderRoadBackVertices()
  1045. {
  1046. BaseGame.Device.SetVertexBuffer(roadBackVb);
  1047. BaseGame.Device.Indices = roadBackIb;
  1048. BaseGame.Device.DrawIndexedPrimitives(PrimitiveType.TriangleList,
  1049. 0, 0, (points.Count - 1) * 6);
  1050. }
  1051. /// <summary>
  1052. /// Render road tunnel vertices
  1053. /// </summary>
  1054. private void RenderRoadTunnelVertices()
  1055. {
  1056. if (roadTunnelVb == null)
  1057. return;
  1058. // Disable culling (render tunnel from both sides)
  1059. BaseGame.Device.RasterizerState = RasterizerState.CullNone;
  1060. // Render vertices
  1061. BaseGame.Device.SetVertexBuffer(roadTunnelVb);
  1062. BaseGame.Device.Indices = roadTunnelIb;
  1063. BaseGame.Device.DrawIndexedPrimitives(PrimitiveType.TriangleList,
  1064. 0, 0, roadTunnelIndices.Length / 3);
  1065. // Restore culling (default is always counter clockwise)
  1066. BaseGame.Device.RasterizerState = RasterizerState.CullCounterClockwise;
  1067. }
  1068. /// <summary>
  1069. /// Generate shadow
  1070. /// </summary>
  1071. public void GenerateShadow()
  1072. {
  1073. // Generate shadows for the road and the tunnel, ignore the road back
  1074. ShaderEffect.shadowMapping.UpdateGenerateShadowWorldMatrix(
  1075. Matrix.Identity);
  1076. // We use tangent vertices for everything here
  1077. // Disable culling (render road and tunnel from both sides,
  1078. // this gives correct shadows to loopings, tunnels and overlappings)
  1079. BaseGame.Device.RasterizerState = RasterizerState.CullNone;
  1080. // Render road and tunnels
  1081. RenderRoadVertices();
  1082. RenderRoadTunnelVertices();
  1083. // Generate shadows for both rails
  1084. leftRail.GenerateShadow();
  1085. rightRail.GenerateShadow();
  1086. // And for all columns
  1087. //not required, we don't see near columns anyway:
  1088. //columns.GenerateShadow();
  1089. }
  1090. /// <summary>
  1091. /// Use shadow
  1092. /// </summary>
  1093. public void UseShadow()
  1094. {
  1095. // Receive shadow on the landscape, just render it out.
  1096. ShaderEffect.shadowMapping.UpdateCalcShadowWorldMatrix(
  1097. Matrix.Identity);
  1098. RenderRoadVertices();
  1099. // Tunnel shadows are kinda important ^^
  1100. RenderRoadTunnelVertices();
  1101. // Guard rails do not need to receive shadow often, but it will look
  1102. // very wrong if the car does not throw shadows at the guard rails.
  1103. // For that reason lets include them.
  1104. leftRail.UseShadow();
  1105. rightRail.UseShadow();
  1106. // And for all columns
  1107. //not required, we don't see near columns anyway:
  1108. //columns.UseShadow();
  1109. }
  1110. /// <summary>
  1111. /// Get track position matrix, put in a value between 0 and 1 and
  1112. /// you get a position on the track (0=start, 1=end).
  1113. /// </summary>
  1114. /// <param name="trackPositionPercent">Track position percent</param>
  1115. /// <param name="roadWidth">Road width</param>
  1116. /// <param name="nextRoadWidth">Next road width</param>
  1117. /// <returns>Matrix</returns>
  1118. public Matrix GetTrackPositionMatrix(float trackPositionPercent,
  1119. out float roadWidth, out float nextRoadWidth)
  1120. {
  1121. // Make sure we are between 0 and 1
  1122. while (trackPositionPercent < 0)
  1123. trackPositionPercent += 1;
  1124. while (trackPositionPercent > 1)
  1125. trackPositionPercent -= 1;
  1126. int num = ((int)(trackPositionPercent * points.Count)) % points.Count;
  1127. // Get position with catmul rom spline
  1128. TrackVertex p1 = points[num - 1 < 0 ? points.Count - 1 : num - 1];
  1129. TrackVertex p2 = points[num];
  1130. TrackVertex p3 = points[(num + 1) % points.Count];
  1131. TrackVertex p4 = points[(num + 2) % points.Count];
  1132. float eachPointPercent = 1.0f / (float)points.Count;
  1133. float pointPercent =
  1134. (trackPositionPercent - num * eachPointPercent) / eachPointPercent;
  1135. //dunno why this is bumpy
  1136. //Log.Write("pointPercent="+pointPercent);
  1137. Vector3 interpolatedPos = Vector3.CatmullRom(
  1138. p1.pos, p2.pos, p3.pos, p4.pos, pointPercent);
  1139. Vector3 interpolatedDir = Vector3.CatmullRom(
  1140. p1.dir, p2.dir, p3.dir, p4.dir, pointPercent);
  1141. Vector3 interpolatedRight = Vector3.CatmullRom(
  1142. p1.right, p2.right, p3.right, p4.right, pointPercent);
  1143. Vector3 interpolatedUp = Vector3.CatmullRom(
  1144. p1.up, p2.up, p3.up, p4.up, pointPercent);
  1145. // Build matrix with interpolated values and return it
  1146. Matrix mat = Matrix.Identity;
  1147. mat.Right = interpolatedRight;
  1148. mat.Up = interpolatedUp;
  1149. mat.Forward = interpolatedDir;
  1150. mat.Translation = interpolatedPos;
  1151. roadWidth = MathHelper.Lerp(p2.roadWidth, p3.roadWidth, pointPercent) *
  1152. TrackVertex.RoadWidthScale;
  1153. nextRoadWidth = p4.roadWidth * TrackVertex.RoadWidthScale;
  1154. return mat;// *
  1155. //Matrix.CreateTranslation(interpolatedPos);
  1156. }
  1157. /// <summary>
  1158. /// Get track position matrix
  1159. /// </summary>
  1160. /// <param name="trackSegmentNum">Track segment number</param>
  1161. /// <param name="trackSegmentPercent">Track segment percent</param>
  1162. /// <param name="roadWidth">Road width</param>
  1163. /// <param name="nextRoadWidth">Next road width</param>
  1164. /// <returns>Matrix</returns>
  1165. public Matrix GetTrackPositionMatrix(
  1166. int trackSegmentNum, float trackSegmentPercent,
  1167. out float roadWidth, out float nextRoadWidth)
  1168. {
  1169. // Make sure we are between 0 and 1
  1170. if (trackSegmentPercent < 0)
  1171. trackSegmentPercent = 0;
  1172. if (trackSegmentPercent > 1)
  1173. trackSegmentPercent = 1;
  1174. float pointPercent = trackSegmentPercent;
  1175. int num = trackSegmentNum % points.Count;
  1176. // Get position with catmul rom spline
  1177. TrackVertex p1 = points[num - 1 < 0 ? points.Count - 1 : num - 1];
  1178. TrackVertex p2 = points[num];
  1179. TrackVertex p3 = points[(num + 1) % points.Count];
  1180. TrackVertex p4 = points[(num + 2) % points.Count];
  1181. Vector3 interpolatedPos = Vector3.CatmullRom(
  1182. p1.pos, p2.pos, p3.pos, p4.pos, pointPercent);
  1183. Vector3 interpolatedDir = Vector3.CatmullRom(
  1184. p1.dir, p2.dir, p3.dir, p4.dir, pointPercent);
  1185. Vector3 interpolatedRight = Vector3.CatmullRom(
  1186. p1.right, p2.right, p3.right, p4.right, pointPercent);
  1187. Vector3 interpolatedUp = Vector3.CatmullRom(
  1188. p1.up, p2.up, p3.up, p4.up, pointPercent);
  1189. // Build matrix with interpolated values and return it
  1190. Matrix mat = Matrix.Identity;
  1191. mat.Right = interpolatedRight;
  1192. mat.Up = interpolatedUp;
  1193. mat.Forward = interpolatedDir;
  1194. mat.Translation = interpolatedPos;
  1195. roadWidth = MathHelper.Lerp(p2.roadWidth, p3.roadWidth, pointPercent) *
  1196. TrackVertex.RoadWidthScale;
  1197. nextRoadWidth = //p4.roadWidth * TrackVertex.RoadWidthScale;
  1198. MathHelper.Lerp(p3.roadWidth, p4.roadWidth, pointPercent) *
  1199. TrackVertex.RoadWidthScale;
  1200. return mat;
  1201. }
  1202. /// <summary>
  1203. /// Update car track position
  1204. /// </summary>
  1205. /// <param name="carPos">Car position</param>
  1206. /// <param name="trackSegmentNumber">Track segment number</param>
  1207. /// <param name="trackPositionPercent">Track position percent</param>
  1208. public void UpdateCarTrackPosition(
  1209. Vector3 carPos,
  1210. ref int trackSegmentNumber, ref float trackSegmentPercent)
  1211. {
  1212. // Make sure trackSegmentNumber is valid, its also easier working with
  1213. // num instead of the long name trackSegmentNumber.
  1214. int num = trackSegmentNumber;
  1215. // Check until car is between trackSegmentNumber and the next segemnt
  1216. bool gotCarInThisSegment = false;
  1217. float thisPointDist = 0;
  1218. float nextPointDist = 1;
  1219. int maxNumberOfIterations = 100;
  1220. do
  1221. {
  1222. TrackVertex thisPoint = points[num];
  1223. TrackVertex nextPoint = points[(num + 1) % points.Count];
  1224. // First check if car is behind trackSegmentNumber
  1225. thisPointDist = Vector3Helper.SignedDistanceToPlane(carPos,
  1226. thisPoint.pos, -thisPoint.dir);
  1227. nextPointDist = Vector3Helper.SignedDistanceToPlane(carPos,
  1228. nextPoint.pos, nextPoint.dir);
  1229. if (thisPointDist < 0)
  1230. num--;
  1231. // Then check if we still are inside this segment
  1232. else if (nextPointDist < 0)
  1233. num++;
  1234. else
  1235. // Ok, we got it.
  1236. gotCarInThisSegment = true;
  1237. if (num < 0)
  1238. num = points.Count - 1;
  1239. if (num >= points.Count)
  1240. num = 0;
  1241. // Get outa here if we are above the max. iterations!
  1242. if (maxNumberOfIterations-- < 0)
  1243. return;
  1244. } while (gotCarInThisSegment == false);
  1245. trackSegmentNumber = num;
  1246. // Btw: Is this a tunnel? Then disable lens flares!
  1247. // Check every 10 frames to save a little performance.
  1248. if (BaseGame.TotalFrames % 10 == 0)
  1249. disableLensFlareInTunnel = IsTunnel(num);
  1250. // Also calculate our track segment position
  1251. float segmentLength = thisPointDist + nextPointDist;
  1252. if (segmentLength == 0)
  1253. trackSegmentPercent = 0;
  1254. else
  1255. trackSegmentPercent = thisPointDist / segmentLength;
  1256. }
  1257. /// <summary>
  1258. /// Disable lens flare if we are inside a tunnel, looks wrong
  1259. /// otherwise because we can't do occlusion querying in XNA yet.
  1260. /// </summary>
  1261. internal static bool disableLensFlareInTunnel = false;
  1262. /// <summary>
  1263. /// Is tunnel
  1264. /// </summary>
  1265. /// <param name="trackSegment">Track segment</param>
  1266. /// <returns>Bool</returns>
  1267. public bool IsTunnel(int trackSegment)
  1268. {
  1269. // Check all tunnels
  1270. for (int num = 0; num < helperPositions.Count; num++)
  1271. {
  1272. RoadHelperPosition tunnelPos = helperPositions[num];
  1273. if (tunnelPos.type == TrackData.RoadHelper.HelperType.Tunnel &&
  1274. trackSegment >= tunnelPos.startNum &&
  1275. trackSegment <= tunnelPos.endNum)
  1276. return true;
  1277. }
  1278. // No tunnel found here
  1279. return false;
  1280. }
  1281. }
  1282. }