PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/Rendering/Cameras/PathCamera.cs

#
C# | 494 lines | 307 code | 52 blank | 135 comment | 33 complexity | 13fb8b42470ca7ff317ea4dfd13c42d4 MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.IO;
  3. using Delta.Engine;
  4. using Delta.Utilities;
  5. using Delta.Utilities.Datatypes;
  6. using Delta.Utilities.Helpers;
  7. namespace Delta.Rendering.Cameras
  8. {
  9. /// <summary>
  10. /// Path camera represent a camera witch moves on a given path.
  11. /// A path consist of many positions in a 3D space. The camera stops moving,
  12. /// when it reached the last position in the given path.
  13. /// The UP-Vector is implemented in the base camera in the UpdateViewMatrix
  14. /// method. Which is the UnitZ-Vector.
  15. /// <para />
  16. /// </summary>
  17. public class PathCamera : LookAtCamera
  18. {
  19. #region Constants
  20. /// <summary>
  21. /// The current version of the implementation of this Camera class.
  22. /// </summary>
  23. private const int ImplementationVersion = 1;
  24. /// <summary>
  25. /// Camera default speed.
  26. /// </summary>
  27. private const float DefaultSpeed = 10f;
  28. #endregion
  29. #region LoadColladaCameraPath (Static)
  30. /// <summary>
  31. /// Loads the exported camera path (view) matrices from a Collada (file)
  32. /// data string.
  33. /// </summary>
  34. /// <param name="colladaMatricesString">Collada matrices string.</param>
  35. /// <returns>Return the loaded array of matrices.</returns>
  36. public static Matrix[] LoadColladaCameraPath(string colladaMatricesString)
  37. {
  38. float[] rawMatricesValues =
  39. colladaMatricesString.ToFloatArray();
  40. // Build the matrices path from the raw path data
  41. int matricesCount = rawMatricesValues.Length / 16;
  42. Matrix[] matricesPath = new Matrix[matricesCount];
  43. // we have to scale down the exported matrix by the engine scaling
  44. // 1m = 0.01 max units (cm)
  45. Matrix correctionScaleMatrix = Matrix.CreateScale(0.01f);
  46. for (int index = 0; index < matricesCount; index++)
  47. {
  48. // The view matrix is in Collada exported as inverse view matrix,
  49. // probably because of optimization reasons of 3DsMax's viewport render
  50. Matrix deltaViewMatrix =
  51. LoadColladaMatrix(rawMatricesValues, index * 16).Inverse;
  52. Matrix.Multiply(ref deltaViewMatrix, ref correctionScaleMatrix,
  53. ref matricesPath[index]);
  54. }
  55. return matricesPath;
  56. }
  57. #endregion
  58. #region LoadColladaMatrix (Static)
  59. /// <summary>
  60. /// Create matrix from collada float value array. The matrices in collada
  61. /// are stored differently from the xna format (row based instead of
  62. /// columns).
  63. /// </summary>
  64. /// <param name="mat">Array of floats containing this matrix, but
  65. /// not exclusively. Can contain many more matrices by using the offset
  66. /// parameter.</param>
  67. /// <param name="offset">The offset index inside the mat array.</param>
  68. /// <returns>Delta Matrix created from collada matrix data.</returns>
  69. public static Matrix LoadColladaMatrix(float[] mat, int offset)
  70. {
  71. return new Matrix(
  72. mat[offset + 0], mat[offset + 4], mat[offset + 8], mat[offset + 12],
  73. mat[offset + 1], mat[offset + 5], mat[offset + 9], mat[offset + 13],
  74. mat[offset + 2], mat[offset + 6], mat[offset + 10], mat[offset + 14],
  75. mat[offset + 3], mat[offset + 7], mat[offset + 11], mat[offset + 15]);
  76. }
  77. #endregion
  78. #region LookDirection (Public)
  79. /// <summary>
  80. /// Look direction
  81. /// </summary>
  82. public override Vector LookDirection
  83. {
  84. get
  85. {
  86. return target - position;
  87. }
  88. set
  89. {
  90. // Ignored anyway
  91. lookDirection = value;
  92. }
  93. }
  94. #endregion
  95. #region IsFinished (Public)
  96. /// <summary>
  97. /// Has the camera reached the last node of the path?
  98. /// </summary>
  99. public bool IsFinished
  100. {
  101. get;
  102. private set;
  103. }
  104. #endregion
  105. #region Speed (Public)
  106. /// <summary>
  107. /// The speed of the camera, default is 10f.
  108. /// </summary>
  109. public float Speed
  110. {
  111. get;
  112. set;
  113. }
  114. #endregion
  115. #region Private
  116. #region pathByMatrices (Private)
  117. /// <summary>
  118. /// Interpolated positions and direction as a matrix.
  119. /// We get all the data from a Collada file.
  120. /// </summary>
  121. private readonly Matrix[] pathByMatrices;
  122. #endregion
  123. #region pathByPoints (Private)
  124. /// <summary>
  125. /// Path of the camera by position points.
  126. /// </summary>
  127. private readonly Vector[] pathByPoints;
  128. #endregion
  129. #region positionDistances (Private)
  130. /// <summary>
  131. /// Precalculated distances from the start to the current position,
  132. /// to speed up the calculations on moving along the path.
  133. /// </summary>
  134. private readonly float[] positionDistances;
  135. #endregion
  136. #region targetPath (Private)
  137. /// <summary>
  138. /// The camera target positions path related to the camera
  139. /// positions path.
  140. /// This path can also be longer or shorter than the positions
  141. /// path, because the current distance on the target path will be
  142. /// calculated relatively to the length of the positions path.
  143. /// </summary>
  144. private readonly Vector[] targetPath;
  145. #endregion
  146. #region targetDistances (Private)
  147. /// <summary>
  148. /// Precalculated distances from the start to the current target
  149. /// position, to speed up the calculations on moving along the path.
  150. /// </summary>
  151. private readonly float[] targetDistances;
  152. #endregion
  153. #region elapsedDistance (Private)
  154. /// <summary>
  155. /// The elapsed distance on the path.
  156. /// </summary>
  157. private float elapsedDistance;
  158. #endregion
  159. #region targetDistanceFactor (Private)
  160. /// <summary>
  161. /// The relative factor between the length of the camera path and
  162. /// the length of the target path.
  163. /// </summary>
  164. private readonly float targetDistanceFactor;
  165. #endregion
  166. #endregion
  167. #region Constructors
  168. /// <summary>
  169. /// Create path camera with a list of matrices.
  170. /// </summary>
  171. /// <param name="setPath">Matrix array containing path data.</param>
  172. public PathCamera(Matrix[] setPath)
  173. : this()
  174. {
  175. // Set the camera path (which is always valid)
  176. pathByMatrices = setPath != null
  177. ? setPath
  178. : new Matrix[0];
  179. if (pathByMatrices.Length < 1)
  180. {
  181. Log.Warning("There is no camera path. We can't move any path " +
  182. "without at least 1 matrix.");
  183. UpdateViewMatrix();
  184. UpdateScreenProperties();
  185. }
  186. positionDistances = new float[pathByMatrices.Length];
  187. float distanceCount = 0f;
  188. for (int index = 1; index < pathByMatrices.Length; index++)
  189. {
  190. Matrix inv1 = Matrix.Invert(pathByMatrices[index]);
  191. Matrix inv2 = Matrix.Invert(pathByMatrices[index - 1]);
  192. distanceCount += (inv1.Translation -
  193. inv2.Translation).Length;
  194. positionDistances[index] = distanceCount;
  195. }
  196. }
  197. /// <summary>
  198. /// Create path camera with a list of vectors (positions) and
  199. /// a list of target vectors (if available).
  200. /// </summary>
  201. /// <param name="setPath">Vector array containing
  202. /// path points data.</param>
  203. /// <param name="setPath">Vector array containing
  204. /// target path points data.</param>
  205. public PathCamera(Vector[] setPath, Vector[] setTargetPath = null)
  206. : this()
  207. {
  208. // Set the camera path (which is always valid)
  209. pathByPoints = (setPath != null)
  210. ? setPath
  211. : new Vector[0];
  212. positionDistances = new float[pathByPoints.Length];
  213. float distanceCount = 0f;
  214. for (int index = 1; index < pathByPoints.Length; index++)
  215. {
  216. distanceCount += (pathByPoints[index] -
  217. pathByPoints[index - 1]).Length;
  218. positionDistances[index] = distanceCount;
  219. }
  220. if (pathByPoints.Length < 2)
  221. {
  222. Log.Warning("There is no camera path. We can't move any path " +
  223. "without at least 2 positions.");
  224. UpdateViewMatrix();
  225. UpdateScreenProperties();
  226. }
  227. // Set the target path (if valid)
  228. if (setTargetPath != null)
  229. {
  230. if (setTargetPath.Length == 0)
  231. {
  232. Log.Warning("The target path needs at least one position!");
  233. }
  234. else
  235. {
  236. targetPath = setTargetPath;
  237. targetDistances = new float[targetPath.Length];
  238. distanceCount = 0f;
  239. for (int index = 1; index < targetPath.Length; index++)
  240. {
  241. distanceCount += (targetPath[index] -
  242. targetPath[index - 1]).Length;
  243. targetDistances[index] = distanceCount;
  244. }
  245. targetDistanceFactor =
  246. targetDistances[targetDistances.Length - 1] /
  247. positionDistances[positionDistances.Length - 1];
  248. }
  249. }
  250. }
  251. /// <summary>
  252. /// Private helper constructor to set the default values for
  253. /// all other constructors above.
  254. /// </summary>
  255. private PathCamera()
  256. : base(DefaultLookAtCamPosition, DefaultTargetPosition)
  257. {
  258. // Set the default values
  259. Speed = DefaultSpeed;
  260. IsFinished = false;
  261. }
  262. #endregion
  263. #region Save (Public)
  264. /// <summary>
  265. /// Override saving data from BinaryStream.
  266. /// </summary>
  267. /// <param name="dataWriter">Binary writer used for writing data.</param>
  268. public override void Save(BinaryWriter dataWriter)
  269. {
  270. base.Save(dataWriter);
  271. // Save the implementation version first
  272. dataWriter.Write(ImplementationVersion);
  273. //throw new NotImplementedException("TODO");
  274. //dataWriter.Write(TargetPath.Length);
  275. //for (int index = 0; index < TargetPath.Length; index++)
  276. //{
  277. // TargetPath[index].Save(dataWriter);
  278. //}
  279. }
  280. #endregion
  281. #region Load (Public)
  282. /// <summary>
  283. /// Override loading data from BinaryStream.
  284. /// </summary>
  285. /// <param name="reader">Binary reader used for reading data.</param>
  286. public override void Load(BinaryReader reader)
  287. {
  288. base.Load(reader);
  289. // First read the implementation version
  290. int version = reader.ReadInt32();
  291. switch (version)
  292. {
  293. // Version 1
  294. case 1:
  295. //throw new NotImplementedException("TODO");
  296. //int length = reader.ReadInt32();
  297. //TargetPath = new Vector[length];
  298. //for (int index = 0; index < length; index++)
  299. //{
  300. // TargetPath[index].Load(reader);
  301. //}
  302. break;
  303. default:
  304. Log.InvalidVersionWarning(GetType().Name + ": " + Name,
  305. version, ImplementationVersion);
  306. break;
  307. }
  308. }
  309. #endregion
  310. #region Methods (Private)
  311. #region InternalRun
  312. /// <summary>
  313. /// Internal run
  314. /// </summary>
  315. protected override void InternalRun()
  316. {
  317. elapsedDistance += Time.Delta * Speed;
  318. if (pathByMatrices != null)
  319. {
  320. UpdateMatrixPath();
  321. }
  322. else if (pathByPoints != null)
  323. {
  324. UpdatePointsPath();
  325. }
  326. }
  327. #endregion
  328. #region UpdateMatrixPath
  329. /// <summary>
  330. /// Update the path camera according to the matrices.
  331. /// </summary>
  332. private void UpdateMatrixPath()
  333. {
  334. Matrix currentMatrix = Matrix.Identity;
  335. if (elapsedDistance <= 0f)
  336. {
  337. currentMatrix = pathByMatrices[0];
  338. }
  339. else if (elapsedDistance >=
  340. positionDistances[positionDistances.Length - 1])
  341. {
  342. IsFinished = true;
  343. currentMatrix = pathByMatrices[pathByMatrices.Length - 1];
  344. }
  345. else
  346. {
  347. int currentIndex = 0;
  348. for (int index = 0; index < positionDistances.Length - 1; index++)
  349. {
  350. if (elapsedDistance >= positionDistances[index] &&
  351. elapsedDistance < positionDistances[index + 1])
  352. {
  353. currentIndex = index;
  354. break;
  355. }
  356. }
  357. currentMatrix = pathByMatrices[currentIndex];
  358. }
  359. viewMatrix = currentMatrix;
  360. UpdateScreenProperties();
  361. }
  362. #endregion
  363. #region UpdatePointsPath
  364. /// <summary>
  365. /// Update the path camera according to the vector positions and target
  366. /// positions (if available).
  367. /// </summary>
  368. private void UpdatePointsPath()
  369. {
  370. Vector direction;
  371. Vector position = GetPointPosition(pathByPoints, positionDistances,
  372. elapsedDistance, out direction);
  373. Vector target = Vector.Zero;
  374. if (targetPath != null)
  375. {
  376. if (targetPath.Length == 1)
  377. {
  378. target = targetPath[0];
  379. }
  380. else
  381. {
  382. target = GetPointPosition(targetPath, targetDistances,
  383. elapsedDistance * targetDistanceFactor, out direction);
  384. }
  385. }
  386. else
  387. {
  388. target = position + direction;
  389. }
  390. if (elapsedDistance >= positionDistances[positionDistances.Length - 1])
  391. {
  392. IsFinished = true;
  393. }
  394. viewMatrix = Matrix.CreateLookAt(position,
  395. target, UpVector);
  396. UpdateScreenProperties();
  397. }
  398. #endregion
  399. #region GetPointPosition
  400. /// <summary>
  401. /// Get a vector position by an array of positions, precalculated
  402. /// distances (for performance) and a current distance.
  403. /// <para />
  404. /// Basically the logic of a 2d graph but with a 3d vector as the
  405. /// result.
  406. /// </summary>
  407. private Vector GetPointPosition(Vector[] positions,
  408. float[] distances, float distance, out Vector direction)
  409. {
  410. int currentIndex = 0;
  411. if (distance >= distances[distances.Length - 1])
  412. {
  413. direction = positions[positions.Length - 1] -
  414. positions[positions.Length - 2];
  415. return positions[positions.Length - 1];
  416. }
  417. else if (distance <= 0f)
  418. {
  419. direction = positions[1] - positions[0];
  420. return positions[0];
  421. }
  422. else
  423. {
  424. for (int index = 0; index < distances.Length - 1; index++)
  425. {
  426. if (distance >= distances[index] &&
  427. distance < distances[index + 1])
  428. {
  429. currentIndex = index;
  430. break;
  431. }
  432. }
  433. }
  434. float relativeDistance = distance - distances[currentIndex];
  435. direction = positions[currentIndex + 1] - positions[currentIndex];
  436. direction.Normalize();
  437. return positions[currentIndex] + (relativeDistance * direction);
  438. }
  439. #endregion
  440. #endregion
  441. }
  442. }