GuardRail.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. //-----------------------------------------------------------------------------
  2. // GuardRail.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.Shaders;
  14. using Model = RacingGame.Graphics.Model;
  15. using RacingGame.Landscapes;
  16. namespace RacingGame.Tracks
  17. {
  18. /// <summary>
  19. /// Guard rail
  20. /// </summary>
  21. class GuardRail : IDisposable
  22. {
  23. /// <summary>
  24. /// Guard rail vertices, determinted with help of the unit test below!
  25. /// </summary>
  26. readonly TangentVertex[] GuardRailVertices =
  27. new TangentVertex[]
  28. {
  29. // 0
  30. new TangentVertex(
  31. new Vector3(10, 0, -105), new Vector2(0.0f, 1 - 0.442877f),
  32. new Vector3(-0.382683f, 0, -0.923880f), new Vector3(0, -1, 0)),
  33. // 1
  34. new TangentVertex(
  35. new Vector3(20, 0, -105), new Vector2(0.0f, 1 - 0.432881f),
  36. new Vector3(0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)),
  37. // 2
  38. new TangentVertex(
  39. new Vector3(-10, 0, -75), new Vector2(0.0f, 1 - 0.402893f),
  40. new Vector3(0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)),
  41. // 3
  42. new TangentVertex(
  43. new Vector3(-10, 0, -45), new Vector2(0.0f, 1 - 0.372905f),
  44. new Vector3(0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)),
  45. // 4
  46. new TangentVertex(
  47. new Vector3(20, 0, -15), new Vector2(0.0f, 1 - 0.342917f),
  48. new Vector3(0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)),
  49. // 5
  50. new TangentVertex(
  51. new Vector3(20, 0, 15), new Vector2(0.0f, 1 - 0.312929f),
  52. new Vector3(0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)),
  53. // 6
  54. new TangentVertex(
  55. new Vector3(-10, 0, 45), new Vector2(0.0f, 1 - 0.282941f),
  56. new Vector3(0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)),
  57. // 7
  58. new TangentVertex(
  59. new Vector3(-10, 0, 75), new Vector2(0.0f, 1 - 0.252953f),
  60. new Vector3(0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)),
  61. // 8
  62. new TangentVertex(
  63. new Vector3(20, 0, 105), new Vector2(0.0f, 1 - 0.222965f),
  64. new Vector3(0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)),
  65. // 9
  66. new TangentVertex(
  67. new Vector3(10, 0, 105), new Vector2(0.0f, 1 - 0.212969f),
  68. new Vector3(-0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)),
  69. // 10
  70. new TangentVertex(
  71. new Vector3(-20, 0, 75), new Vector2(0.0f, 1 - 0.182981f),
  72. new Vector3(-0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)),
  73. // 11
  74. new TangentVertex(
  75. new Vector3(-20, 0, 45), new Vector2(0.0f, 1 - 0.152993f),
  76. new Vector3(-0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)),
  77. // 12
  78. new TangentVertex(
  79. new Vector3(10, 0, 15), new Vector2(0.0f, 1 - 0.123005f),
  80. new Vector3(-0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)),
  81. // 13
  82. new TangentVertex(
  83. new Vector3(10, 0, -15), new Vector2(0.0f, 1 - 0.093017f),
  84. new Vector3(-0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)),
  85. // 14
  86. new TangentVertex(
  87. new Vector3(-20, 0, -45), new Vector2(0.0f, 1 - 0.063029f),
  88. new Vector3(-0.923880f, 0, 0.382683f), new Vector3(0, -1, 0)),
  89. // 15
  90. new TangentVertex(
  91. new Vector3(-20, 0, -75), new Vector2(0.0f, 1 - 0.033041f),
  92. new Vector3(-0.923880f, 0, -0.382683f), new Vector3(0, -1, 0)),
  93. // 16 (duplicated first to wrap around tex coords)
  94. new TangentVertex(
  95. new Vector3(10, 0, -105), new Vector2(0.0f, 1 - 0.003053f),
  96. new Vector3(-0.382683f, 0, -0.923880f), new Vector3(0, -1, 0)),
  97. };
  98. /// <summary>
  99. /// Downscale factor for the vertices.
  100. /// </summary>
  101. const float CorrectionScale = 0.0019f;
  102. /// <summary>
  103. /// Gap between the seperate piles.
  104. /// </summary>
  105. const float HolderGap = 15.0f;
  106. /// <summary>
  107. /// Guard rail rendering height (little bit above the ground).
  108. /// </summary>
  109. const float GuardRailHeight = 1.35f * 1.5f * 0.425f;
  110. /// <summary>
  111. /// Put guardrails a little bit on the inside of the road.
  112. /// </summary>
  113. public const float InsideRoadDistance = 0.5f;
  114. /// <summary>
  115. /// Correction vector to put holder pile at the correct position.
  116. /// </summary>
  117. static readonly Vector3 HolderPileCorrectionVector =
  118. new Vector3(0.225f, 0, 0);
  119. /// <summary>
  120. /// We got 2 modes for a guard rail, left and right, depending
  121. /// on with side of the road we want to have it.
  122. /// </summary>
  123. public enum Modes
  124. {
  125. Left,
  126. Right,
  127. }
  128. /// <summary>
  129. /// Rail points for the whole guard rail.
  130. /// </summary>
  131. TrackVertex[] railPoints = null;
  132. /// <summary>
  133. /// Rail vertices.
  134. /// </summary>
  135. TangentVertex[] railVertices = null;
  136. /// <summary>
  137. /// Vertex buffer of guard rail.
  138. /// </summary>
  139. VertexBuffer railVb = null;
  140. /// <summary>
  141. /// Index buffer orf guard rail.
  142. /// </summary>
  143. IndexBuffer railIb = null;
  144. /*now done in track class!
  145. /// <summary>
  146. /// Matrix list for each of the holder pile objects we have to render.
  147. /// </summary>
  148. List<Matrix> holderPileMatrices = new List<Matrix>();
  149. */
  150. /// <summary>
  151. /// Create guard rail
  152. /// </summary>
  153. /// <param name="points">Points of the road itself</param>
  154. /// <param name="mode">Mode, left or right</param>
  155. /// <param name="landscape">Landscape</param>
  156. public GuardRail(List<TrackVertex> points, Modes mode,
  157. Landscape landscape)
  158. {
  159. // First generate a list of points at the side of the road where
  160. // we are going to generate all the guard rail vertices.
  161. // Note: We use only half as much points as points provides!
  162. railPoints = new TrackVertex[points.Count / 2 + 1];
  163. for (int num = 0; num < railPoints.Length; num++)
  164. {
  165. // Make sure we have a closed line, we might have to skip the
  166. // last points entry because we devided through 2.
  167. int pointNum = num * 2;
  168. if (pointNum >= points.Count - 1)
  169. pointNum = points.Count - 1;
  170. // Just copy the points over and manipulate the position and the
  171. // right vector depending on which side of the guard rail we are
  172. // going to generate here.
  173. if (mode == Modes.Left)
  174. {
  175. railPoints[num] = points[pointNum].LeftTrackVertex;
  176. // Invert the direction and right vector for the left side
  177. // This makes everything point in the other road direction!
  178. railPoints[num].right = -railPoints[num].right;
  179. railPoints[num].dir = -railPoints[num].dir;
  180. // Move it a little inside the road
  181. railPoints[num].pos -=
  182. railPoints[num].right * InsideRoadDistance;
  183. }
  184. else
  185. {
  186. railPoints[num] = points[pointNum].RightTrackVertex;
  187. // Move it a little inside the road
  188. railPoints[num].pos -=
  189. railPoints[num].right * InsideRoadDistance;
  190. }
  191. }
  192. railVertices =
  193. new TangentVertex[railPoints.Length * GuardRailVertices.Length];
  194. // Current texture coordinate for the guardrail in our current direction.
  195. float uTexValue = 0.5f;
  196. float lastHolderGap = 0;//HolderGap;
  197. for (int num = 0; num < railPoints.Length; num++)
  198. {
  199. // The unit vectors for our local point space
  200. Vector3 right = railPoints[num].right;
  201. Vector3 dir = railPoints[num].dir;
  202. Vector3 up = railPoints[num].up;
  203. // Create the coordinate system for the current point by the 3 unit
  204. // vectors.
  205. Matrix pointSpace = Matrix.Identity;
  206. pointSpace.M11 = right.X;
  207. pointSpace.M12 = right.Y;
  208. pointSpace.M13 = right.Z;
  209. pointSpace.M21 = dir.X;
  210. pointSpace.M22 = dir.Y;
  211. pointSpace.M23 = dir.Z;
  212. pointSpace.M31 = up.X;
  213. pointSpace.M32 = up.Y;
  214. pointSpace.M33 = up.Z;
  215. Vector3 localPos = railPoints[num].pos;
  216. // Adjust the position for the guardrail, put it up a little.
  217. localPos += up * GuardRailHeight;
  218. // Set the beginning- or ending point for the guardrail around the
  219. // current spline position
  220. for (int i = 0; i < GuardRailVertices.Length; i++)
  221. {
  222. // Transform each of our guardrail points by our local point space
  223. // and translate it to our current position.
  224. Vector3 pos = Vector3.Transform(
  225. GuardRailVertices[i].pos * CorrectionScale,
  226. pointSpace * Matrix.CreateTranslation(localPos));
  227. // Also transform our normal and tangent data
  228. Vector3 normal = Vector3.TransformNormal(
  229. // Left side needs inverted normals (side is inverted)
  230. (mode == Modes.Left ? -1 : 1) *
  231. GuardRailVertices[i].normal,
  232. pointSpace);
  233. Vector3 tangent = Vector3.TransformNormal(
  234. -GuardRailVertices[i].tangent,
  235. //GuardRailVertices[i].normal,
  236. //new Vector3(0, 0, -1),
  237. pointSpace);
  238. // Store vertex
  239. railVertices[num * GuardRailVertices.Length + i] =
  240. new TangentVertex(pos,
  241. uTexValue, GuardRailVertices[i].V,
  242. normal, tangent);
  243. }
  244. // Distance of the current position to the next position
  245. float distance = Vector3.Distance(
  246. railPoints[(num + 1) % railPoints.Length].pos, railPoints[num].pos);
  247. // Uniform calculation of the texture coordinates for the guardrail,
  248. // so it doesn't matter if there is a gap of 2 or 200 m
  249. // -> through "1 / HolderGap" we guarantee that the drilling
  250. // (from the texture) is always set in the front of the pile,
  251. // no matter which holder gap is set
  252. // Note: Only display a holder for every 3 texture loops.
  253. uTexValue += (1 / HolderGap) * distance * 2.0f;
  254. // Have we reach or go over the holder gap ?
  255. if (lastHolderGap - distance <= 0)
  256. {
  257. // Catmull interpolation, instead the linear interpolation, for a
  258. // better position calculation, especially in curves
  259. Vector3 p1 =
  260. railPoints[num - 1 < 0 ? railPoints.Length - 1 : num - 1].pos;
  261. Vector3 p2 = railPoints[num].pos;
  262. Vector3 p3 = railPoints[(num + 1) % railPoints.Length].pos;
  263. Vector3 p4 = railPoints[(num + 2) % railPoints.Length].pos;
  264. Vector3 holderPoint = Vector3.CatmullRom(p1, p2, p3, p4,
  265. lastHolderGap / distance);
  266. // Store the calculated render matrix for this holder pile object
  267. if (landscape != null &&
  268. // Completely ignore all guard rails for low detail
  269. // to save performance (few thousand objects per track less)
  270. BaseGame.HighDetail)
  271. landscape.AddObjectToRender(
  272. "GuardRailHolder",
  273. // Fix scaling a little
  274. //Matrix.CreateScale(0.9f) *
  275. Matrix.CreateScale(1.125f) *
  276. // First the translation to get the pile to the back of the
  277. // guardrail and not in the middle
  278. Matrix.CreateTranslation(HolderPileCorrectionVector) *
  279. // the ordinary transformation to the current point space
  280. pointSpace *
  281. // at least we calculate to correct position where the pile
  282. // reaches exactly the holder gap
  283. Matrix.CreateTranslation(holderPoint),
  284. // Optimize performance: Set this to false,
  285. // but then we won't have shadows for the holder piles.
  286. false);//true);
  287. // We have just set a pile, the next pile will be set after
  288. // reaching the next holder gap.
  289. lastHolderGap += HolderGap;
  290. }
  291. // The distance we have to cover until the next position.
  292. // We subtract our current distance from the remaining gap distance,
  293. // which will then be checked in the next loop.
  294. lastHolderGap -= distance;
  295. }
  296. // Create the vertex buffer from our vertices.
  297. //railVb = new VertexBuffer(
  298. // BaseGame.Device,
  299. // typeof(TangentVertex),
  300. // railVertices.Length,
  301. // ResourceUsage.WriteOnly,
  302. // ResourceManagementMode.Automatic);
  303. railVb = new VertexBuffer(
  304. BaseGame.Device,
  305. typeof(TangentVertex),
  306. railVertices.Length,
  307. BufferUsage.WriteOnly);
  308. railVb.SetData(railVertices);
  309. // Count of quads (polygons) which creates the current guardrail segment
  310. int quadPolysPerStrip = GuardRailVertices.Length - 1;
  311. int[] indices =
  312. new int[(2 * 3 * quadPolysPerStrip) * (railPoints.Length - 1)];
  313. // Current vertex index
  314. int vertexIndex = 0;
  315. // Helper variable, current index of the indices list
  316. int indicesIndex = 0;
  317. for (int num = 0; num < railPoints.Length - 1; num++)
  318. {
  319. // Set all quads of the guardrail
  320. for (int j = 0; j < quadPolysPerStrip; j++)
  321. {
  322. indicesIndex = 3 * 2 * (num * quadPolysPerStrip + j);
  323. // 1. Polygon
  324. indices[indicesIndex] = vertexIndex + j;
  325. indices[indicesIndex + 1] = vertexIndex + 1 + j;
  326. indices[indicesIndex + 2] =
  327. vertexIndex + 1 + GuardRailVertices.Length + j;
  328. // 2. Polygon
  329. indices[indicesIndex + 3] = indices[indicesIndex + 2];
  330. indices[indicesIndex + 4] =
  331. vertexIndex + GuardRailVertices.Length + j;
  332. indices[indicesIndex + 5] = indices[indicesIndex];
  333. }
  334. vertexIndex += GuardRailVertices.Length;
  335. }
  336. // Create the index buffer from our indices.
  337. //railIb = new IndexBuffer(
  338. // BaseGame.Device,
  339. // typeof(int),
  340. // indices.Length,
  341. // ResourceUsage.WriteOnly,
  342. // ResourceManagementMode.Automatic);
  343. railIb = new IndexBuffer(
  344. BaseGame.Device,
  345. typeof(int),
  346. indices.Length,
  347. BufferUsage.WriteOnly);
  348. railIb.SetData(indices);
  349. }
  350. public void Dispose()
  351. {
  352. railVb.Dispose();
  353. railIb.Dispose();
  354. }
  355. /// <summary>
  356. /// Render
  357. /// </summary>
  358. public void Render(Material guardRailMaterial)
  359. {
  360. // We use tangent vertices for everything here
  361. // Restore the world matrix
  362. BaseGame.WorldMatrix = Matrix.Identity;
  363. // Render the complete guardrail
  364. ShaderEffect.normalMapping.Render(
  365. guardRailMaterial,
  366. "Specular20",
  367. new BaseGame.RenderHandler(RenderGuardRailVertices));
  368. /*done in track class
  369. // And also render all the holder piles
  370. foreach (Matrix mat in holderPileMatrices)
  371. {
  372. holderModel.Render(mat);
  373. }
  374. */
  375. // Restore the world matrix
  376. BaseGame.WorldMatrix = Matrix.Identity;
  377. }
  378. /// <summary>
  379. /// Render guard rail vertices
  380. /// </summary>
  381. private void RenderGuardRailVertices()
  382. {
  383. BaseGame.Device.SetVertexBuffer(railVb);
  384. BaseGame.Device.Indices = railIb;
  385. BaseGame.Device.DrawIndexedPrimitives(
  386. PrimitiveType.TriangleList,
  387. 0, 0, (GuardRailVertices.Length - 1) * (railPoints.Length - 1) * 2);
  388. }
  389. /// <summary>
  390. /// Generate shadow
  391. /// </summary>
  392. public void GenerateShadow()
  393. {
  394. // Just render out the guard rails (world matrix is already set)
  395. RenderGuardRailVertices();
  396. }
  397. /// <summary>
  398. /// Use shadow
  399. /// </summary>
  400. public void UseShadow()
  401. {
  402. // Receive shadow on the guard rails
  403. RenderGuardRailVertices();
  404. }
  405. }
  406. }