matrices_and_transforms.rst 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. .. _doc_matrices_and_transforms:
  2. Matrices and transforms
  3. =======================
  4. Introduction
  5. ------------
  6. Before reading this tutorial, it is advised to read the previous one
  7. about :ref:`doc_vector_math` as this one is a direct continuation.
  8. This tutorial will be about *transformations* and will cover a little
  9. about matrices (but not in-depth).
  10. Transformations are most of the time applied as translation, rotation
  11. and scale so they will be considered as priority here.
  12. Oriented coordinate system (OCS)
  13. --------------------------------
  14. Imagine we have a spaceship somewhere in space. In Godot this is easy,
  15. just move the ship somewhere and rotate it:
  16. .. image:: img/tutomat1.png
  17. Ok, so in 2D this looks simple, a position and an angle for a rotation.
  18. But remember, we are grown ups here and don't use angles (plus, angles
  19. are not even that useful when working in 3D).
  20. We should realize that at some point, someone *designed* this
  21. spaceship. Be it for 2D in a drawing such as Paint.net, Gimp,
  22. Photoshop, etc. or in 3D through a 3D DCC tool such as Blender, Max,
  23. Maya, etc.
  24. When it was designed, it was not rotated. It was designed in its own
  25. *coordinate system*.
  26. .. image:: img/tutomat2.png
  27. This means that the tip of the ship has a coordinate, the fin has
  28. another, etc. Be it in pixels (2D) or vertices (3D).
  29. So, let's recall again that the ship was somewhere in space:
  30. .. image:: img/tutomat3.png
  31. How did it get there? What moved it and rotated it from the place it was
  32. designed to its current position? The answer is... a **transform**, the
  33. ship was *transformed* from their original position to the new one. This
  34. allows the ship to be displayed where it is.
  35. But transform is too generic of a term to describe this process. To solve this
  36. puzzle, we will superimpose the ship's original design position at their
  37. current position:
  38. .. image:: img/tutomat4.png
  39. So, we can see that the "design space" has been transformed too. How can
  40. we best represent this transformation? Let's use 3 vectors for this (in
  41. 2D), a unit vector pointing towards X positive, a unit vector pointing
  42. towards Y positive and a translation.
  43. .. image:: img/tutomat5.png
  44. Let's call the 3 vectors "X", "Y" and "Origin", and let's also
  45. superimpose them over the ship so it makes more sense:
  46. .. image:: img/tutomat6.png
  47. Ok, this is nicer, but it still does not make sense. What do X,Y and
  48. Origin have to do with how the ship got there?
  49. Well, let's take the point from top tip of the ship as reference:
  50. .. image:: img/tutomat7.png
  51. And let's apply the following operation to it (and to all the points in
  52. the ship too, but we'll track the top tip as our reference point):
  53. .. tabs::
  54. .. code-tab:: gdscript GDScript
  55. var new_pos = pos - origin
  56. .. code-tab:: csharp
  57. var newPosition = pos - origin;
  58. Doing this to the selected point will move it back to the center:
  59. .. image:: img/tutomat8.png
  60. This was expected, but then let's do something more interesting. Use the
  61. dot product of X and the point, and add it to the dot product of Y and
  62. the point:
  63. .. tabs::
  64. .. code-tab:: gdscript GDScript
  65. var final_pos = Vector2(x.dot(new_pos), y.dot(new_pos))
  66. .. code-tab:: csharp
  67. var finalPosition = new Vector2(x.Dot(newPosition), y.Dot(newPosition));
  68. Then what we have is.. wait a minute, it's the ship in its design
  69. position!
  70. .. image:: img/tutomat9.png
  71. How did this black magic happen? The ship was lost in space, and now
  72. it's back home!
  73. It might seem strange, but it does have plenty of logic. Remember, as
  74. we have seen in the :ref:`doc_vector_math`, what
  75. happened is that the distance to X axis, and the distance to Y axis
  76. were computed. Calculating distance in a direction or plane was one of
  77. the uses for the dot product. This was enough to obtain back the
  78. design coordinates for every point in the ship.
  79. So, what we have been working with so far (with X, Y and Origin) is an
  80. *Oriented Coordinate System*. X an Y are the **Basis**, and *Origin*
  81. is the offset.
  82. Basis
  83. -----
  84. We know what the Origin is. It's where the 0,0 (origin) of the design
  85. coordinate system ended up after being transformed to a new position.
  86. This is why it's called *Origin*, But in practice, it's just an offset
  87. to the new position.
  88. The Basis is more interesting. The basis is the direction of X and Y in the OCS
  89. from the new, transformed location. It tells what has changed, in either 2D or
  90. 3D. The Origin (offset) and Basis (direction) communicate "Hey, the original X
  91. and Y axes of your design are *right here*, pointing towards *these
  92. directions*."
  93. So, let's change the representation of the basis. Instead of 2 vectors,
  94. let's use a *matrix*.
  95. .. image:: img/tutomat10.png
  96. The vectors are up there in the matrix, horizontally. The next problem
  97. now is that.. what is this matrix thing? Well, we'll assume you've never
  98. heard of a matrix.
  99. Transforms in Godot
  100. -------------------
  101. This tutorial will not explain matrix math (and their operations) in
  102. depth, only its practical use. There is plenty of material for that,
  103. which should be a lot simpler to understand after completing this
  104. tutorial. We'll just explain how to use transforms.
  105. Transform2D
  106. ~~~~~~~~~~~
  107. :ref:`class_Transform2D` is a 3x2 matrix. It has 3 Vector2 elements and
  108. it's used for 2D. The "X" axis is the element 0, "Y" axis is the element 1 and
  109. "Origin" is element 2. It's not divided in basis/origin for convenience, due to
  110. its simplicity.
  111. .. tabs::
  112. .. code-tab:: gdscript GDScript
  113. var m = Transform2D()
  114. var x = m[0] # 'X'
  115. var y = m[1] # 'Y'
  116. var o = m[2] # 'Origin'
  117. .. code-tab:: csharp
  118. var m = new Transform2D();
  119. Vector2 x = m[0]; // 'X'
  120. Vector2 y = m[1]; // 'Y'
  121. Vector2 o = m[2]; // 'Origin'
  122. Most operations will be explained with this datatype (Transform2D), but the
  123. same logic applies to 3D.
  124. Identity
  125. ~~~~~~~~
  126. An important transform is the "identity" matrix. This means:
  127. - 'X' Points right: Vector2(1,0)
  128. - 'Y' Points up (or down in pixels): Vector2(0,1)
  129. - 'Origin' is the origin Vector2(0,0)
  130. .. image:: img/tutomat11.png
  131. It's easy to guess that an *identity* matrix is just a matrix that
  132. aligns the transform to its parent coordinate system. It's an *OCS*
  133. that hasn't been translated, rotated or scaled.
  134. .. tabs::
  135. .. code-tab:: gdscript GDScript
  136. # The Transform2D constructor will default to Identity
  137. var m = Transform2D()
  138. print(m)
  139. # prints: ((1, 0), (0, 1), (0, 0))
  140. .. code-tab:: csharp
  141. // Due to technical limitations on structs in C# the default
  142. // constructor will contain zero values for all fields.
  143. var defaultTransform = new Transform2D();
  144. GD.Print(defaultTransform);
  145. // prints: ((0, 0), (0, 0), (0, 0))
  146. // Instead we can use the Identity property.
  147. var identityTransform = Transform2D.Identity;
  148. GD.Print(identityTransform);
  149. // prints: ((1, 0), (0, 1), (0, 0))
  150. Operations
  151. ----------
  152. Rotation
  153. ~~~~~~~~
  154. Rotating Transform2D is done by using the "rotated" function:
  155. .. tabs::
  156. .. code-tab:: gdscript GDScript
  157. var m = Transform2D()
  158. m = m.rotated(PI/2) # rotate 90°
  159. .. code-tab:: csharp
  160. var m = Transform2D.Identity;
  161. m = m.Rotated(Mathf.Pi / 2); // rotate 90°
  162. .. image:: img/tutomat12.png
  163. Translation
  164. ~~~~~~~~~~~
  165. There are two ways to translate a Transform2D, the first one is moving
  166. the origin:
  167. .. tabs::
  168. .. code-tab:: gdscript GDScript
  169. # Move 2 units to the right
  170. var m = Transform2D()
  171. m = m.rotated(PI/2) # rotate 90°
  172. m[2] += Vector2(2,0)
  173. .. code-tab:: csharp
  174. // Move 2 units to the right
  175. var m = Transform2D.Identity;
  176. m = m.Rotated(Mathf.Pi / 2); // rotate 90°
  177. m[2] += new Vector2(2, 0);
  178. .. image:: img/tutomat13.png
  179. This will always work in global coordinates.
  180. If instead, translation is desired in *local* coordinates of the
  181. matrix (towards where the *basis* is oriented), there is the
  182. :ref:`Transform2D.translated() <class_Transform2D_translated>`
  183. method:
  184. .. tabs::
  185. .. code-tab:: gdscript GDScript
  186. # Move 2 units towards where the basis is oriented
  187. var m = Transform2D()
  188. m = m.rotated(PI/2) # rotate 90°
  189. m = m.translated( Vector2(2,0) )
  190. .. code-tab:: csharp
  191. // Move 2 units towards where the basis is oriented
  192. var m = Transform2D.Identity;
  193. m = m.Rotated(Mathf.Pi / 2); // rotate 90°
  194. m = m.Translated(new Vector2(2, 0));
  195. .. image:: img/tutomat14.png
  196. You could also transform the global coordinates to local coordinates manually:
  197. .. tabs::
  198. .. code-tab:: gdscript GDScript
  199. var local_pos = m.xform_inv(point)
  200. .. code-tab:: csharp
  201. var localPosition = m.XformInv(point);
  202. But even better, there are helper functions for this as you can read in the next sections.
  203. Local to global coordinates and vice versa
  204. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  205. There are helper methods for converting between local and global coordinates.
  206. There are :ref:`Node2D.to_local() <class_Node2D_to_local>` and :ref:`Node2D.to_global() <class_Node2D_to_global>` for 2D
  207. as well as :ref:`Spatial.to_local() <class_Spatial_to_local>` and :ref:`Spatial.to_global() <class_Spatial_to_global>` for 3D.
  208. Scale
  209. ~~~~~
  210. A matrix can be scaled too. Scaling will multiply the basis vectors by a
  211. vector (X vector by x component of the scale, Y vector by y component of
  212. the scale). It will leave the origin alone:
  213. .. tabs::
  214. .. code-tab:: gdscript GDScript
  215. # Make the basis twice its size.
  216. var m = Transform2D()
  217. m = m.scaled( Vector2(2,2) )
  218. .. code-tab:: csharp
  219. // Make the basis twice its size.
  220. var m = Transform2D.Identity;
  221. m = m.Scaled(new Vector2(2, 2));
  222. .. image:: img/tutomat15.png
  223. These kind of operations in matrices are accumulative. It means every
  224. one starts relative to the previous one. For those who have been living
  225. on this planet long enough, a good reference of how transform works is
  226. this:
  227. .. image:: img/tutomat16.png
  228. A matrix is used similarly to a turtle. The turtle most likely had a
  229. matrix inside (and you are likely learning this many years *after*
  230. discovering Santa is not real).
  231. Transform
  232. ~~~~~~~~~
  233. Transform is the act of switching between coordinate systems. To convert
  234. a position (either 2D or 3D) from "designer" coordinate system to the
  235. OCS, the "xform" method is used.
  236. .. tabs::
  237. .. code-tab:: gdscript GDScript
  238. var new_pos = m.xform(pos)
  239. .. code-tab:: csharp
  240. var newPosition = m.Xform(position);
  241. And only for basis (no translation):
  242. .. tabs::
  243. .. code-tab:: gdscript GDScript
  244. var new_pos = m.basis_xform(pos)
  245. .. code-tab:: csharp
  246. var newPosition = m.BasisXform(position);
  247. Inverse transform
  248. ~~~~~~~~~~~~~~~~~
  249. To do the opposite operation (what we did up there with the rocket), the
  250. "xform_inv" method is used:
  251. .. tabs::
  252. .. code-tab:: gdscript GDScript
  253. var new_pos = m.xform_inv(pos)
  254. .. code-tab:: csharp
  255. var newPosition = m.XformInv(position);
  256. Only for Basis:
  257. .. tabs::
  258. .. code-tab:: gdscript GDScript
  259. var new_pos = m.basis_xform_inv(pos)
  260. .. code-tab:: csharp
  261. var newPosition = m.BasisXformInv(position);
  262. Orthonormal matrices
  263. ^^^^^^^^^^^^^^^^^^^^
  264. However, if the matrix has been scaled (vectors are not unit length),
  265. or the basis vectors are not orthogonal (90°), the inverse transform
  266. will not work.
  267. In other words, inverse transform is only valid in *orthonormal*
  268. matrices. For this, these cases an affine inverse must be computed.
  269. The transform, or inverse transform of an identity matrix will return
  270. the position unchanged:
  271. .. tabs::
  272. .. code-tab:: gdscript GDScript
  273. # Does nothing, pos is unchanged
  274. pos = Transform2D().xform(pos)
  275. .. code-tab:: csharp
  276. // Does nothing, position is unchanged
  277. position = Transform2D.Identity.Xform(position);
  278. Affine inverse
  279. ~~~~~~~~~~~~~~
  280. The affine inverse is a matrix that does the inverse operation of
  281. another matrix, no matter if the matrix has scale or the axis vectors
  282. are not orthogonal. The affine inverse is calculated with the
  283. affine_inverse() method:
  284. .. tabs::
  285. .. code-tab:: gdscript GDScript
  286. var mi = m.affine_inverse()
  287. pos = m.xform(pos)
  288. pos = mi.xform(pos)
  289. # pos is unchanged
  290. .. code-tab:: csharp
  291. var mi = m.AffineInverse();
  292. position = m.Xform(position);
  293. position = mi.Xform(position);
  294. // position is unchanged
  295. If the matrix is orthonormal, then:
  296. .. tabs::
  297. .. code-tab:: gdscript GDScript
  298. # if m is orthonormal, then
  299. pos = mi.xform(pos)
  300. # is the same is
  301. pos = m.xform_inv(pos)
  302. .. code-tab:: csharp
  303. // if m is orthonormal, then
  304. position = mi.Xform(position);
  305. // is the same is
  306. position = m.XformInv(position);
  307. Matrix multiplication
  308. ~~~~~~~~~~~~~~~~~~~~~
  309. Matrices can be multiplied. Multiplication of two matrices "chains"
  310. (concatenates) their transforms.
  311. However, as per convention, multiplication takes place in reverse
  312. order.
  313. Example:
  314. .. tabs::
  315. .. code-tab:: gdscript GDScript
  316. var m = more_transforms * some_transforms
  317. .. code-tab:: csharp
  318. var m = moreTransforms * someTransforms;
  319. To make it a little clearer, this:
  320. .. tabs::
  321. .. code-tab:: gdscript GDScript
  322. pos = transform1.xform(pos)
  323. pos = transform2.xform(pos)
  324. .. code-tab:: csharp
  325. position = transform1.Xform(position);
  326. position = transform2.Xform(position);
  327. Is the same as:
  328. .. tabs::
  329. .. code-tab:: gdscript GDScript
  330. # note the inverse order
  331. pos = (transform2 * transform1).xform(pos)
  332. .. code-tab:: csharp
  333. // note the inverse order
  334. position = (transform2 * transform1).Xform(position);
  335. However, this is not the same:
  336. .. tabs::
  337. .. code-tab:: gdscript GDScript
  338. # yields a different results
  339. pos = (transform1 * transform2).xform(pos)
  340. .. code-tab:: csharp
  341. // yields a different results
  342. position = (transform1 * transform2).Xform(position);
  343. Because in matrix math, A * B is not the same as B * A.
  344. Multiplication by inverse
  345. ~~~~~~~~~~~~~~~~~~~~~~~~~
  346. Multiplying a matrix by its inverse, results in identity:
  347. .. tabs::
  348. .. code-tab:: gdscript GDScript
  349. # No matter what A is, B will be identity
  350. var B = A.affine_inverse() * A
  351. .. code-tab:: csharp
  352. // No matter what A is, B will be identity
  353. var B = A.AffineInverse() * A;
  354. Multiplication by identity
  355. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  356. Multiplying a matrix by identity, will result in the unchanged matrix:
  357. .. tabs::
  358. .. code-tab:: gdscript GDScript
  359. # B will be equal to A
  360. B = A * Transform2D()
  361. .. code-tab:: csharp
  362. // B will be equal to A
  363. var B = A * Transform2D.Identity;
  364. Matrix tips
  365. -----------
  366. When using a transform hierarchy, remember that matrix multiplication is
  367. reversed! To obtain the global transform for a hierarchy, do:
  368. .. tabs::
  369. .. code-tab:: gdscript GDScript
  370. var global_xform = parent_matrix * child_matrix
  371. .. code-tab:: csharp
  372. var globalTransform = parentMatrix * childMatrix;
  373. For 3 levels:
  374. .. tabs::
  375. .. code-tab:: gdscript GDScript
  376. var global_xform = gradparent_matrix * parent_matrix * child_matrix
  377. .. code-tab:: csharp
  378. var globalTransform = grandparentMatrix * parentMatrix * childMatrix;
  379. To make a matrix relative to the parent, use the affine inverse (or
  380. regular inverse for orthonormal matrices).
  381. .. tabs::
  382. .. code-tab:: gdscript GDScript
  383. # transform B from a global matrix to one local to A
  384. var B_local_to_A = A.affine_inverse() * B
  385. .. code-tab:: csharp
  386. // transform B from a global matrix to one local to A
  387. var bLocalToA = A.AffineInverse() * B;
  388. Revert it just like the example above:
  389. .. tabs::
  390. .. code-tab:: gdscript GDScript
  391. # transform back local B to global B
  392. B = A * B_local_to_A
  393. .. code-tab:: csharp
  394. // transform back local B to global B
  395. B = A * bLocalToA;
  396. OK, hopefully this should be enough! Let's complete the tutorial by
  397. moving to 3D matrices.
  398. Matrices & transforms in 3D
  399. ---------------------------
  400. As mentioned before, for 3D, we deal with 3 :ref:`Vector3 <class_Vector3>`
  401. vectors for the rotation matrix, and an extra one for the origin.
  402. Basis
  403. ~~~~~
  404. Godot has a special type for a 3x3 matrix, named :ref:`Basis <class_basis>`.
  405. It can be used to represent a 3D rotation and scale. Sub vectors can be
  406. accessed as:
  407. .. tabs::
  408. .. code-tab:: gdscript GDScript
  409. var m = Basis()
  410. var x = m[0] # Vector3
  411. var y = m[1] # Vector3
  412. var z = m[2] # Vector3
  413. .. code-tab:: csharp
  414. var m = new Basis();
  415. Vector3 x = m[0];
  416. Vector3 y = m[1];
  417. Vector3 z = m[2];
  418. Or, alternatively as:
  419. .. tabs::
  420. .. code-tab:: gdscript GDScript
  421. var m = Basis()
  422. var x = m.x # Vector3
  423. var y = m.y # Vector3
  424. var z = m.z # Vector3
  425. .. code-tab:: csharp
  426. var m = new Basis();
  427. Vector3 x = m.x;
  428. Vector3 y = m.y;
  429. Vector3 z = m.z;
  430. The Identity Basis has the following values:
  431. .. image:: img/tutomat17.png
  432. And can be accessed like this:
  433. .. tabs::
  434. .. code-tab:: gdscript GDScript
  435. # The Basis constructor will default to Identity
  436. var m = Basis()
  437. print(m)
  438. # prints: ((1, 0, 0), (0, 1, 0), (0, 0, 1))
  439. .. code-tab:: csharp
  440. // Due to technical limitations on structs in C# the default
  441. // constructor will contain zero values for all fields.
  442. var defaultBasis = new Basis();
  443. GD.Print(defaultBasis);
  444. // prints: ((0, 0, 0), (0, 0, 0), (0, 0, 0))
  445. // Instead we can use the Identity property.
  446. var identityBasis = Basis.Identity;
  447. GD.Print(identityBasis);;
  448. // prints: ((1, 0, 0), (0, 1, 0), (0, 0, 1))
  449. Rotation in 3D
  450. ~~~~~~~~~~~~~~
  451. Rotation in 3D is more complex than in 2D (translation and scale are the
  452. same), because rotation is an implicit 2D operation. To rotate in 3D, an
  453. *axis*, must be picked. Rotation, then, happens around this axis.
  454. The axis for the rotation must be a *normal vector*. As in, a vector
  455. that can point to any direction, but length must be one (1.0).
  456. .. tabs::
  457. .. code-tab:: gdscript GDScript
  458. #rotate in Y axis
  459. var m3 = Basis()
  460. m3 = m3.rotated( Vector3(0,1,0), PI/2 )
  461. .. code-tab:: csharp
  462. // rotate in Y axis
  463. var m3 = Basis.Identity;
  464. m3 = m3.Rotated(new Vector3(0, 1, 0), Mathf.Pi / 2);
  465. Transform
  466. ~~~~~~~~~
  467. To add the final component to the mix, Godot provides the
  468. :ref:`Transform <class_Transform>` type. Transform has two members:
  469. - *basis* (of type :ref:`Basis <class_Basis>`)
  470. - *origin* (of type :ref:`Vector3 <class_Vector3>`)
  471. Any 3D transform can be represented with Transform, and the separation
  472. of basis and origin makes it easier to work translation and rotation
  473. separately.
  474. An example:
  475. .. tabs::
  476. .. code-tab:: gdscript GDScript
  477. var t = Transform()
  478. pos = t.xform(pos) # transform 3D position
  479. pos = t.basis.xform(pos) # (only rotate)
  480. pos = t.origin + pos # (only translate)
  481. .. code-tab:: csharp
  482. var t = new Transform(Basis.Identity, Vector3.Zero);
  483. position = t.Xform(position); // transform 3D position
  484. position = t.basis.Xform(position); // (only rotate)
  485. position = t.origin + position; // (only translate)