Transform.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. using System;
  2. using System.ComponentModel;
  3. using Microsoft.Xna.Framework;
  4. namespace MonoGame.Extended
  5. {
  6. // Code derived from top answer: http://gamedev.stackexchange.com/questions/113977/should-i-store-local-forward-right-up-vector-or-calculate-when-necessary
  7. [Flags]
  8. internal enum TransformFlags : byte
  9. {
  10. WorldMatrixIsDirty = 1 << 0,
  11. LocalMatrixIsDirty = 1 << 1,
  12. All = WorldMatrixIsDirty | LocalMatrixIsDirty
  13. }
  14. /// <summary>
  15. /// Represents the base class for the position, rotation, and scale of a game object in two-dimensions or
  16. /// three-dimensions.
  17. /// </summary>
  18. /// <typeparam name="TMatrix">The type of the matrix.</typeparam>
  19. /// <remarks>
  20. /// <para>
  21. /// Every game object has a transform which is used to store and manipulate the position, rotation and scale
  22. /// of the object. Every transform can have a parent, which allows to apply position, rotation and scale to game
  23. /// objects hierarchically.
  24. /// </para>
  25. /// <para>
  26. /// This class shouldn't be used directly. Instead use either of the derived classes; <see cref="Transform2" /> or
  27. /// Transform3D.
  28. /// </para>
  29. /// </remarks>
  30. [EditorBrowsable(EditorBrowsableState.Never)]
  31. public abstract class BaseTransform<TMatrix>
  32. where TMatrix : struct
  33. {
  34. private TransformFlags _flags = TransformFlags.All; // dirty flags, set all dirty flags when created
  35. private TMatrix _localMatrix; // model space to local space
  36. private BaseTransform<TMatrix> _parent; // parent
  37. private TMatrix _worldMatrix; // local space to world space
  38. // internal contructor because people should not be using this class directly; they should use Transform2D or Transform3D
  39. internal BaseTransform()
  40. {
  41. }
  42. /// <summary>
  43. /// Gets the model-to-local space <see cref="Matrix3x2" />.
  44. /// </summary>
  45. /// <value>
  46. /// The model-to-local space <see cref="Matrix3x2" />.
  47. /// </value>
  48. public TMatrix LocalMatrix
  49. {
  50. get
  51. {
  52. RecalculateLocalMatrixIfNecessary(); // attempt to update local matrix upon request if it is dirty
  53. return _localMatrix;
  54. }
  55. }
  56. /// <summary>
  57. /// Gets the local-to-world space <see cref="Matrix3x2" />.
  58. /// </summary>
  59. /// <value>
  60. /// The local-to-world space <see cref="Matrix3x2" />.
  61. /// </value>
  62. public TMatrix WorldMatrix
  63. {
  64. get
  65. {
  66. RecalculateWorldMatrixIfNecessary(); // attempt to update world matrix upon request if it is dirty
  67. return _worldMatrix;
  68. }
  69. }
  70. /// <summary>
  71. /// Gets or sets the parent instance.
  72. /// </summary>
  73. /// <value>
  74. /// The parent instance.
  75. /// </value>
  76. /// <remarks>
  77. /// <para>
  78. /// Setting <see cref="Parent" /> to a non-null instance enables this instance to
  79. /// inherit the position, rotation, and scale of the parent instance. Setting <see cref="Parent" /> to
  80. /// <code>null</code> disables the inheritance altogether for this instance.
  81. /// </para>
  82. /// </remarks>
  83. [EditorBrowsable(EditorBrowsableState.Never)]
  84. public BaseTransform<TMatrix> Parent
  85. {
  86. get { return _parent; }
  87. set
  88. {
  89. if (_parent == value)
  90. return;
  91. var oldParentTransform = Parent;
  92. _parent = value;
  93. OnParentChanged(oldParentTransform, value);
  94. }
  95. }
  96. public event Action TransformBecameDirty; // observer pattern for when the world (or local) matrix became dirty
  97. public event Action TranformUpdated; // observer pattern for after the world (or local) matrix was re-calculated
  98. /// <summary>
  99. /// Gets the model-to-local space <see cref="Matrix3x2" />.
  100. /// </summary>
  101. /// <param name="matrix">The model-to-local space <see cref="Matrix3x2" />.</param>
  102. public void GetLocalMatrix(out TMatrix matrix)
  103. {
  104. RecalculateLocalMatrixIfNecessary();
  105. matrix = _localMatrix;
  106. }
  107. /// <summary>
  108. /// Gets the local-to-world space <see cref="Matrix3x2" />.
  109. /// </summary>
  110. /// <param name="matrix">The local-to-world space <see cref="Matrix3x2" />.</param>
  111. public void GetWorldMatrix(out TMatrix matrix)
  112. {
  113. RecalculateWorldMatrixIfNecessary();
  114. matrix = _worldMatrix;
  115. }
  116. protected internal void LocalMatrixBecameDirty()
  117. {
  118. _flags |= TransformFlags.LocalMatrixIsDirty;
  119. }
  120. protected internal void WorldMatrixBecameDirty()
  121. {
  122. _flags |= TransformFlags.WorldMatrixIsDirty;
  123. TransformBecameDirty?.Invoke();
  124. }
  125. private void OnParentChanged(BaseTransform<TMatrix> oldParent, BaseTransform<TMatrix> newParent)
  126. {
  127. var parent = oldParent;
  128. while (parent != null)
  129. {
  130. parent.TransformBecameDirty -= ParentOnTransformBecameDirty;
  131. parent = parent.Parent;
  132. }
  133. parent = newParent;
  134. while (parent != null)
  135. {
  136. parent.TransformBecameDirty += ParentOnTransformBecameDirty;
  137. parent = parent.Parent;
  138. }
  139. }
  140. private void ParentOnTransformBecameDirty()
  141. {
  142. _flags |= TransformFlags.All;
  143. }
  144. private void RecalculateWorldMatrixIfNecessary()
  145. {
  146. if ((_flags & TransformFlags.WorldMatrixIsDirty) == 0)
  147. return;
  148. RecalculateLocalMatrixIfNecessary();
  149. RecalculateWorldMatrix(ref _localMatrix, out _worldMatrix);
  150. _flags &= ~TransformFlags.WorldMatrixIsDirty;
  151. TranformUpdated?.Invoke();
  152. }
  153. protected internal abstract void RecalculateWorldMatrix(ref TMatrix localMatrix, out TMatrix matrix);
  154. private void RecalculateLocalMatrixIfNecessary()
  155. {
  156. if ((_flags & TransformFlags.LocalMatrixIsDirty) == 0)
  157. return;
  158. RecalculateLocalMatrix(out _localMatrix);
  159. _flags &= ~TransformFlags.LocalMatrixIsDirty;
  160. WorldMatrixBecameDirty();
  161. }
  162. protected internal abstract void RecalculateLocalMatrix(out TMatrix matrix);
  163. }
  164. /// <summary>
  165. /// Represents the position, rotation, and scale of a two-dimensional game object.
  166. /// </summary>
  167. /// <seealso cref="BaseTransform{Matrix3x2D}" />
  168. /// <seealso cref="IMovable" />
  169. /// <seealso cref="IRotatable" />
  170. /// <seealso cref="IScalable" />
  171. /// <remarks>
  172. /// <para>
  173. /// Every game object has a transform which is used to store and manipulate the position, rotation and scale
  174. /// of the object. Every transform can have a parent, which allows to apply position, rotation and scale to game
  175. /// objects hierarchically.
  176. /// </para>
  177. /// </remarks>
  178. public class Transform2 : BaseTransform<Matrix3x2>, IMovable, IRotatable, IScalable
  179. {
  180. private Vector2 _position;
  181. private float _rotation;
  182. private Vector2 _scale = Vector2.One;
  183. public Transform2()
  184. : this(Vector2.Zero, 0, Vector2.One)
  185. {
  186. }
  187. public Transform2(float x, float y, float rotation = 0, float scaleX = 1, float scaleY = 1)
  188. : this(new Vector2(x, y), rotation, new Vector2(scaleX, scaleY))
  189. {
  190. }
  191. public Transform2(Vector2? position = null, float rotation = 0, Vector2? scale = null)
  192. {
  193. Position = position ?? Vector2.Zero;
  194. Rotation = rotation;
  195. Scale = scale ?? Vector2.One;
  196. }
  197. /// <summary>
  198. /// Gets the world position.
  199. /// </summary>
  200. /// <value>
  201. /// The world position.
  202. /// </value>
  203. public Vector2 WorldPosition => WorldMatrix.Translation;
  204. /// <summary>
  205. /// Gets the world scale.
  206. /// </summary>
  207. /// <value>
  208. /// The world scale.
  209. /// </value>
  210. public Vector2 WorldScale => WorldMatrix.Scale;
  211. /// <summary>
  212. /// Gets the world rotation angle in radians.
  213. /// </summary>
  214. /// <value>
  215. /// The world rotation angle in radians.
  216. /// </value>
  217. public float WorldRotation => WorldMatrix.Rotation;
  218. /// <summary>
  219. /// Gets or sets the local position.
  220. /// </summary>
  221. /// <value>
  222. /// The local position.
  223. /// </value>
  224. public Vector2 Position
  225. {
  226. get { return _position; }
  227. set
  228. {
  229. _position = value;
  230. LocalMatrixBecameDirty();
  231. WorldMatrixBecameDirty();
  232. }
  233. }
  234. /// <summary>
  235. /// Gets or sets the local rotation angle in radians.
  236. /// </summary>
  237. /// <value>
  238. /// The local rotation angle in radians.
  239. /// </value>
  240. public float Rotation
  241. {
  242. get { return _rotation; }
  243. set
  244. {
  245. _rotation = value;
  246. LocalMatrixBecameDirty();
  247. WorldMatrixBecameDirty();
  248. }
  249. }
  250. /// <summary>
  251. /// Gets or sets the local scale.
  252. /// </summary>
  253. /// <value>
  254. /// The local scale.
  255. /// </value>
  256. public Vector2 Scale
  257. {
  258. get { return _scale; }
  259. set
  260. {
  261. _scale = value;
  262. LocalMatrixBecameDirty();
  263. WorldMatrixBecameDirty();
  264. }
  265. }
  266. protected internal override void RecalculateWorldMatrix(ref Matrix3x2 localMatrix, out Matrix3x2 matrix)
  267. {
  268. if (Parent != null)
  269. {
  270. Parent.GetWorldMatrix(out matrix);
  271. Matrix3x2.Multiply(ref localMatrix, ref matrix, out matrix);
  272. }
  273. else
  274. {
  275. matrix = localMatrix;
  276. }
  277. }
  278. protected internal override void RecalculateLocalMatrix(out Matrix3x2 matrix)
  279. {
  280. matrix = Matrix3x2.CreateScale(_scale) *
  281. Matrix3x2.CreateRotationZ(_rotation) *
  282. Matrix3x2.CreateTranslation(_position);
  283. }
  284. public override string ToString()
  285. {
  286. return $"Position: {Position}, Rotation: {Rotation}, Scale: {Scale}";
  287. }
  288. }
  289. /// <summary>
  290. /// Represents the position, rotation, and scale of a three-dimensional game object.
  291. /// </summary>
  292. /// <seealso cref="BaseTransform{Matrix}" />
  293. /// <remarks>
  294. /// <para>
  295. /// Every game object has a transform which is used to store and manipulate the position, rotation and scale
  296. /// of the object. Every transform can have a parent, which allows to apply position, rotation and scale to game
  297. /// objects hierarchically.
  298. /// </para>
  299. /// </remarks>
  300. public class Transform3 : BaseTransform<Matrix> {
  301. private Vector3 _position;
  302. private Quaternion _rotation;
  303. private Vector3 _scale = Vector3.One;
  304. public Transform3(Vector3? position = null, Quaternion? rotation = null, Vector3? scale = null) {
  305. Position = position ?? Vector3.Zero;
  306. Rotation = rotation ?? Quaternion.Identity;
  307. Scale = scale ?? Vector3.One;
  308. }
  309. /// <summary>
  310. /// Gets the world position.
  311. /// </summary>
  312. /// <value>
  313. /// The world position.
  314. /// </value>
  315. public Vector3 WorldPosition => WorldMatrix.Translation;
  316. /// <summary>
  317. /// Gets the world scale.
  318. /// </summary>
  319. /// <value>
  320. /// The world scale.
  321. /// </value>
  322. public Vector3 WorldScale {
  323. get {
  324. Vector3 scale = Vector3.Zero;
  325. Quaternion rotation = Quaternion.Identity;
  326. Vector3 translation = Vector3.Zero;
  327. WorldMatrix.Decompose(out scale, out rotation, out translation);
  328. return scale;
  329. }
  330. }
  331. /// <summary>
  332. /// Gets the world rotation quaternion in radians.
  333. /// </summary>
  334. /// <value>
  335. /// The world rotation quaternion in radians.
  336. /// </value>
  337. public Quaternion WorldRotation {
  338. get {
  339. Vector3 scale = Vector3.Zero;
  340. Quaternion rotation = Quaternion.Identity;
  341. Vector3 translation = Vector3.Zero;
  342. WorldMatrix.Decompose(out scale, out rotation, out translation);
  343. return rotation;
  344. }
  345. }
  346. /// <summary>
  347. /// Gets or sets the local position.
  348. /// </summary>
  349. /// <value>
  350. /// The local position.
  351. /// </value>
  352. public Vector3 Position {
  353. get { return _position; }
  354. set {
  355. _position = value;
  356. LocalMatrixBecameDirty();
  357. WorldMatrixBecameDirty();
  358. }
  359. }
  360. /// <summary>
  361. /// Gets or sets the local rotation quaternion in radians.
  362. /// </summary>
  363. /// <value>
  364. /// The local rotation quaternion in radians.
  365. /// </value>
  366. public Quaternion Rotation {
  367. get { return _rotation; }
  368. set {
  369. _rotation = value;
  370. LocalMatrixBecameDirty();
  371. WorldMatrixBecameDirty();
  372. }
  373. }
  374. /// <summary>
  375. /// Gets or sets the local scale.
  376. /// </summary>
  377. /// <value>
  378. /// The local scale.
  379. /// </value>
  380. public Vector3 Scale {
  381. get { return _scale; }
  382. set {
  383. _scale = value;
  384. LocalMatrixBecameDirty();
  385. WorldMatrixBecameDirty();
  386. }
  387. }
  388. protected internal override void RecalculateWorldMatrix(ref Matrix localMatrix, out Matrix matrix) {
  389. if (Parent != null) {
  390. Parent.GetWorldMatrix(out matrix);
  391. Matrix.Multiply(ref localMatrix, ref matrix, out matrix);
  392. }
  393. else {
  394. matrix = localMatrix;
  395. }
  396. }
  397. protected internal override void RecalculateLocalMatrix(out Matrix matrix) {
  398. matrix = Matrix.CreateScale(_scale) *
  399. Matrix.CreateFromQuaternion(_rotation) *
  400. Matrix.CreateTranslation(_position);
  401. }
  402. public override string ToString() {
  403. return $"Position: {Position}, Rotation: {Rotation}, Scale: {Scale}";
  404. }
  405. }
  406. }