PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Rendering/Cameras/BaseCamera.cs

#
C# | 710 lines | 420 code | 73 blank | 217 comment | 15 complexity | 0f3a6da11b0025fc0151367f44d62b12 MD5 | raw file
Possible License(s): Apache-2.0
  1. using System.IO;
  2. using Delta.Engine;
  3. using Delta.Engine.Dynamic;
  4. using Delta.InputSystem;
  5. using Delta.Utilities;
  6. using Delta.Utilities.Datatypes;
  7. using Delta.Utilities.Helpers;
  8. namespace Delta.Rendering.Cameras
  9. {
  10. /// <summary>
  11. /// Abstract base camera class, provides some useful constants and properties
  12. /// that are commonly used in most camera classes. Is used as a dynamic
  13. /// module which updates itself directly after the input module updated, so
  14. /// the camera always have the newest input data to use for camera movement.
  15. /// </summary>
  16. public abstract class BaseCamera : DynamicModule, ISaveLoadBinary
  17. {
  18. #region Constants
  19. /// <summary>
  20. /// The current version of the implementation of this Camera class.
  21. /// </summary>
  22. private const int ImplementationVersion = 1;
  23. /// <summary>
  24. /// The near plane of the camera.
  25. /// </summary>
  26. public const float DefaultNearPlane = 0.45f; //0.75f;//1.0f;//0.1f;
  27. /// <summary>
  28. /// The far plane distance for the camera. See below for previous values.
  29. /// </summary>
  30. public const float DefaultFarPlane = 120.0f;
  31. /// <summary>
  32. /// The default FieldOfView is 60 degrees.
  33. /// </summary>
  34. public static readonly float DefaultFieldOfView =
  35. MathHelper.DegreeToRadians(60);
  36. /// <summary>
  37. /// The direction vector for the camera to show where is top.
  38. /// </summary>
  39. public static readonly Vector UpVector = Vector.UnitZ;
  40. /// <summary>
  41. /// Speed multiplier if "fast" key is pressed
  42. /// </summary>
  43. protected const float FastMultiplier = 5.0f;
  44. /// <summary>
  45. /// Speed multiplier if "slow" key is pressed
  46. /// </summary>
  47. protected const float SlowMultiplier = 0.2f;
  48. #endregion
  49. #region Current (Static)
  50. /// <summary>
  51. /// Currently active camera.
  52. /// </summary>
  53. public static BaseCamera Current
  54. {
  55. get
  56. {
  57. if (currentlyUsedCamera == null)
  58. {
  59. // We got no camera yet? Create one!
  60. currentlyUsedCamera = new LookAtCamera(new Vector(10, 10, 10));
  61. }
  62. return currentlyUsedCamera;
  63. }
  64. set
  65. {
  66. currentlyUsedCamera = value;
  67. }
  68. }
  69. #endregion
  70. #region Position (Public)
  71. /// <summary>
  72. /// The position of this camera.
  73. /// </summary>
  74. public virtual Vector Position
  75. {
  76. get
  77. {
  78. return position;
  79. }
  80. set
  81. {
  82. position = value;
  83. }
  84. }
  85. #endregion
  86. #region LookDirection (Public)
  87. /// <summary>
  88. /// The look direction
  89. /// </summary>
  90. public virtual Vector LookDirection
  91. {
  92. get
  93. {
  94. return lookDirection;
  95. }
  96. set
  97. {
  98. lookDirection = value;
  99. }
  100. }
  101. #endregion
  102. #region Target (Public)
  103. /// <summary>
  104. /// The camera target position
  105. /// </summary>
  106. public virtual Vector Target
  107. {
  108. get
  109. {
  110. return target;
  111. }
  112. set
  113. {
  114. target = value;
  115. }
  116. }
  117. #endregion
  118. #region Distance (Public)
  119. /// <summary>
  120. /// The distance between target position and camera position
  121. /// </summary>
  122. public virtual float Distance
  123. {
  124. get
  125. {
  126. // Make sure that we never have a distance of exactly 0, because
  127. // then the "View" matrix will be bad and "crash".
  128. return (distance != 0.0f)
  129. ? distance
  130. : MathHelper.Epsilon;
  131. }
  132. set
  133. {
  134. distance = value;
  135. }
  136. }
  137. #endregion
  138. #region Rotation (Public)
  139. /// <summary>
  140. /// Camera rotation vector in yaw pitch roll format
  141. /// Rotation Vector should be in degrees
  142. /// </summary>
  143. public virtual Vector Rotation
  144. {
  145. get
  146. {
  147. return rotation;
  148. }
  149. set
  150. {
  151. rotation = value;
  152. }
  153. }
  154. #endregion
  155. #region FieldOfView (Public)
  156. private float internalFieldOfView;
  157. /// <summary>
  158. /// Field of view, will cause the ProjectionMatrix to be updated the
  159. /// next time InternalRun is called.
  160. /// </summary>
  161. public float FieldOfView
  162. {
  163. get
  164. {
  165. return internalFieldOfView;
  166. }
  167. set
  168. {
  169. internalFieldOfView = value;
  170. needToUpdateProjectionMatrix = true;
  171. }
  172. }
  173. #endregion
  174. #region FarPlane (Public)
  175. private float internalFarPlane;
  176. /// <summary>
  177. /// Far plane
  178. /// </summary>
  179. public float FarPlane
  180. {
  181. get
  182. {
  183. return internalFarPlane;
  184. }
  185. set
  186. {
  187. internalFarPlane = value;
  188. needToUpdateProjectionMatrix = true;
  189. }
  190. }
  191. #endregion
  192. #region NearPlane (Public)
  193. private float internalNearPlane;
  194. /// <summary>
  195. /// Near plane
  196. /// </summary>
  197. public float NearPlane
  198. {
  199. get
  200. {
  201. return internalNearPlane;
  202. }
  203. set
  204. {
  205. internalNearPlane = value;
  206. needToUpdateProjectionMatrix = true;
  207. }
  208. }
  209. #endregion
  210. #region IsActive (Public)
  211. /// <summary>
  212. /// Determines if the camera is active or not.
  213. /// Only one camera may be active at a given time
  214. /// </summary>
  215. public bool IsActive
  216. {
  217. get
  218. {
  219. return (currentlyUsedCamera == this);
  220. }
  221. }
  222. #endregion
  223. #region IsLocked (Public)
  224. /// <summary>
  225. /// If set to true this camera should ignore all user input, which would
  226. /// otherwise be used to control the camera (via Mouse, Touch and Keys).
  227. /// </summary>
  228. public bool IsLocked
  229. {
  230. get;
  231. set;
  232. }
  233. #endregion
  234. #region ViewMatrix (Public)
  235. /// <summary>
  236. /// Public ViewMatrix for updating the screen properties.
  237. /// </summary>
  238. public Matrix ViewMatrix
  239. {
  240. get
  241. {
  242. return viewMatrix;
  243. }
  244. }
  245. #endregion
  246. #region ViewFrustum (Public)
  247. /// <summary>
  248. /// An array of 6 planes, one plane for each surface of the viewFrustum.
  249. /// Used order: 0=Left, 1=Right, 2=Top, 3=Bottom, 4=Near, 5=Far
  250. /// </summary>
  251. public Plane[] ViewFrustum
  252. {
  253. get;
  254. private set;
  255. }
  256. #endregion
  257. #region VertexLookupTable (Public)
  258. /// <summary>
  259. /// A 2 dimensional array used in checking boxes vs frustum.
  260. /// The first field in the array represents the 6 different planes.
  261. /// The second field represents X, Y, Z normal values of each field.
  262. /// 0 = xMax, 1 = yMax, 2 = zMax
  263. /// 3 = xMin, 4 = yMin, 5 = zMin.
  264. /// Example: If the bottom (3) plane Y (1) normal is positive (negative = 4)
  265. /// then VertexLookupTable[3,1] = 1.
  266. /// </summary>
  267. public int[,] VertexLookupTable
  268. {
  269. get;
  270. set;
  271. }
  272. #endregion
  273. #region Protected
  274. #region projectionMatrix (Protected)
  275. /// <summary>
  276. /// The projection matrix is only calculated once in the constructor
  277. /// and when the window resizes (because it depends on the AspectRatio).
  278. /// It is only used in UpdateViewMatrix, which will just set
  279. /// ScreenSpace.ViewProjection3D and ScreenSpace.ViewInverse.
  280. /// </summary>
  281. protected static Matrix projectionMatrix = Matrix.Identity;
  282. #endregion
  283. #region viewMatrix (Protected)
  284. /// <summary>
  285. /// Protected viewMatrix for updating the screen properties.
  286. /// </summary>
  287. protected static Matrix viewMatrix = Matrix.Identity;
  288. #endregion
  289. #region position (Protected)
  290. /// <summary>
  291. /// Position
  292. /// </summary>
  293. protected Vector position = Vector.Zero;
  294. #endregion
  295. #region lookDirection (Protected)
  296. /// <summary>
  297. /// Look direction
  298. /// </summary>
  299. protected Vector lookDirection = Vector.Zero;
  300. #endregion
  301. #region target (Protected)
  302. /// <summary>
  303. /// Target
  304. /// </summary>
  305. protected Vector target = Vector.Zero;
  306. #endregion
  307. #region distance (Protected)
  308. /// <summary>
  309. /// Distance
  310. /// </summary>
  311. protected float distance;
  312. #endregion
  313. #region rotation (Protected)
  314. /// <summary>
  315. /// Rotation
  316. /// </summary>
  317. protected Vector rotation = Vector.Zero;
  318. #endregion
  319. #region AlwaysNeedsUpdate (Protected)
  320. /// <summary>
  321. /// If this camera always needs to be updated.
  322. /// Mainly used for camera implementations which contain multiple cameras,
  323. /// so they are able to decide if they need to switch control.
  324. /// </summary>
  325. protected bool AlwaysNeedsUpdate
  326. {
  327. get;
  328. set;
  329. }
  330. #endregion
  331. #endregion
  332. #region Private
  333. #region currentlyUsedCamera (Private)
  334. /// <summary>
  335. /// Currently used camera
  336. /// </summary>
  337. private static BaseCamera currentlyUsedCamera;
  338. #endregion
  339. /// <summary>
  340. /// Helper flag to indicate we need to update the projection matrix because
  341. /// something like NearPlane, FarPlane or FieldOfView has changed.
  342. /// </summary>
  343. private static bool needToUpdateProjectionMatrix;
  344. #endregion
  345. #region Constructors
  346. /// <summary>
  347. /// Create the camera
  348. /// </summary>
  349. /// <param name="setPosition">Camera initial position.</param>
  350. protected BaseCamera(Vector setPosition)
  351. : base("Camera", typeof(Input))
  352. {
  353. FieldOfView = DefaultFieldOfView;
  354. FarPlane = DefaultFarPlane;
  355. NearPlane = DefaultNearPlane;
  356. // At first we set the properties.
  357. Position = setPosition;
  358. ViewFrustum = new Plane[6];
  359. VertexLookupTable = new int[6,3];
  360. Application.Window.ResizeEvent += UpdateProjectionMatrix;
  361. // Update the projection matrix at first because we only update
  362. // it if something changed in the aspectRatio.
  363. UpdateProjectionMatrix();
  364. // Only update newest and last initialized camera!
  365. currentlyUsedCamera = this;
  366. SetupInputCommands();
  367. }
  368. #endregion
  369. #region Destructor
  370. /// <summary>
  371. /// Releases unmanaged resources and performs other cleanup operations
  372. /// before the <see cref="BaseCamera"/> is reclaimed by garbage collection.
  373. /// </summary>
  374. ~BaseCamera()
  375. {
  376. Dispose();
  377. }
  378. #endregion
  379. #region ISaveLoadBinary Members
  380. /// <summary>
  381. /// Load camera from BinaryStream.
  382. /// </summary>
  383. /// <param name="reader">Binary reader used for reading data.</param>
  384. public virtual void Load(BinaryReader reader)
  385. {
  386. // First read the implementation version
  387. int version = reader.ReadInt32();
  388. switch (version)
  389. {
  390. // Version 1
  391. case 1:
  392. // Now just read the saved values
  393. position.Load(reader);
  394. lookDirection.Load(reader);
  395. target.Load(reader);
  396. distance = reader.ReadSingle();
  397. rotation.Load(reader);
  398. FieldOfView = reader.ReadSingle();
  399. FarPlane = reader.ReadSingle();
  400. NearPlane = reader.ReadSingle();
  401. AlwaysNeedsUpdate = reader.ReadBoolean();
  402. IsLocked = reader.ReadBoolean();
  403. break;
  404. default:
  405. Log.InvalidVersionWarning(GetType().Name + ": " + Name,
  406. version, ImplementationVersion);
  407. break;
  408. }
  409. }
  410. /// <summary>
  411. /// Save camera data to BinaryWritter.
  412. /// </summary>
  413. /// <param name="dataWriter">Binary writer used for writing data.</param>
  414. public virtual void Save(BinaryWriter dataWriter)
  415. {
  416. // Save the implementation version first
  417. dataWriter.Write(ImplementationVersion);
  418. // and then all required values
  419. position.Save(dataWriter);
  420. lookDirection.Save(dataWriter);
  421. target.Save(dataWriter);
  422. dataWriter.Write(distance);
  423. rotation.Save(dataWriter);
  424. dataWriter.Write(FieldOfView);
  425. dataWriter.Write(FarPlane);
  426. dataWriter.Write(NearPlane);
  427. dataWriter.Write(AlwaysNeedsUpdate);
  428. dataWriter.Write(IsLocked);
  429. }
  430. #endregion
  431. #region Dispose (Public)
  432. /// <summary>
  433. /// Dispose the camera and most important remove it from the camera list.
  434. /// </summary>
  435. public override void Dispose()
  436. {
  437. // Kill module and everything that might be connected to this
  438. base.Dispose();
  439. if (currentlyUsedCamera == this)
  440. {
  441. currentlyUsedCamera = null;
  442. }
  443. // Remove it from event listening too
  444. Application.Window.ResizeEvent -= UpdateProjectionMatrix;
  445. // Also inform the module base class that this module can be removed from
  446. // the module list
  447. IsValid = false;
  448. }
  449. #endregion
  450. #region Activate (Public)
  451. /// <summary>
  452. /// Make the camera current (e.g. index zero on the camera list),
  453. /// so it'll be updated.
  454. /// </summary>
  455. /// <returns>
  456. /// True if this camera has been set as active one, false if it was active
  457. /// last frame.
  458. /// </returns>
  459. public bool Activate()
  460. {
  461. if (currentlyUsedCamera != this)
  462. {
  463. currentlyUsedCamera = this;
  464. return true;
  465. }
  466. return false;
  467. }
  468. #endregion
  469. #region UpdateProjectionMatrix (Public)
  470. /// <summary>
  471. /// Update projection matrix. In the base class this simply creates a
  472. /// normal perspective camera with the Screen aspect ratio. In overridden
  473. /// classes this can be changed (for example for isometric projection).
  474. /// </summary>
  475. public virtual void UpdateProjectionMatrix()
  476. {
  477. // Create a standard perspective matrix
  478. needToUpdateProjectionMatrix = false;
  479. projectionMatrix = Matrix.CreatePerspective(FieldOfView,
  480. ScreenSpace.AspectRatio, NearPlane, FarPlane);
  481. }
  482. #endregion
  483. #region Run (Public)
  484. /// <summary>
  485. /// Update the camera every tick directly after input updated its
  486. /// values so we always have the newest input data.
  487. /// </summary>
  488. public override void Run()
  489. {
  490. if (IsActive ||
  491. AlwaysNeedsUpdate)
  492. {
  493. InternalRun();
  494. }
  495. }
  496. #endregion
  497. #region Methods (Private)
  498. #region UpdateScreenProperties
  499. /// <summary>
  500. /// Update the screen properties ViewProjection3D and ViewInverse.
  501. /// Called from the InternalRun method.
  502. /// </summary>
  503. protected static void UpdateScreenProperties()
  504. {
  505. // Little bit ugly code, but nicely optimized :)
  506. Matrix.Multiply(ref viewMatrix, ref projectionMatrix,
  507. ref ScreenSpace.InternalViewProjection3D);
  508. Matrix.Invert(ref viewMatrix, ref ScreenSpace.InternalViewInverse);
  509. }
  510. #endregion
  511. #region SetupInputCommands
  512. /// <summary>
  513. /// Setup the input commands for controlling this camera.
  514. /// Can be derived in the camera implementing classes.
  515. /// Normally no input is attached.
  516. /// </summary>
  517. protected virtual void SetupInputCommands()
  518. {
  519. }
  520. #endregion
  521. #region UpdateViewMatrix
  522. /// <summary>
  523. /// Update view matrix of this camera.
  524. /// </summary>
  525. protected virtual void UpdateViewMatrix()
  526. {
  527. // Create a standard look at matrix
  528. viewMatrix =
  529. Matrix.CreateLookAt(position, position + LookDirection, UpVector);
  530. }
  531. #endregion
  532. #region BuildViewFrustum
  533. /// <summary>
  534. /// Build view frustum
  535. /// http://www.chadvernon.com/blog/resources/directx9/frustum-culling/
  536. /// </summary>
  537. protected virtual void BuildViewFrustum()
  538. {
  539. Matrix viewProjection3D = ScreenSpace.InternalViewProjection3D;
  540. // The order of left, right, near, far, top and bottom is supposedly
  541. // the best order to test the planes in. Very small difference either
  542. // way but it's better than nothing. This might change if the game is
  543. // top down for example, then it might be faster to check the top and
  544. // bottom planes earlier and to check far and near planes last.
  545. // Left plane
  546. ViewFrustum[0].Normal.X = viewProjection3D.M14 + viewProjection3D.M11;
  547. ViewFrustum[0].Normal.Y = viewProjection3D.M24 + viewProjection3D.M21;
  548. ViewFrustum[0].Normal.Z = viewProjection3D.M34 + viewProjection3D.M31;
  549. ViewFrustum[0].Distance = viewProjection3D.M44 + viewProjection3D.M41;
  550. // Right plane
  551. ViewFrustum[1].Normal.X = viewProjection3D.M14 - viewProjection3D.M11;
  552. ViewFrustum[1].Normal.Y = viewProjection3D.M24 - viewProjection3D.M21;
  553. ViewFrustum[1].Normal.Z = viewProjection3D.M34 - viewProjection3D.M31;
  554. ViewFrustum[1].Distance = viewProjection3D.M44 - viewProjection3D.M41;
  555. // Near plane
  556. ViewFrustum[2].Normal.X = viewProjection3D.M14 + viewProjection3D.M13;
  557. ViewFrustum[2].Normal.Y = viewProjection3D.M24 + viewProjection3D.M23;
  558. ViewFrustum[2].Normal.Z = viewProjection3D.M34 + viewProjection3D.M33;
  559. ViewFrustum[2].Distance = viewProjection3D.M44 + viewProjection3D.M43;
  560. // Far plane
  561. ViewFrustum[3].Normal.X = viewProjection3D.M14 - viewProjection3D.M13;
  562. ViewFrustum[3].Normal.Y = viewProjection3D.M24 - viewProjection3D.M23;
  563. ViewFrustum[3].Normal.Z = viewProjection3D.M34 - viewProjection3D.M33;
  564. ViewFrustum[3].Distance = viewProjection3D.M44 - viewProjection3D.M43;
  565. // Top plane
  566. ViewFrustum[4].Normal.X = viewProjection3D.M14 - viewProjection3D.M12;
  567. ViewFrustum[4].Normal.Y = viewProjection3D.M24 - viewProjection3D.M22;
  568. ViewFrustum[4].Normal.Z = viewProjection3D.M34 - viewProjection3D.M32;
  569. ViewFrustum[4].Distance = viewProjection3D.M44 - viewProjection3D.M42;
  570. // Bottom plane
  571. ViewFrustum[5].Normal.X = viewProjection3D.M14 + viewProjection3D.M12;
  572. ViewFrustum[5].Normal.Y = viewProjection3D.M24 + viewProjection3D.M22;
  573. ViewFrustum[5].Normal.Z = viewProjection3D.M34 + viewProjection3D.M32;
  574. ViewFrustum[5].Distance = viewProjection3D.M44 + viewProjection3D.M42;
  575. // Build the lookUpTable
  576. // 0 = xMax, 1 = yMax, 2 = zMax
  577. // 3 = xMin, 4 = yMin, 5 = zMin
  578. for (int i = 0; i < 6; i++)
  579. {
  580. // First we Normalize the plane
  581. // Normalization is only needed if we are testing against spheres,
  582. // currently all testing is done versus boxes so it's not needed.
  583. //viewFrustum[i].Normalize();
  584. // Check if the normal is >= 0 then set the index to max.
  585. // else we set the index to point at the min value.
  586. // we do this for all 3 values (X, Y, Z).
  587. if (ViewFrustum[i].Normal.X >= 0)
  588. {
  589. VertexLookupTable[i, 0] = 0;
  590. } // if
  591. else
  592. {
  593. VertexLookupTable[i, 0] = 3;
  594. } // else
  595. if (ViewFrustum[i].Normal.Y >= 0)
  596. {
  597. VertexLookupTable[i, 1] = 1;
  598. } // if
  599. else
  600. {
  601. VertexLookupTable[i, 1] = 4;
  602. } // else
  603. if (ViewFrustum[i].Normal.Z >= 0)
  604. {
  605. VertexLookupTable[i, 2] = 2;
  606. } // if
  607. else
  608. {
  609. VertexLookupTable[i, 2] = 5;
  610. } // else
  611. } // for
  612. }
  613. #endregion
  614. #region InternalRun
  615. /// <summary>
  616. /// Internal run method for overriding in the derived classes.
  617. /// </summary>
  618. protected virtual void InternalRun()
  619. {
  620. // The projection matrix is usually unchanged (except FarPlane,
  621. // NearPlane, FieldOfView or ScreenSpace.AspectRatio changes, e.g. when
  622. // the screen resolution changes).
  623. if (needToUpdateProjectionMatrix)
  624. {
  625. UpdateProjectionMatrix();
  626. }
  627. // The view matrix usually changes often (not all frames, but often).
  628. UpdateViewMatrix();
  629. // This means we also need to update the ViewProjection3D and ViewInverse
  630. UpdateScreenProperties();
  631. // And finally build the view frustum from that for culling!
  632. BuildViewFrustum();
  633. }
  634. #endregion
  635. #endregion
  636. }
  637. }