Model.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. //-----------------------------------------------------------------------------
  2. // Model.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 RacingGame.GameLogic;
  12. using RacingGame.Helpers;
  13. using RacingGame.Shaders;
  14. using RacingGame.Tracks;
  15. using XnaModel = Microsoft.Xna.Framework.Graphics.Model;
  16. using System.IO;
  17. namespace RacingGame.Graphics
  18. {
  19. /// <summary>
  20. /// Model class for loading and displaying x files including all materials
  21. /// and textures. Provides load and render functions to render static
  22. /// non animated models (mostly 1 mesh), for animated models we need a more
  23. /// advanced class, which is not required for this game yet.
  24. /// </summary>
  25. public class Model : IDisposable
  26. {
  27. /// <summary>
  28. /// Name of this model, also used to load it from the content system.
  29. /// </summary>
  30. string name = "";
  31. /// <summary>
  32. /// Underlying xna model object. Loaded with the content system.
  33. /// </summary>
  34. XnaModel xnaModel = null;
  35. /*not longer required
  36. /// <summary>
  37. /// Scaling factor from 3ds max to our engine (1 unit = 1 meter)
  38. /// </summary>
  39. const float MaxModelScaling = 1.0f;
  40. */
  41. /// <summary>
  42. /// Default object matrix to fix models from 3ds max to our engine!
  43. /// </summary>
  44. static readonly Matrix objectMatrix =
  45. //right handed models: Matrix.CreateRotationX(MathHelper.Pi);// *
  46. //Matrix.CreateScale(MaxModelScaling);
  47. // left handed models (else everything is mirrored with x files)
  48. Matrix.CreateRotationX(MathHelper.Pi / 2.0f);
  49. /// <summary>
  50. /// Transform matrices for this model, used in all Render methods,
  51. /// build once in the constructor, it never changes and none of our
  52. /// models are animated.
  53. /// </summary>
  54. Matrix[] transforms = null;
  55. /// <summary>
  56. /// Scaling for this object, used for distance comparisons.
  57. /// </summary>
  58. float realScaling = 1.0f, scaling = 1.0f;
  59. /// <summary>
  60. /// Does this model has alpha textures? Then render with alpha blending
  61. /// turned on. This is usually false and rendering is faster without
  62. /// alpha blending. Also used to skip shadow receiving, which looks
  63. /// strange on palms.
  64. /// </summary>
  65. bool hasAlpha = false;
  66. /// <summary>
  67. /// Is this the car model? Set in constructor and used in the render
  68. /// methods, this way we can compare much faster when rendering!
  69. /// </summary>
  70. bool isCar = false;
  71. /// <summary>
  72. /// If we want to animated some mesh in the model, just set the
  73. /// modelmesh here. Used for the windmill, which is rotated in
  74. /// Render!
  75. /// </summary>
  76. ModelMesh animatedMesh = null;
  77. /// <summary>
  78. /// Cached effect parameters to improve performance.
  79. /// For around 100 000 objects we save 1 second per effect parameters
  80. /// call. We only save the world matrix, worldViewProj matrix,
  81. /// viewInverse matrix and the lightDir vector effect parameters.
  82. /// Update: We also save diffuseTexture, ambientColor and diffuseColor now
  83. /// </summary>
  84. List<EffectParameter> cachedEffectParameters =
  85. new List<EffectParameter>();
  86. /// <summary>
  87. /// Another helper to check if the effect technique is
  88. /// "ReflectionSpecular". Checking this each frame takes a lot of time,
  89. /// this helper does the check only once in the constuctor.
  90. /// Used in RenderCar!
  91. /// </summary>
  92. List<bool> cachedIsReflectionSpecularTechnique =
  93. new List<bool>();
  94. /// <summary>
  95. /// Renderable meshes dictionary. Used to render every RenderableMesh
  96. /// in our render method.
  97. /// </summary>
  98. Dictionary<ModelMeshPart, MeshRenderManager.RenderableMesh>
  99. renderableMeshes =
  100. new Dictionary<ModelMeshPart, MeshRenderManager.RenderableMesh>();
  101. /// <summary>
  102. /// Name for this model, this is the content name.
  103. /// </summary>
  104. /// <returns>String</returns>
  105. public string Name
  106. {
  107. get
  108. {
  109. return name;
  110. }
  111. }
  112. /// <summary>
  113. /// Size
  114. /// </summary>
  115. /// <returns>Float</returns>
  116. public float Size
  117. {
  118. get
  119. {
  120. return realScaling;
  121. }
  122. }
  123. /// <summary>
  124. /// Number of mesh parts
  125. /// </summary>
  126. /// <returns>Int</returns>
  127. public int NumOfMeshParts
  128. {
  129. get
  130. {
  131. int ret = 0;
  132. for (int meshNum = 0; meshNum < xnaModel.Meshes.Count; meshNum++)
  133. ret += xnaModel.Meshes[meshNum].MeshParts.Count;
  134. return ret;
  135. }
  136. }
  137. /// <summary>
  138. /// Create model
  139. /// </summary>
  140. /// <param name="setModelName">Set model name</param>
  141. public Model(string setModelName)
  142. {
  143. name = setModelName;
  144. xnaModel = BaseGame.Content.Load<XnaModel>(
  145. Path.Combine(Directories.ContentDirectory, "Models", name));
  146. // Get matrix transformations of the model
  147. // Has to be done only once because we don't use animations in our game.
  148. if (xnaModel != null)
  149. {
  150. transforms = new Matrix[xnaModel.Bones.Count];
  151. xnaModel.CopyAbsoluteBoneTransformsTo(transforms);
  152. // Calculate scaling for this object, used for distance comparisons.
  153. if (xnaModel.Meshes.Count > 0)
  154. realScaling = scaling =
  155. xnaModel.Meshes[0].BoundingSphere.Radius *
  156. transforms[0].Right.Length();
  157. // For palms, laterns, holders and column holders reduce scaling
  158. // to reduce the number of objects we have to render.
  159. if (name.ToLower() == "alphapalm" ||
  160. name.ToLower() == "alphapalm2" ||
  161. name.ToLower() == "alphapalm3" ||
  162. name.ToLower() == "roadcolumnsegment")
  163. scaling *= 0.75f;
  164. // Hotels and windmills should always be visible (they are big)
  165. if (name.ToLower() == "hotel01" ||
  166. name.ToLower() == "hotel02" ||
  167. name.ToLower() == "casino01" ||
  168. name.ToLower() == "windmill")
  169. scaling *= 5.0f;
  170. else
  171. // Don't use more than 3m for scaling and checking smaller objects
  172. if (scaling > 3)
  173. scaling = 3;
  174. }
  175. hasAlpha = name.ToLower().StartsWith("alpha");
  176. // Is this a sign or banner? Then make sure ambient is pretty high!
  177. bool isSign = name.ToLower().StartsWith("sign") ||
  178. name.ToLower().StartsWith("banner") ||
  179. // Also include windmills, they are too dark
  180. name.ToLower().StartsWith("windmill");
  181. //name.StartsWith("StartLight");
  182. isCar = (name.ToLower() == "car");
  183. // Go through all meshes in the model
  184. for (int meshNum = 0; meshNum < xnaModel.Meshes.Count; meshNum++)
  185. {
  186. ModelMesh mesh = xnaModel.Meshes[meshNum];
  187. int meshPartNum = 0;
  188. string meshName = mesh.Name;
  189. // Remember this mesh for animations done in Render!
  190. if (name.ToLower() == "windmill" &&
  191. meshName.ToLower().StartsWith("windmill_wings"))
  192. animatedMesh = mesh;
  193. // And for each effect this mesh uses (usually just 1, multimaterials
  194. // are nice in 3ds max, but not efficiently for rendering stuff).
  195. for (int effectNum = 0; effectNum < mesh.Effects.Count; effectNum++)
  196. {
  197. Effect effect = mesh.Effects[effectNum];
  198. // MonoGame's XImporter produces BasicEffect for .x files; in that case
  199. // cache parameters from the NormalMapping shader that actually renders them.
  200. Effect paramSource = (effect is BasicEffect)
  201. ? ShaderEffect.normalMapping.Effect
  202. : effect;
  203. // Store our effect parameters
  204. cachedEffectParameters.Add(paramSource.Parameters["diffuseTexture"]);
  205. cachedEffectParameters.Add(paramSource.Parameters["ambientColor"]);
  206. cachedEffectParameters.Add(paramSource.Parameters["diffuseColor"]);
  207. cachedEffectParameters.Add(paramSource.Parameters["world"]);
  208. cachedEffectParameters.Add(paramSource.Parameters["viewProj"]);
  209. cachedEffectParameters.Add(paramSource.Parameters["viewInverse"]);
  210. cachedEffectParameters.Add(paramSource.Parameters["lightDir"]);
  211. // Store if this is a "ReflectionSpecular" technique.
  212. // For BasicEffect fallback, identify glass by mesh name.
  213. cachedIsReflectionSpecularTechnique.Add(
  214. (effect is BasicEffect)
  215. ? mesh.Name.ToLower().StartsWith("glass")
  216. : effect.CurrentTechnique.Name.Contains("ReflectionSpecular"));
  217. // Increase ambient value to 0.5, 0.5, 0.5 for signs and banners!
  218. if (isSign
  219. && effect.Parameters["ambientColor"] != null)
  220. effect.Parameters["ambientColor"].SetValue(
  221. new Color(128, 128, 128).ToVector4());
  222. // Car only uses alpha on the glass
  223. if (isCar
  224. && !mesh.Name.StartsWith("glass")
  225. && effect.Parameters["UseAlpha"] != null)
  226. effect.Parameters["UseAlpha"].SetValue(false);
  227. // Get technique from meshName
  228. int techniqueIndex = -1;
  229. if (meshName.Length > meshPartNum)
  230. {
  231. string techniqueNumberString = meshName.Substring(
  232. meshName.Length - (1 + meshPartNum), 1);
  233. #if !XBOX360
  234. // Faster and does not throw an exception!
  235. int.TryParse(techniqueNumberString, out techniqueIndex);
  236. #else
  237. try
  238. {
  239. techniqueIndex = Convert.ToInt32(techniqueNumberString);
  240. }
  241. catch { }
  242. #endif
  243. }
  244. // No technique found or invalid?
  245. if (techniqueIndex < 0 ||
  246. techniqueIndex >= effect.Techniques.Count)
  247. {
  248. // Try to use last technique
  249. techniqueIndex = effect.Techniques.Count - 1;
  250. // If this is NormalMapping, use DiffuseSpecular20 instead
  251. // of the last technique (which is SpecularWithReflection20)
  252. /*if (effect.Techniques[techniqueIndex].Name.Contains(
  253. "SpecularWithReflection"))
  254. techniqueIndex -= 2;
  255. // Update: We have now 2 more techniques (ReflectionSpecular)
  256. if (effect.Techniques[techniqueIndex].Name.Contains(
  257. "ReflectionSpecular"))
  258. techniqueIndex -= 4;*/
  259. }
  260. // Set current technique for rendering below
  261. effect.CurrentTechnique = effect.Techniques[techniqueIndex];
  262. // Next mesh part
  263. meshPartNum++;
  264. }
  265. // Add all mesh parts!
  266. for (int partNum = 0; partNum < mesh.MeshParts.Count; partNum++)
  267. {
  268. ModelMeshPart part = mesh.MeshParts[partNum];
  269. // The model mesh part is not really used, we just extract the
  270. // index and vertex buffers and all the render data.
  271. // Material settings are build from the effect settings.
  272. // Also add this to our own dictionary for rendering.
  273. renderableMeshes.Add(part, BaseGame.MeshRenderManager.Add(
  274. part.VertexBuffer, part.IndexBuffer, part, part.Effect));
  275. }
  276. }
  277. #if DEBUG
  278. // Check if there are no meshes to render
  279. if (xnaModel.Meshes.Count == 0)
  280. throw new ArgumentException("Invalid model "+name+
  281. ". It does not contain any meshes");
  282. #endif
  283. }
  284. /// <summary>
  285. /// Dispose
  286. /// </summary>
  287. public void Dispose()
  288. {
  289. Dispose(true);
  290. GC.SuppressFinalize(this);
  291. }
  292. /// <summary>
  293. /// Dispose
  294. /// </summary>
  295. /// <param name="disposing">Disposing</param>
  296. protected virtual void Dispose(bool disposing)
  297. {
  298. if (disposing)
  299. {
  300. // Just set everything to null so we stop using this!
  301. name = "";
  302. xnaModel = null;
  303. transforms = null;
  304. animatedMesh = null;
  305. }
  306. }
  307. /// <summary>
  308. /// Default view distance optimizer is at 250m, then skip stuff.
  309. /// This will be reduced as our framerate runs low to improve performance
  310. /// on low end systems.
  311. /// </summary>
  312. static int maxViewDistance = 200;
  313. /// <summary>
  314. /// Maximum view distance
  315. /// </summary>
  316. /// <returns>Int</returns>
  317. public static int MaxViewDistance
  318. {
  319. get
  320. {
  321. return maxViewDistance;
  322. }
  323. set
  324. {
  325. // Only set if we reduce, don't increase again if it is running
  326. // a little faster for a short time.
  327. if (value < maxViewDistance)
  328. maxViewDistance = value;
  329. }
  330. }
  331. /// <summary>
  332. /// Render
  333. /// </summary>
  334. /// <param name="renderMatrix">Render matrix</param>
  335. public void Render(Matrix renderMatrix)
  336. {
  337. // Optimization to skip smaller objects, which are very far away!
  338. // Display 1 meter big objects only if in a distance of 250 meters!
  339. // Scaling is guessed by the length of the first vector in our matrix,
  340. // because we always use the same scaling for x, y, z this should be
  341. // correct!
  342. float maxDistance = maxViewDistance * scaling;
  343. float distanceSquared = Vector3.DistanceSquared(
  344. BaseGame.CameraPos, renderMatrix.Translation);
  345. if (distanceSquared > maxDistance * maxDistance)
  346. // Don't render, too far away!
  347. return;
  348. // Check out if object is behind us or not visible, then we can skip
  349. // rendering. This is the GREATEST performance gain in the whole game!
  350. // Object must be away at least 20 units!
  351. if (distanceSquared > 20 * 20 &&
  352. // And the object size must be small
  353. distanceSquared > (10 * scaling) * (10 * scaling))
  354. {
  355. Vector3 objectDirection =
  356. Vector3.Normalize(BaseGame.CameraPos - renderMatrix.Translation);
  357. // Half field of view should be fov / 2, but because of
  358. // the aspect ratio (1.33) and an additional offset we need
  359. // to include to see objects at the borders.
  360. float objAngle = Vector3Helper.GetAngleBetweenVectors(
  361. BaseGame.CameraRotation, objectDirection);
  362. if (objAngle > BaseGame.ViewableFieldOfView)
  363. // Skip.
  364. return;
  365. }
  366. // Multiply object matrix by render matrix, result is used multiple
  367. // times here.
  368. renderMatrix = objectMatrix * renderMatrix;
  369. // Go through all meshes in the model
  370. for (int meshNum = 0; meshNum < xnaModel.Meshes.Count; meshNum++)
  371. {
  372. ModelMesh mesh = xnaModel.Meshes[meshNum];
  373. // Assign world matrix
  374. Matrix worldMatrix =
  375. transforms[mesh.ParentBone.Index] *
  376. renderMatrix;
  377. // Got animation?
  378. if (animatedMesh == mesh)
  379. {
  380. worldMatrix =
  381. Matrix.CreateRotationZ(
  382. // Use pseudo number for this object for different rotations
  383. renderMatrix.Translation.Length() * 3 +
  384. renderMatrix.Determinant() * 5 +
  385. (1.0f + ((int)(renderMatrix.M42 * 33.3f) % 100) * 0.00123f) *
  386. BaseGame.TotalTime / 0.654f) *
  387. transforms[mesh.ParentBone.Index] *
  388. renderMatrix;
  389. }
  390. // Just add this world matrix to our render matrices for each part.
  391. for (int partNum = 0; partNum < mesh.MeshParts.Count; partNum++)
  392. {
  393. // Find mesh part in the renderableMeshes dictionary and add the
  394. // new render matrix to be picked up in the mesh rendering later.
  395. renderableMeshes[mesh.MeshParts[partNum]].renderMatrices.Add(
  396. worldMatrix);
  397. }
  398. }
  399. }
  400. /// <summary>
  401. /// Render
  402. /// </summary>
  403. /// <param name="renderPos">Render position</param>
  404. public void Render(Vector3 renderPos)
  405. {
  406. Render(Matrix.CreateTranslation(renderPos));
  407. }
  408. /// <summary>
  409. /// Render car model with this seperate method because we
  410. /// render it in 2 steps, first the solid stuff, then the alpha glass.
  411. /// We also rotate the wheels around :)
  412. /// </summary>
  413. /// <param name="carNumber">Car type number (0, 1 or 2) for the car
  414. /// texture</param>
  415. /// <param name="carColor">Car color we are currently using.</param>
  416. /// <param name="shadowCarMode">In the shadow car mode we render
  417. /// everything (including wheels and glass) with a special ShadowCar
  418. /// shader, that is very transparent. Used for the shadow car when
  419. /// playing that shows how we drove the last time.</param>
  420. /// <param name="renderMatrix">Render matrix for the car</param>
  421. public void RenderCar(int carNumber, Color carColor, bool shadowCarMode,
  422. Matrix renderMatrix)
  423. {
  424. // Multiply object matrix by render matrix, result is used multiple
  425. // times here.
  426. renderMatrix = objectMatrix * renderMatrix;
  427. // Do we just want to render the shadow car? Then do this in a
  428. // simpified way here instead of messing with the already complicated
  429. // code below.
  430. if (shadowCarMode)
  431. {
  432. // Start shadow car shader
  433. ShaderEffect simpleShader = ShaderEffect.lighting;
  434. simpleShader.Render(
  435. "ShadowCar20",
  436. delegate
  437. {
  438. int wheelNumber = 0;
  439. // And just render all meshes with it!
  440. for (int meshNum = 0; meshNum < xnaModel.Meshes.Count; meshNum++)
  441. {
  442. ModelMesh mesh = xnaModel.Meshes[meshNum];
  443. Matrix meshMatrix = transforms[mesh.ParentBone.Index];
  444. // Only the wheels have 2 mesh parts (gummi and chrome)
  445. if (mesh.MeshParts.Count == 2)
  446. {
  447. wheelNumber++;
  448. meshMatrix =
  449. Matrix.CreateRotationX(
  450. // Rotate left 2 wheels forward, the other 2 backward
  451. (wheelNumber == 2 || wheelNumber == 4 ? 1 : -1) *
  452. RacingGameManager.Player.CarWheelPos) *
  453. meshMatrix;
  454. }
  455. // Assign world matrix
  456. BaseGame.WorldMatrix =
  457. meshMatrix *
  458. renderMatrix;
  459. // Set all matrices
  460. simpleShader.SetParameters();
  461. simpleShader.Update();
  462. // And render (must be done without mesh.Draw, which would
  463. // just use the original shaders for the model)
  464. for (int partNum = 0; partNum < mesh.MeshParts.Count; partNum++)
  465. {
  466. ModelMeshPart part = mesh.MeshParts[partNum];
  467. // Make sure vertex declaration is correct
  468. // Set vertex buffer and index buffer
  469. BaseGame.Device.SetVertexBuffer(part.VertexBuffer);
  470. BaseGame.Device.Indices = part.IndexBuffer;
  471. // And render all primitives
  472. BaseGame.Device.DrawIndexedPrimitives(
  473. PrimitiveType.TriangleList,
  474. part.VertexOffset, part.StartIndex, part.PrimitiveCount);
  475. }
  476. }
  477. });
  478. // And get outta here
  479. return;
  480. }
  481. // Usually use default color values
  482. Color ambientColor = Material.DefaultAmbientColor;
  483. Color diffuseColor = Material.DefaultDiffuseColor;
  484. EffectTechnique remCurrentTechnique = null;
  485. for (int alphaPass = 0; alphaPass < 2; alphaPass++)
  486. {
  487. int wheelNumber = 0;
  488. int effectParameterIndex = 0;
  489. int effectTechniqueIndex = 0;
  490. for (int meshNum = 0; meshNum < xnaModel.Meshes.Count; meshNum++)
  491. {
  492. ModelMesh mesh = xnaModel.Meshes[meshNum];
  493. bool dontRender = false;
  494. for (int effectNum = 0; effectNum < mesh.Effects.Count; effectNum++)
  495. {
  496. Effect effect = mesh.Effects[effectNum];
  497. if (effectNum == 0)
  498. {
  499. // For BasicEffect fallback, remember the normalMapping technique
  500. remCurrentTechnique = (effect is BasicEffect)
  501. ? ShaderEffect.normalMapping.Effect.CurrentTechnique
  502. : effect.CurrentTechnique;
  503. }
  504. // Find out if this is ReflectionSimpleGlass.fx,
  505. // NormalMapping.fx will also use reflection, but the techniques
  506. // are named in another way (SpecularWithReflection, etc.)
  507. if (cachedIsReflectionSpecularTechnique[effectTechniqueIndex++])
  508. {
  509. if (alphaPass == 0)
  510. {
  511. dontRender = true;
  512. effectParameterIndex += 7;
  513. break;
  514. }
  515. // Skip the first 3 effect parameters
  516. effectParameterIndex += 3;
  517. }
  518. else
  519. {
  520. if (alphaPass == 1)
  521. {
  522. dontRender = true;
  523. effectParameterIndex += 7;
  524. break;
  525. }
  526. // To improve performance we only have to set this when it
  527. // changes! Doesn't do much, because this eats only 10%
  528. // performance, 5-10% are the matrices below and most of the
  529. // performance is just rendering the car with Draw!
  530. // Overwrite car diffuse textures depending on the car number
  531. // we want to render.
  532. cachedEffectParameters[effectParameterIndex++].SetValue(
  533. RacingGameManager.CarTexture(carNumber).XnaTexture);
  534. // Also set color
  535. cachedEffectParameters[effectParameterIndex++].SetValue(
  536. ambientColor.ToVector4());
  537. cachedEffectParameters[effectParameterIndex++].SetValue(
  538. diffuseColor.ToVector4());
  539. // Change shader to SpecularWithReflectionForCar20 if we changed the color.
  540. if (RacingGameManager.currentCarColor != 0 &&
  541. effectNum == 0)
  542. {
  543. // Route through normalMapping when effect is BasicEffect fallback
  544. Effect carEffect = (effect is BasicEffect)
  545. ? ShaderEffect.normalMapping.Effect
  546. : effect;
  547. carEffect.CurrentTechnique =
  548. carEffect.Techniques["SpecularWithReflectionForCar20"];
  549. carEffect.Parameters["carHueColor"]?.SetValue(
  550. carColor.ToVector3());
  551. }
  552. }
  553. Matrix meshMatrix = transforms[mesh.ParentBone.Index];
  554. // Only the wheels have 2 mesh parts (gummi and chrome)
  555. if (mesh.MeshParts.Count == 2)
  556. {
  557. wheelNumber++;
  558. meshMatrix =
  559. Matrix.CreateRotationX(
  560. // Rotate left 2 wheels forward, the other 2 backward!
  561. (wheelNumber == 2 || wheelNumber == 4 ? 1 : -1) *
  562. RacingGameManager.Player.CarWheelPos) *
  563. meshMatrix;
  564. }
  565. // Assign world matrix
  566. BaseGame.WorldMatrix =
  567. meshMatrix *
  568. renderMatrix;
  569. // Set matrices
  570. cachedEffectParameters[effectParameterIndex++].SetValue(
  571. BaseGame.WorldMatrix);
  572. // These values should only be set once every frame (see above)!
  573. // to improve performance again, also we should access them
  574. // with EffectParameter and not via name!
  575. // But since we got only 1 car it doesn't matter so much ..
  576. cachedEffectParameters[effectParameterIndex++].SetValue(
  577. BaseGame.ViewProjectionMatrix);
  578. cachedEffectParameters[effectParameterIndex++].SetValue(
  579. BaseGame.InverseViewMatrix.Translation);
  580. // Set light direction
  581. if (cachedEffectParameters [effectParameterIndex] != null) {
  582. cachedEffectParameters [effectParameterIndex++].SetValue (
  583. BaseGame.LightDirection);
  584. }
  585. else {
  586. effectParameterIndex++;
  587. }
  588. }
  589. // Render
  590. if (dontRender == false)
  591. {
  592. // For BasicEffect fallback, render manually through normalMapping shader
  593. if (mesh.Effects.Count > 0 && mesh.Effects[0] is BasicEffect)
  594. {
  595. ShaderEffect.normalMapping.Effect.CurrentTechnique.Passes[0].Apply();
  596. for (int partNum = 0; partNum < mesh.MeshParts.Count; partNum++)
  597. {
  598. ModelMeshPart part = mesh.MeshParts[partNum];
  599. BaseGame.Device.SetVertexBuffer(part.VertexBuffer);
  600. BaseGame.Device.Indices = part.IndexBuffer;
  601. BaseGame.Device.DrawIndexedPrimitives(
  602. PrimitiveType.TriangleList,
  603. part.VertexOffset, part.StartIndex, part.PrimitiveCount);
  604. }
  605. }
  606. else
  607. {
  608. mesh.Draw();
  609. }
  610. }
  611. // Change shader back to default render technique.
  612. // We only have to do this if the color was changed
  613. if (RacingGameManager.currentCarColor != 0 &&
  614. remCurrentTechnique != null)
  615. {
  616. Effect restoreEffect = (mesh.Effects.Count > 0 && mesh.Effects[0] is BasicEffect)
  617. ? ShaderEffect.normalMapping.Effect
  618. : mesh.Effects[0];
  619. restoreEffect.CurrentTechnique = remCurrentTechnique;
  620. }
  621. }
  622. }
  623. }
  624. /// <summary>
  625. /// Generate shadow for this model in the generate shadow pass
  626. /// of our shadow mapping shader. All objects rendered here will
  627. /// cast shadows to our scene (if they are in range of the light)
  628. /// </summary>
  629. /// <param name="renderMatrix">Render matrix</param>
  630. public void GenerateShadow(Matrix renderMatrix)
  631. {
  632. // Find out how far the object is away from the shadow,
  633. // we can ignore it if it is outside of the shadow generation range.
  634. // Everything smaller than 0.5 meter can be ignored.
  635. float maxDistance =
  636. //nice, but not good for shadow mapping, have to use fixed value!
  637. scaling / 2.5f + 1.015f * ShaderEffect.shadowMapping.ShadowDistance;
  638. if (Vector3.DistanceSquared(
  639. ShaderEffect.shadowMapping.ShadowLightPos, renderMatrix.Translation) >
  640. maxDistance * maxDistance)
  641. // Don't render, too far away!
  642. return;
  643. // Multiply object matrix by render matrix.
  644. renderMatrix = objectMatrix * renderMatrix;
  645. for (int meshNum = 0; meshNum < xnaModel.Meshes.Count; meshNum++)
  646. {
  647. ModelMesh mesh = xnaModel.Meshes[meshNum];
  648. // Use the ShadowMapShader helper method to set the world matrices
  649. ShaderEffect.shadowMapping.UpdateGenerateShadowWorldMatrix(
  650. transforms[mesh.ParentBone.Index] *
  651. renderMatrix);
  652. // Got animation?
  653. if (animatedMesh == mesh)
  654. {
  655. ShaderEffect.shadowMapping.UpdateGenerateShadowWorldMatrix(
  656. Matrix.CreateRotationZ(
  657. // Use pseudo number for this object for different rotations
  658. renderMatrix.Translation.Length() * 3 +
  659. renderMatrix.Determinant() * 5 +
  660. (1.0f + ((int)(renderMatrix.M42 * 33.3f) % 100) * 0.00123f) *
  661. BaseGame.TotalTime / 0.654f) *
  662. transforms[mesh.ParentBone.Index] *
  663. renderMatrix);
  664. }
  665. for (int partNum = 0; partNum < mesh.MeshParts.Count; partNum++)
  666. {
  667. ModelMeshPart part = mesh.MeshParts[partNum];
  668. // Render just the vertices, do not use the shaders of our model.
  669. // This is the same code as ModelMeshPart.Draw() uses, but
  670. // this method is internal and can't be used by us :(
  671. BaseGame.Device.SetVertexBuffer(part.VertexBuffer);
  672. BaseGame.Device.Indices = part.IndexBuffer;
  673. BaseGame.Device.DrawIndexedPrimitives(
  674. PrimitiveType.TriangleList,
  675. part.VertexOffset, part.StartIndex, part.PrimitiveCount);
  676. }
  677. }
  678. }
  679. /// <summary>
  680. /// Use shadow for our scene. We render all objects that should receive
  681. /// shadows here. Called from the ShadowMappingShader.UseShadow method.
  682. /// </summary>
  683. /// <param name="renderMatrix">Render matrix</param>
  684. public void UseShadow(Matrix renderMatrix)
  685. {
  686. // If this is an object that uses alpha textures, never receive
  687. // shadows, this only causes troubes and needs a much more complex
  688. // shader. This affects usually only palms anyway, which look better
  689. // without shadowing on.
  690. if (hasAlpha)
  691. return;
  692. // Find out how far the object is away from the shadow,
  693. // we can ignore it if it is outside of the shadow generation range.
  694. // Everything smaller than 0.25 meter can be ignored.
  695. // Note: For receiving we usually use more objects than for generating
  696. // shadows.
  697. float maxDistance =
  698. //nice, but not good for shadow mapping, have to use fixed value!
  699. 1.015f * ShaderEffect.shadowMapping.ShadowDistance;
  700. if (Vector3.DistanceSquared(
  701. ShaderEffect.shadowMapping.ShadowLightPos, renderMatrix.Translation) >
  702. maxDistance * maxDistance)
  703. // Don't render, too far away!
  704. return;
  705. // Multiply object matrix by render matrix.
  706. renderMatrix = objectMatrix * renderMatrix;
  707. for (int meshNum = 0; meshNum < xnaModel.Meshes.Count; meshNum++)
  708. {
  709. ModelMesh mesh = xnaModel.Meshes[meshNum];
  710. // Use the ShadowMapShader helper method to set the world matrices
  711. ShaderEffect.shadowMapping.UpdateCalcShadowWorldMatrix(
  712. transforms[mesh.ParentBone.Index] *
  713. renderMatrix);
  714. // Got animation?
  715. if (animatedMesh == mesh)
  716. {
  717. ShaderEffect.shadowMapping.UpdateCalcShadowWorldMatrix(
  718. Matrix.CreateRotationZ(
  719. // Use pseudo number for this object for different rotations
  720. renderMatrix.Translation.Length() * 3 +
  721. renderMatrix.Determinant() * 5 +
  722. (1.0f + ((int)(renderMatrix.M42 * 33.3f) % 100) * 0.00123f) *
  723. BaseGame.TotalTime / 0.654f) *
  724. transforms[mesh.ParentBone.Index] *
  725. renderMatrix);
  726. }
  727. for (int partNum = 0; partNum < mesh.MeshParts.Count; partNum++)
  728. {
  729. ModelMeshPart part = mesh.MeshParts[partNum];
  730. // Render just the vertices, do not use the shaders of our model.
  731. // This is the same code as ModelMeshPart.Draw() uses, but
  732. // this method is internal and can't be used by us :(
  733. BaseGame.Device.SetVertexBuffer(part.VertexBuffer);
  734. BaseGame.Device.Indices = part.IndexBuffer;
  735. BaseGame.Device.DrawIndexedPrimitives(
  736. PrimitiveType.TriangleList,
  737. part.VertexOffset, part.StartIndex, part.PrimitiveCount);
  738. }
  739. }
  740. }
  741. }
  742. }