Bläddra i källkod

Rewrite Matrices and Transforms article

Aaron Franke 6 år sedan
förälder
incheckning
f0ab9e05fc
44 ändrade filer med 446 tillägg och 554 borttagningar
  1. BIN
      tutorials/math/img/empty_grid.png
  2. BIN
      tutorials/math/img/matrices_and_transforms/3d-identity.png
  3. BIN
      tutorials/math/img/matrices_and_transforms/3d-identity.xcf
  4. BIN
      tutorials/math/img/matrices_and_transforms/apply.png
  5. BIN
      tutorials/math/img/matrices_and_transforms/apply.xcf
  6. BIN
      tutorials/math/img/matrices_and_transforms/identity-godot.png
  7. BIN
      tutorials/math/img/matrices_and_transforms/identity-godot.xcf
  8. BIN
      tutorials/math/img/matrices_and_transforms/identity-grid.png
  9. BIN
      tutorials/math/img/matrices_and_transforms/identity-grid.xcf
  10. BIN
      tutorials/math/img/matrices_and_transforms/identity-origin.png
  11. BIN
      tutorials/math/img/matrices_and_transforms/identity-origin.xcf
  12. BIN
      tutorials/math/img/matrices_and_transforms/identity.png
  13. BIN
      tutorials/math/img/matrices_and_transforms/identity.xcf
  14. BIN
      tutorials/math/img/matrices_and_transforms/putting-all-together.png
  15. BIN
      tutorials/math/img/matrices_and_transforms/rotate1.png
  16. BIN
      tutorials/math/img/matrices_and_transforms/rotate1.xcf
  17. BIN
      tutorials/math/img/matrices_and_transforms/rotate2.png
  18. BIN
      tutorials/math/img/matrices_and_transforms/rotate2.xcf
  19. BIN
      tutorials/math/img/matrices_and_transforms/rotate3.png
  20. BIN
      tutorials/math/img/matrices_and_transforms/rotate3.xcf
  21. BIN
      tutorials/math/img/matrices_and_transforms/scale.png
  22. BIN
      tutorials/math/img/matrices_and_transforms/scale.xcf
  23. BIN
      tutorials/math/img/matrices_and_transforms/shear.png
  24. BIN
      tutorials/math/img/matrices_and_transforms/shear.xcf
  25. BIN
      tutorials/math/img/matrices_and_transforms/translate.png
  26. BIN
      tutorials/math/img/matrices_and_transforms/translate.xcf
  27. BIN
      tutorials/math/img/tutomat1.png
  28. BIN
      tutorials/math/img/tutomat10.png
  29. BIN
      tutorials/math/img/tutomat11.png
  30. BIN
      tutorials/math/img/tutomat12.png
  31. BIN
      tutorials/math/img/tutomat13.png
  32. BIN
      tutorials/math/img/tutomat14.png
  33. BIN
      tutorials/math/img/tutomat15.png
  34. BIN
      tutorials/math/img/tutomat16.png
  35. BIN
      tutorials/math/img/tutomat17.png
  36. BIN
      tutorials/math/img/tutomat2.png
  37. BIN
      tutorials/math/img/tutomat3.png
  38. BIN
      tutorials/math/img/tutomat4.png
  39. BIN
      tutorials/math/img/tutomat5.png
  40. BIN
      tutorials/math/img/tutomat6.png
  41. BIN
      tutorials/math/img/tutomat7.png
  42. BIN
      tutorials/math/img/tutomat8.png
  43. BIN
      tutorials/math/img/tutomat9.png
  44. 446 554
      tutorials/math/matrices_and_transforms.rst

BIN
tutorials/math/img/empty_grid.png


BIN
tutorials/math/img/matrices_and_transforms/3d-identity.png


BIN
tutorials/math/img/matrices_and_transforms/3d-identity.xcf


BIN
tutorials/math/img/matrices_and_transforms/apply.png


BIN
tutorials/math/img/matrices_and_transforms/apply.xcf


BIN
tutorials/math/img/matrices_and_transforms/identity-godot.png


BIN
tutorials/math/img/matrices_and_transforms/identity-godot.xcf


BIN
tutorials/math/img/matrices_and_transforms/identity-grid.png


BIN
tutorials/math/img/matrices_and_transforms/identity-grid.xcf


BIN
tutorials/math/img/matrices_and_transforms/identity-origin.png


BIN
tutorials/math/img/matrices_and_transforms/identity-origin.xcf


BIN
tutorials/math/img/matrices_and_transforms/identity.png


BIN
tutorials/math/img/matrices_and_transforms/identity.xcf


BIN
tutorials/math/img/matrices_and_transforms/putting-all-together.png


BIN
tutorials/math/img/matrices_and_transforms/rotate1.png


BIN
tutorials/math/img/matrices_and_transforms/rotate1.xcf


BIN
tutorials/math/img/matrices_and_transforms/rotate2.png


BIN
tutorials/math/img/matrices_and_transforms/rotate2.xcf


BIN
tutorials/math/img/matrices_and_transforms/rotate3.png


BIN
tutorials/math/img/matrices_and_transforms/rotate3.xcf


BIN
tutorials/math/img/matrices_and_transforms/scale.png


BIN
tutorials/math/img/matrices_and_transforms/scale.xcf


BIN
tutorials/math/img/matrices_and_transforms/shear.png


BIN
tutorials/math/img/matrices_and_transforms/shear.xcf


BIN
tutorials/math/img/matrices_and_transforms/translate.png


BIN
tutorials/math/img/matrices_and_transforms/translate.xcf


BIN
tutorials/math/img/tutomat1.png


BIN
tutorials/math/img/tutomat10.png


BIN
tutorials/math/img/tutomat11.png


BIN
tutorials/math/img/tutomat12.png


BIN
tutorials/math/img/tutomat13.png


BIN
tutorials/math/img/tutomat14.png


BIN
tutorials/math/img/tutomat15.png


BIN
tutorials/math/img/tutomat16.png


BIN
tutorials/math/img/tutomat17.png


BIN
tutorials/math/img/tutomat2.png


BIN
tutorials/math/img/tutomat3.png


BIN
tutorials/math/img/tutomat4.png


BIN
tutorials/math/img/tutomat5.png


BIN
tutorials/math/img/tutomat6.png


BIN
tutorials/math/img/tutomat7.png


BIN
tutorials/math/img/tutomat8.png


BIN
tutorials/math/img/tutomat9.png


+ 446 - 554
tutorials/math/matrices_and_transforms.rst

@@ -6,725 +6,617 @@ Matrices and transforms
 Introduction
 ------------
 
-Before reading this tutorial, it is advised to read the previous one
-about :ref:`doc_vector_math` as this one is a direct continuation.
+Before reading this tutorial, we recommend that you thoroughly read
+and understand the :ref:`doc_vector_math` tutorial, as this tutorial
+requires a knowledge of vectors.
 
-This tutorial will be about *transformations* and will cover a little
-about matrices (but not in-depth).
+This tutorial is about *transformations* and how we represent them
+in Godot using matrices. It is not a full in-depth guide to matrices.
+Transformations are most of the time applied as translation, rotation,
+and scale, so we will focus on how to represent those with matrices.
 
-Transformations are most of the time applied as translation, rotation
-and scale so they will be considered as priority here.
+Most of this guide focuses on 2D, using :ref:`class_Transform2D` and
+:ref:`class_Vector2`, but the way things work in 3D is very similar.
 
-Oriented coordinate system (OCS)
---------------------------------
+.. note:: As mentioned in the previous tutorial, it is important to
+          remember that in Godot, the Y axis points *down* in 2D.
+          This is the opposite of how most schools teach linear
+          algebra, with the Y axis pointing up.
 
-Imagine we have a spaceship somewhere in space. In Godot this is easy,
-just move the ship somewhere and rotate it:
+.. note:: The convention is that the X axis is red, the Y axis is
+          green, and the Z axis is blue. This tutorial is color-coded
+          to match these conventions, but we will also represent
+          the origin vector with a blue color.
 
-.. image:: img/tutomat1.png
+Matrix components and the Identity matrix
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Ok, so in 2D this looks simple, a position and an angle for a rotation.
-But remember, we are grown ups here and don't use angles (plus, angles
-are not even that useful when working in 3D).
+The identity matrix represents a transform with no translation,
+no rotation, and no scale. Let's start by looking at the identity
+matrix and how its components relate to how it visually appears.
 
-We should realize that at some point, someone *designed* this
-spaceship. Be it for 2D in a drawing such as Paint.net, Gimp,
-Photoshop, etc. or in 3D through a 3D DCC tool such as Blender, Max,
-Maya, etc.
+.. image:: img/matrices_and_transforms/identity.png
 
-When it was designed, it was not rotated. It was designed in its own
-*coordinate system*.
+Matrices have rows and columns, and a transformation matrix has
+specific conventions on what each does.
 
-.. image:: img/tutomat2.png
+In the image above, we can see that the red X vector is represented
+by the first column of the matrix, and the green Y vector is
+likewise represented by the second column. A change to the columns
+will change these vectors. We will see how they can be manipulated
+in the next few examples.
 
-This means that the tip of the ship has a coordinate, the fin has
-another, etc. Be it in pixels (2D) or vertices (3D).
+You should not worry about manipulating rows directly, as we usually
+work with columns. However, you can think of the rows of the matrix
+as showing which vectors contribute to moving in a given direction.
 
-So, let's recall again that the ship was somewhere in space:
+When we refer to a value such as `t.x.y`, that's the Y component of
+the X column vector. In other words, the bottom-left of the matrix.
+Similarly, `t.x.x` is top-left, `t.y.x` is top-right,and `t.y.y`
+is bottom-right, where `t` is the Transform2D.
 
-.. image:: img/tutomat3.png
+Scaling the transformation matrix
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-How did it get there? What moved it and rotated it from the place it was
-designed to its current position? The answer is... a **transform**, the
-ship was *transformed* from their original position to the new one. This
-allows the ship to be displayed where it is.
+Applying a scale is one of the easiest operations to understand.
+Let's start by placing the Godot logo underneath our vectors
+so that we can visually see the effects on an object:
 
-But transform is too generic of a term to describe this process. To solve this
-puzzle, we will superimpose the ship's original design position at their
-current position:
+.. image:: img/matrices_and_transforms/identity-godot.png
 
-.. image:: img/tutomat4.png
+Now, to scale the matrix, all we need to do is multiply each
+component by the scale we want. Let's scale it up by 2. 1 times 2
+becomes 2, and 0 times 2 becomes 0, so we end up with this:
 
-So, we can see that the "design space" has been transformed too. How can
-we best represent this transformation? Let's use 3 vectors for this (in
-2D), a unit vector pointing towards X positive, a unit vector pointing
-towards Y positive and a translation.
+.. image:: img/matrices_and_transforms/scale.png
 
-.. image:: img/tutomat5.png
-
-Let's call the 3 vectors "X", "Y" and "Origin", and let's also
-superimpose them over the ship so it makes more sense:
-
-.. image:: img/tutomat6.png
-
-Ok, this is nicer, but it still does not make sense. What do X,Y and
-Origin have to do with how the ship got there?
-
-Well, let's take the point from top tip of the ship as reference:
-
-.. image:: img/tutomat7.png
-
-And let's apply the following operation to it (and to all the points in
-the ship too, but we'll track the top tip as our reference point):
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    var new_pos = pos - origin
-
- .. code-tab:: csharp
-
-    var newPosition = pos - origin;
-
-Doing this to the selected point will move it back to the center:
-
-.. image:: img/tutomat8.png
-
-This was expected, but then let's do something more interesting. Use the
-dot product of X and the point, and add it to the dot product of Y and
-the point:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    var final_pos = Vector2(x.dot(new_pos), y.dot(new_pos))
-
- .. code-tab:: csharp
-
-    var finalPosition = new Vector2(x.Dot(newPosition), y.Dot(newPosition));
-
-Then what we have is.. wait a minute, it's the ship in its design
-position!
-
-.. image:: img/tutomat9.png
-
-How did this black magic happen? The ship was lost in space, and now
-it's back home!
-
-It might seem strange, but it does have plenty of logic. Remember, as
-we have seen in the :ref:`doc_vector_math`, what
-happened is that the distance to X axis, and the distance to Y axis
-were computed. Calculating distance in a direction or plane was one of
-the uses for the dot product. This was enough to obtain back the
-design coordinates for every point in the ship.
-
-So, what we have been working with so far (with X, Y and Origin) is an
-*Oriented Coordinate System*. X an Y are the **Basis**, and *Origin*
-is the offset.
-
-Basis
------
-
-We know what the Origin is. It's where the 0,0 (origin) of the design
-coordinate system ended up after being transformed to a new position.
-This is why it's called *Origin*, But in practice, it's just an offset
-to the new position.
-
-The Basis is more interesting. The basis is the direction of X and Y in the OCS
-from the new, transformed location. It tells what has changed, in either 2D or
-3D. The Origin (offset) and Basis (direction) communicate "Hey, the original X
-and Y axes of your design are *right here*, pointing towards *these
-directions*."
-
-So, let's change the representation of the basis. Instead of 2 vectors,
-let's use a *matrix*.
-
-.. image:: img/tutomat10.png
-
-The vectors are up there in the matrix, horizontally. The next problem
-now is that.. what is this matrix thing? Well, we'll assume you've never
-heard of a matrix.
-
-Transforms in Godot
--------------------
-
-This tutorial will not explain matrix math (and their operations) in
-depth, only its practical use. There is plenty of material for that,
-which should be a lot simpler to understand after completing this
-tutorial. We'll just explain how to use transforms.
-
-Transform2D
-~~~~~~~~~~~
-
-:ref:`class_Transform2D` is a 3x2 matrix. It has 3 Vector2 elements and
-it's used for 2D. The "X" axis is the element 0, "Y" axis is the element 1 and
-"Origin" is element 2. It's not divided in basis/origin for convenience, due to
-its simplicity.
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    var m = Transform2D()
-    var x = m[0] # 'X'
-    var y = m[1] # 'Y'
-    var o = m[2] # 'Origin'
-
- .. code-tab:: csharp
-
-    var m = new Transform2D();
-    Vector2 x = m[0]; // 'X'
-    Vector2 y = m[1]; // 'Y'
-    Vector2 o = m[2]; // 'Origin'
-
-Most operations will be explained with this datatype (Transform2D), but the
-same logic applies to 3D.
-
-Identity
-~~~~~~~~
-
-An important transform is the "identity" matrix. This means:
-
--  'X' Points right: Vector2(1,0)
--  'Y' Points up (or down in pixels): Vector2(0,1)
--  'Origin' is the origin Vector2(0,0)
-
-.. image:: img/tutomat11.png
-
-It's easy to guess that an *identity* matrix is just a matrix that
-aligns the transform to its parent coordinate system. It's an *OCS*
-that hasn't been translated, rotated or scaled.
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    # The Transform2D constructor will default to Identity
-    var m = Transform2D()
-    print(m)
-    # prints: ((1, 0), (0, 1), (0, 0))
-
-
- .. code-tab:: csharp
-
-    // Due to technical limitations on structs in C# the default
-    // constructor will contain zero values for all fields.
-    var defaultTransform = new Transform2D();
-    GD.Print(defaultTransform);
-    // prints: ((0, 0), (0, 0), (0, 0))
-
-    // Instead we can use the Identity property.
-    var identityTransform = Transform2D.Identity;
-    GD.Print(identityTransform);
-    // prints: ((1, 0), (0, 1), (0, 0))
-
-Operations
-----------
-
-Rotation
-~~~~~~~~
-
-Rotating Transform2D is done by using the "rotated" function:
+To do this in code, we can simply multiply each of the vectors:
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    var m = Transform2D()
-    m = m.rotated(PI/2) # rotate 90°
+    var t = Transform2D()
+    # Scale
+    t.x *= 2
+    t.y *= 2
+    transform = t # Change the node's transform to what we just calculated.
 
  .. code-tab:: csharp
+    Transform2D t = new Transform2D();
+    // Scale
+    t.x *= 2;
+    t.y *= 2;
+    Transform = t; // Change the node's transform to what we just calculated.
 
-    var m = Transform2D.Identity;
-    m = m.Rotated(Mathf.Pi / 2); // rotate 90°
+If we wanted to return it to its original scale, we can multiply
+each component by 0.5. That's pretty much all there is to scaling
+a transformation matrix.
 
-.. image:: img/tutomat12.png
+To calculate the object's scale from an existing transformation
+matrix, you can use `length()` on each of the column vectors.
 
-Translation
-~~~~~~~~~~~
+.. note:: In actual projects, you can use the `scaled()`
+          method to perform scaling.
 
-There are two ways to translate a Transform2D, the first one is moving
-the origin:
+Rotating the transformation matrix
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+We'll start the same way as earlier, with the Godot logo underneath
+the identity matrix:
 
-    # Move 2 units to the right
-    var m = Transform2D()
-    m = m.rotated(PI/2) # rotate 90°
-    m[2] += Vector2(2,0)
+.. image:: img/matrices_and_transforms/identity-godot.png
 
- .. code-tab:: csharp
+As an example, let's say we want to rotate our Godot logo clockwise
+by 90 degrees. Right now the X axis points right and the Y axis
+points down. If we rotate these in our head, we would logically
+see that the new X axis should point down and the new Y axis
+should point left.
 
-    // Move 2 units to the right
-    var m = Transform2D.Identity;
-    m = m.Rotated(Mathf.Pi / 2); // rotate 90°
-    m[2] += new Vector2(2, 0);
+You can imagine that you grab both the Godot logo and its vectors,
+and then spin it around the center. Wherever you finish spinning,
+the orientation of the vectors determines what the matrix is.
 
-.. image:: img/tutomat13.png
+We need to represent "down" and "left" in normal coordinates,
+so means we'll set X to (0, 1) and Y to (-1, 0). These are
+also the values of `Vector2.DOWN` and `Vector2.LEFT`.
+When we do this, we get the desired result of rotating the object:
 
-This will always work in global coordinates.
+.. image:: img/matrices_and_transforms/rotate1.png
 
-If instead, translation is desired in *local* coordinates of the
-matrix (towards where the *basis* is oriented), there is the
-:ref:`Transform2D.translated() <class_Transform2D_method_translated>`
-method:
+If you have trouble understanding the above, try this excercise:
+Cut a square of paper, draw X and Y vectors on top of it, place
+it on graph paper, then rotate it and note the endpoints.
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+To perform rotation in code, we need to be able to calculate
+the values programatically. This image shows the formulas needed
+to calculate the transformation matrix from a rotation angle.
+Don't worry if this part seems complicated, I promise it's the
+hardest thing you need to know.
 
-    # Move 2 units towards where the basis is oriented
-    var m = Transform2D()
-    m = m.rotated(PI/2) # rotate 90°
-    m = m.translated( Vector2(2,0) )
+.. image:: img/matrices_and_transforms/rotate2.png
 
- .. code-tab:: csharp
+.. note:: Godot represents all rotations with radians, not degrees.
+          A full turn is `TAU` or `PI*2` radians, and a quarter
+          turn of 90 degrees is `TAU/4` or `PI/2` radians. Working
+          with `TAU` usually results in more readable code.
 
-    // Move 2 units towards where the basis is oriented
-    var m = Transform2D.Identity;
-    m = m.Rotated(Mathf.Pi / 2); // rotate 90°
-    m = m.Translated(new Vector2(2, 0));
+.. note:: Fun fact: In addition to Y being *down* in Godot, rotation
+          is represented clockwise. This means that all the math and
+          trig functions behave the same as a Y-is-up CCW system,
+          since these differences "cancel out". You can think of
+          rotations in both systems being "from X to Y".
 
-.. image:: img/tutomat14.png
+In order to perform a rotation of 0.5 radians (about 28.65 degrees),
+we simply plug in a value of 0.5 to the formula above and evaluate
+to find what the actual values should be:
 
-You could also transform the global coordinates to local coordinates manually:
+.. image:: img/matrices_and_transforms/rotate3.png
 
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    var local_pos = m.xform_inv(point)
-
- .. code-tab:: csharp
-
-    var localPosition = m.XformInv(point);
-
-But even better, there are helper functions for this as you can read in the next sections.
-
-Local to global coordinates and vice versa
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-There are helper methods for converting between local and global coordinates.
-
-There are :ref:`Node2D.to_local() <class_Node2D_method_to_local>` and :ref:`Node2D.to_global() <class_Node2D_method_to_global>` for 2D
-as well as :ref:`Spatial.to_local() <class_Spatial_method_to_local>` and :ref:`Spatial.to_global() <class_Spatial_method_to_global>` for 3D.
-
-Scale
-~~~~~
-
-A matrix can be scaled too. Scaling will multiply the basis vectors by a
-vector (X vector by x component of the scale, Y vector by y component of
-the scale). It will leave the origin alone:
+Here's how that would be done in code (place the script on a Node2D):
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    # Make the basis twice its size.
-    var m = Transform2D()
-    m = m.scaled( Vector2(2,2) )
+    var t = Transform2D()
+    var rot = 0.5 # The rotation to apply.
+    t.x.x = cos(rot)
+    t.y.y = cos(rot)
+    t.x.y = sin(rot)
+    t.y.x = -sin(rot)
+    transform = t # Change the node's transform to what we just calculated.
 
  .. code-tab:: csharp
+    float rot = 0.5f; // The rotation to apply.
+    Transform2D t = new Transform2D();
+    t.x.x = t.y.y = Mathf.Cos(rot);
+    t.x.y = t.y.x = Mathf.Sin(rot);
+    t.y.x *= -1;
+    Transform = t; // Change the node's transform to what we just calculated.
 
-    // Make the basis twice its size.
-    var m = Transform2D.Identity;
-    m = m.Scaled(new Vector2(2, 2));
+To calculate the object's rotation from an existing transformation
+matrix, you can use `atan2(t.x.y, t.x.x)`, where t is the Transform2D.
 
-.. image:: img/tutomat15.png
+.. note:: In actual projects, you can use the `rotated()`
+          method to perform rotations.
 
-These kind of operations in matrices are accumulative. It means every
-one starts relative to the previous one. For those who have been living
-on this planet long enough, a good reference of how transform works is
-this:
+Basis of the transformation matrix
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. image:: img/tutomat16.png
+So far we have only been working with the `x` and `y`, vectors, which
+are in charge of representing rotation, scale, and/or shearing
+(advanced, covered at the end). The X and Y vectors are together
+called the *basis* of the transformation matrix. The terms "basis"
+and "basis vectors" are important to know.
 
-A matrix is used similarly to a turtle. The turtle most likely had a
-matrix inside (and you are likely learning this many years *after*
-discovering Santa is not real).
+You might have noticed that :ref:`class_Transform2D` actually
+has three :ref:`class_Vector2` values: `x`, `y`, and `origin`.
+The `origin` value is not part of the basis, but it is part of the
+transform, and we need it to represent position. From now on we'll
+keep track of the origin vector in all examples. You can think of
+origin as another column, but it's often better to think of it as
+completely separate.
 
-Transform
-~~~~~~~~~
+Note that in 3D, Godot has a separate :ref:`class_Basis` structure
+for holding the three :ref:`class_Vector3` values of the basis,
+since the code can get complex and it makes sense to separate
+it from :ref:`class_Transform` (which is composed of one
+:ref:`class_Basis` and one extra :ref:`class_Vector3` for the origin).
 
-Transform is the act of switching between coordinate systems. To convert
-a position (either 2D or 3D) from "designer" coordinate system to the
-OCS, the "xform" method is used.
+Translating the transformation matrix
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+Changing the `origin` vector is called a *translating* the transformation
+matrix. Translating is basically a technical term for "moving" the
+object, but it explicitly does not involve any rotation.
 
-    var new_pos = m.xform(pos)
+This should be fairly common sense, assuming that you read and
+understood the vector tutorial, but let's work through an example.
+Again, we'll start with the identity transform, but this time we'll
+also keep track of the origin vector:
 
- .. code-tab:: csharp
+.. image:: img/matrices_and_transforms/identity-origin.png
 
-    var newPosition = m.Xform(position);
+If we want the object to move to a position of (1, 2), we simply need
+to set its `origin` vector to (1, 2):
 
-And only for basis (no translation):
+.. image:: img/matrices_and_transforms/translate.png
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+There is also a `translated()` method, which performs a different
+operation to adding or changing `origin` directly. The `translated()`
+method will translate the object *relative to its own rotation*.
+For example, an object rotated 90 degrees clockwise will move to
+the right when `translated()` with `Vector2.UP`.
 
-    var new_pos = m.basis_xform(pos)
+.. note:: Godot's 2D uses coordinates based on pixels, so in actual
+          projects you will want to translate by hundreds of units.
 
- .. code-tab:: csharp
+Putting it all together
+~~~~~~~~~~~~~~~~~~~~~~~
 
-    var newPosition = m.BasisXform(position);
+We're going to apply everything we mentioned so far onto one transform.
+To follow along, get a simple project with a Sprite set to the Godot logo.
 
-Inverse transform
-~~~~~~~~~~~~~~~~~
+Let's set the translation to (350, 150), rotate by -0.5 rad, and scale by 3.
+I've posted a screenshot, and the code to reproduce it, but I encourage
+you to try and reproduce the screenshot without looking at the code!
 
-To do the opposite operation (what we did up there with the rocket), the
-"xform_inv" method is used:
+.. image:: img/matrices_and_transforms/putting-all-together.png
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    var new_pos = m.xform_inv(pos)
+    var t = Transform2D()
+    # Translation
+    t.origin = Vector2(350, 150)
+    # Rotation
+    var rot = -0.5 # The rotation to apply.
+    t.x.x = cos(rot)
+    t.y.y = cos(rot)
+    t.x.y = sin(rot)
+    t.y.x = -sin(rot)
+    # Scale
+    t.x *= 3
+    t.y *= 3
+    transform = t # Change the node's transform to what we just calculated.
 
  .. code-tab:: csharp
-
-    var newPosition = m.XformInv(position);
-
-Only for Basis:
+    Transform2D t = new Transform2D();
+    // Translation
+    t.origin = new Vector2(350, 150);
+    // Rotation
+    float rot = -0.5f; // The rotation to apply.
+    t.x.x = t.y.y = Mathf.Cos(rot);
+    t.x.y = t.y.x = Mathf.Sin(rot);
+    t.y.x *= -1;
+    // Scale
+    t.x *= 3;
+    t.y *= 3;
+    Transform = t; // Change the node's transform to what we just calculated.
+
+Shearing the transformation matrix (advanced)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. note:: If you are only looking for how to *use* transformation matrices,
+          feel free to skip this section of the tutorial. This section
+          explores an uncommonly used aspect of transformation matrices
+          for the purpose of building an understanding of them.
+
+You may have noticed that a transform has more degrees of freedom than
+the combination of the above actions. The basis of a 2D transformation
+matrix has four total numbers in two :ref:`class_Vector2` values, while
+a rotation value and a Vector2 for scale only has 3 numbers. The high-level
+concept for the missing degree of freedom is called *shearing*.
+
+Normally you will always have the basis vectors perpendicular to each
+other. However, shearing can be useful in some situations, and
+understanding shearing helps you understand how transforms work.
+
+To show you visually how it will look, let's overlay a grid onto the Godot
+logo:
+
+.. image:: img/matrices_and_transforms/identity-grid.png
+
+Each point on this grid is obtained by adding the basis vectors together.
+The bottom-right corner is X + Y, while the top-right corner is X - Y.
+If we change the basis vectors, the entire grid moves with it, as the
+grid is composed of the basis vectors. All lines on the grid that are
+currently parallel will remain parallel no matter what changes we make to
+the basis vectors.
+
+As an example, let's set Y to (1, 1):
+
+.. image:: img/matrices_and_transforms/shear.png
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    var new_pos = m.basis_xform_inv(pos)
+    var t = Transform2D()
+    # Shear by setting Y to (1, 1)
+    t.y = Vector2.ONE
+    transform = t # Change the node's transform to what we just calculated.
 
  .. code-tab:: csharp
+    Transform2D t = new Transform2D();
+    // Shear by setting Y to (1, 1)
+    t.y = Vector2.One;
+    Transform = t; // Change the node's transform to what we just calculated.
 
-    var newPosition = m.BasisXformInv(position);
+.. note:: You can't set the raw values of a Transform2D in the editor,
+          so you *must* use code if you want to shear the object.
 
-Orthonormal matrices
-^^^^^^^^^^^^^^^^^^^^
+Due to the vectors no longer being perpendicular, the object has been
+sheared. The bottom-center of the grid, which is (0, 1) relative
+to itself, is now located at a world position of (1, 1).
 
-However, if the matrix has been scaled (vectors are not unit length),
-or the basis vectors are not orthogonal (90°), the inverse transform
-will not work.
+The intra-object coordinates are called UV coordinates in textures,
+so let's borrow that terminology for here. To find the world position
+from a relative position, the formula is U * X + V * Y, where U and V
+are numbers and X and Y are the basis vectors.
 
-In other words, inverse transform is only valid in *orthonormal*
-matrices. For this, these cases an affine inverse must be computed.
+The bottom-right corner of the grid, which is always at the UV position
+of (1, 1), is at the world position of (2, 1), which is calculated from
+X*1 + Y*1, which is (1, 0) + (1, 1), or (1 + 1, 0 + 1), or (2, 1).
+This matches up with our observation of where the bottom-right corner
+of the image is.
 
-The transform, or inverse transform of an identity matrix will return
-the position unchanged:
+Similarly, the top-right corner of the grid, which is always at the UV
+position of (1, -1), is at the world position of (0, -1), which is calculated
+from X*1 + Y*-1, which is (1, 0) - (1, 1), or (1 - 1, 0 - 1), or (0, -1).
+This matches up with our observation of where the top-right corner
+of the image is.
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+Hopefully you now fully understand the how a transformation matrix affects
+the object, and the relationship between the basis vectors and how the
+object's "UV" or "intra-coordinates" have their world position changed.
 
-    # Does nothing, pos is unchanged
-    pos = Transform2D().xform(pos)
+.. note:: In Godot, all transform math is done relative to the parent node.
+          When we refer to "world position", that would be relative to the
+          node's parent instead, if the node had a parent.
 
- .. code-tab:: csharp
-
-    // Does nothing, position is unchanged
-    position = Transform2D.Identity.Xform(position);
-
-Affine inverse
-~~~~~~~~~~~~~~
+If you would like additional explanation, you should check out
+3Blue1Brown's excellent video about linear transformations:
+https://www.youtube.com/watch?v=kYB8IZa5AuE
 
-The affine inverse is a matrix that does the inverse operation of
-another matrix, no matter if the matrix has scale or the axis vectors
-are not orthogonal. The affine inverse is calculated with the
-affine_inverse() method:
+Practical applications of transforms
+------------------------------------
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+In actual projects, you will usually be working with transforms inside
+transforms by having multiple :ref:`class_Node2D` or :ref:`class_Spatial`
+nodes parented to each other.
 
-    var mi = m.affine_inverse()
-    pos = m.xform(pos)
-    pos = mi.xform(pos)
-    # pos is unchanged
+However, sometimes it's very useful to manually calculate the values we
+need. We will go over how you could use :ref:`class_Transform2D` or
+:ref:`class_Transform` to manually calculate transforms of nodes.
 
- .. code-tab:: csharp
+Converting positions between transforms
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    var mi = m.AffineInverse();
-    position = m.Xform(position);
-    position = mi.Xform(position);
-    // position is unchanged
+There are many cases where you'd want to convert a position in and out of
+a transform. For example, if you have a position relative to the player
+and would like to find the world (parent-relative) position, or if you
+have a world position and want to know where it is relative to the player.
 
-If the matrix is orthonormal, then:
+We can find what a vector relative to the player would be defined in
+world space as using the "xform" method:
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    # if m is orthonormal, then
-    pos = mi.xform(pos)
-    # is the same is
-    pos = m.xform_inv(pos)
+    # World space vector 100 units below the player.
+    print(transform.xform(Vector2(0, 100)))
 
  .. code-tab:: csharp
+    // World space vector 100 units below the player.
+    GD.Print(Transform.Xform(new Vector2(0, 100)));
 
-    // if m is orthonormal, then
-    position = mi.Xform(position);
-    // is the same is
-    position = m.XformInv(position);
-
-Matrix multiplication
-~~~~~~~~~~~~~~~~~~~~~
-
-Matrices can be multiplied. Multiplication of two matrices "chains"
-(concatenates) their transforms.
-
-However, as per convention, multiplication takes place in reverse
-order.
-
-Example:
+And we can use the "xform_inv" method to find a what world space position
+would be if it was instead defined relative to the player:
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    var m = more_transforms * some_transforms
+    # Where is (0, 100) relative to the player?
+    print(transform.xform_inv(Vector2(0, 100)))
 
  .. code-tab:: csharp
+    // Where is (0, 100) relative to the player?
+    GD.Print(Transform.XformInv(new Vector2(0, 100)));
 
-    var m = moreTransforms * someTransforms;
-
-To make it a little clearer, this:
+.. note:: If you know in advance that the transform is positioned at
+          (0, 0), you can use the "basis_xform" or "basis_xform_inv"
+          methods instead, which skip dealing with translation.
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+Moving an object relative to itself
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    pos = transform1.xform(pos)
-    pos = transform2.xform(pos)
+A common operation, especially in 3D games, is to move an object relative
+to itself. For example, in first-person shooter games, you would want the
+character to move forward (-Z axis) when you press the W key.
 
- .. code-tab:: csharp
+Since the basis vectors are the orientation relative to the parent,
+and the origin vector is the position relative to the parent, we can simply
+add multiples of the basis vectors to move an object relative to itself.
 
-    position = transform1.Xform(position);
-    position = transform2.Xform(position);
-
-Is the same as:
+This code moves an object 100 units to its own right:
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    # note the inverse order
-    pos = (transform2 * transform1).xform(pos)
+    transform.origin += transform.x * 100
 
  .. code-tab:: csharp
+    Transform2D t = Transform;
+    t.origin += t.x * 100;
+    Transform = t;
 
-    // note the inverse order
-    position = (transform2 * transform1).Xform(position);
+For moving in 3D, you would need to replace "x" with "basis.x".
 
-However, this is not the same:
+.. note:: In actual projects, you can use `translate_object_local` in 3D
+          or `move_local_x` and `move_local_y` in 2D to do this.
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+Applying transforms onto transforms
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    # yields a different results
-    pos = (transform1 * transform2).xform(pos)
+One of the most important things to know about transforms is how you
+can use several of them together. A parent node's transform affects
+all of its children. Let's dissect an example.
 
- .. code-tab:: csharp
+In this image, the child node has a "2" after the component names
+to distinguish them from the parent node. It might look a bit
+overwhelming with so many numbers, but remember that each number
+is displayed twice (next to the arrows and also in the matrices),
+and that almost half of the numbers are zero.
 
-    // yields a different results
-    position = (transform1 * transform2).Xform(position);
+.. image:: img/matrices_and_transforms/apply.png
 
-Because in matrix math, A * B is not the same as B * A.
+The only transformations going on here are that the parent node has
+been given a scale of (2, 1), the child has been given a scale of
+(0.5, 0.5), and both nodes have been given positions.
 
-Multiplication by inverse
-~~~~~~~~~~~~~~~~~~~~~~~~~
+All child transformations are affected by the parent transformations.
+The child has a scale of (0.5, 0.5), so you would expect it to be
+a 1:1 ratio square, and it is, but only relative to the parent.
+The child's X vector ends up being (1, 0) in world space, because
+it is scaled by the parent's basis vectors.
+Similarly, the child node's `origin` vector is set to (1, 1), but this
+actually moves it (2, 1) in world space, due to the parent node's
+basis vectors.
 
-Multiplying a matrix by its inverse, results in identity:
+To calculate a child transform's world space transform manually, this is
+the code we would use:
 
 .. tabs::
  .. code-tab:: gdscript GDScript
+    # Set up transforms just like in the image, except make positions be 100 times bigger.
+    var parent = Transform2D(2, 0, 0, 1, 100, 200)
+    var child = Transform2D(0.5, 0, 0, 0.5, 100, 100)
 
-    # No matter what A is, B will be identity
-    var B = A.affine_inverse() * A
+    # Calculate the child's world space transform
+    # origin = (2, 0) * 100 + (0, 1) * 100 + (100, 200)
+    var origin = parent.x * child.origin.x + parent.y * child.origin.y + parent.origin
+    # basis_x = (2, 0) * 0.5 + (0, 1) * 0
+    var basis_x = parent.x * child.x.x + parent.y * child.x.y
+    # basis_y = (2, 0) * 0 + (0, 1) * 0.5
+    var basis_y = parent.x * child.y.x + parent.y * child.y.y
 
- .. code-tab:: csharp
-
-    // No matter what A is, B will be identity
-    var B = A.AffineInverse() * A;
-
-Multiplication by identity
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Multiplying a matrix by identity, will result in the unchanged matrix:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    # B will be equal to A
-    B = A * Transform2D()
+    # Change the node's transform to what we just calculated.
+    transform = Transform2D(basis_x, basis_y, origin)
 
  .. code-tab:: csharp
+    // Set up transforms just like in the image, except make positions be 100 times bigger.
+    Transform2D parent = new Transform2D(2, 0, 0, 1, 100, 200);
+    Transform2D child = new Transform2D(0.5f, 0, 0, 0.5f, 100, 100);
 
-    // B will be equal to A
-    var B = A * Transform2D.Identity;
+    // Calculate the child's world space transform
+    // origin = (2, 0) * 100 + (0, 1) * 100 + (100, 200)
+    Vector2 origin = parent.x * child.origin.x + parent.y * child.origin.y + parent.origin;
+    // basisX = (2, 0) * 0.5 + (0, 1) * 0 = (0.5, 0)
+    Vector2 basisX = parent.x * child.x.x + parent.y * child.x.y;
+    // basisY = (2, 0) * 0 + (0, 1) * 0.5 = (0.5, 0)
+    Vector2 basisY = parent.x * child.y.x + parent.y * child.y.y;
 
-Matrix tips
------------
+    // Change the node's transform to what we just calculated.
+    Transform = new Transform2D(basisX, basisY, origin);
 
-When using a transform hierarchy, remember that matrix multiplication is
-reversed! To obtain the global transform for a hierarchy, do:
+In actual projects, we can find the world transform of the child by
+applying one transform onto another using the `*` operator:
 
 .. tabs::
  .. code-tab:: gdscript GDScript
+    # Set up transforms just like in the image, except make positions be 100 times bigger.
+    var parent = Transform2D(Vector2(2, 0), Vector2(0, 1), Vector2(100, 200))
+    var child = Transform2D(Vector2(0.5, 0), Vector2(0, 0.5), Vector2(100, 100))
 
-    var global_xform = parent_matrix * child_matrix
+    # Change the node's transform to what would be the child's world transform.
+    transform = parent * child
 
  .. code-tab:: csharp
+    // Set up transforms just like in the image, except make positions be 100 times bigger.
+    Transform2D parent = new Transform2D(2, 0, 0, 1, 100, 200);
+    Transform2D child = new Transform2D(0.5f, 0, 0, 0.5f, 100, 100);
 
-    var globalTransform = parentMatrix * childMatrix;
+    // Change the node's transform to what would be the child's world transform.
+    Transform = parent * child;
 
-For 3 levels:
+.. note:: When multiplying matrices, order matters! Don't mix them up.
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+Lastly, applying the identity transform will always do nothing.
 
-    var global_xform = gradparent_matrix * parent_matrix * child_matrix
+If you would like additional explanation, you should check out
+3Blue1Brown's excellent video about matrix composition:
+https://www.youtube.com/watch?v=XkY2DOUCWMU
 
- .. code-tab:: csharp
+Inverting a transformation matrix
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-    var globalTransform = grandparentMatrix * parentMatrix * childMatrix;
+The "affine_inverse" function returns a transform that "undoes" the
+previous transform. This can be useful in some situations, but it's
+easier to just provide a few examples.
 
-To make a matrix relative to the parent, use the affine inverse (or
-regular inverse for orthonormal matrices).
+Multiplying an inverse transform by the normal transform undoes all
+transformations:
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    # transform B from a global matrix to one local to A
-    var B_local_to_A = A.affine_inverse() * B
+    var ti = transform.affine_inverse()
+    var t = ti * transform
+    # The transform is the identity transform.
 
  .. code-tab:: csharp
+    Transform2D ti = Transform.AffineInverse();
+    Transform2D t = ti * Transform;
+    // The transform is the identity transform.
 
-    // transform B from a global matrix to one local to A
-    var bLocalToA = A.AffineInverse() * B;
-
-Revert it just like the example above:
+Transforming a position by a transform and its inverse results in the
+same position (same for "xform_inv"):
 
 .. tabs::
  .. code-tab:: gdscript GDScript
-
-    # transform back local B to global B
-    B = A * B_local_to_A
+    var ti = transform.affine_inverse()
+    position = transform.xform(pos)
+    position = ti.xform(pos)
+    # The position is the same as before.
 
  .. code-tab:: csharp
+    Transform2D ti = Transform.AffineInverse();
+    Position = Transform.Xform(Position);
+    Position = ti.Xform(Position);
+    // The position is the same as before.
 
-    // transform back local B to global B
-    B = A * bLocalToA;
-
-OK, hopefully this should be enough! Let's complete the tutorial by
-moving to 3D matrices.
-
-Matrices & transforms in 3D
+How does it all work in 3D?
 ---------------------------
 
-As mentioned before, for 3D, we deal with 3 :ref:`Vector3 <class_Vector3>`
-vectors for the rotation matrix, and an extra one for the origin.
-
-Basis
-~~~~~
-
-Godot has a special type for a 3x3 matrix, named :ref:`Basis <class_Basis>`.
-It can be used to represent a 3D rotation and scale. Sub vectors can be
-accessed as:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    var m = Basis()
-    var x = m[0] # Vector3
-    var y = m[1] # Vector3
-    var z = m[2] # Vector3
-
- .. code-tab:: csharp
-
-    var m = new Basis();
-    Vector3 x = m[0];
-    Vector3 y = m[1];
-    Vector3 z = m[2];
-
-Or, alternatively as:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    var m = Basis()
-    var x = m.x # Vector3
-    var y = m.y # Vector3
-    var z = m.z # Vector3
-
- .. code-tab:: csharp
-
-    var m = new Basis();
-    Vector3 x = m.x;
-    Vector3 y = m.y;
-    Vector3 z = m.z;
-
-The Identity Basis has the following values:
+One of the great things about transformation matrices is that they
+work very similarly between 2D and 3D transformations.
+All of the code and formulas used above for 2D work the same in 3D,
+with 3 exceptions: the addition of a third axis, that each
+axis is of type :ref:`class_Vector3`, and also that Godot stores
+the :ref:`class_Basis` separately from the :ref:`class_Transform`,
+since the math can get complex and it makes sense to separate it.
 
-.. image:: img/tutomat17.png
+All of the concepts for how translation, rotation, scale, and shearing
+work in 3D are all the same compared to 2D. To scale, we take each
+component and multiply it; to rotate, we change where each basis vector
+is pointing; to translate, we manipulate the origin; and to shear, we
+change the basis vectors to be non-perpendicular.
 
-And can be accessed like this:
+.. image:: img/matrices_and_transforms/3d-identity.png
 
-.. tabs::
- .. code-tab:: gdscript GDScript
+If you would like, it's a good idea to play around with transforms
+to get an understanding of how they work. Godot allows you to edit
+3D transform matrices directly from the inspector. You can download
+this project which has colored lines and cubes to help visualize the
+:ref:`class_Basis` vectors and the origin in both 2D and 3D:
+https://github.com/godotengine/godot-demo-projects/tree/master/misc/matrix_transform
 
-    # The Basis constructor will default to Identity
-    var m = Basis()
-    print(m)
-    # prints: ((1, 0, 0), (0, 1, 0), (0, 0, 1))
+.. note:: Spatial's "Matrix" section in Godot 3.2's inspector
+          displays the matrix as transposed, with the columns
+          horizontal and the rows vertical. This may be changed
+          to be less confusing in a future release of Godot.
 
- .. code-tab:: csharp
+.. note:: You cannot edit Node2D's transform matrix directly in Godot 3.2's
+          inspector. This may be changed in a future release of Godot.
 
-    // Due to technical limitations on structs in C# the default
-    // constructor will contain zero values for all fields.
-    var defaultBasis = new Basis();
-    GD.Print(defaultBasis);
-    // prints: ((0, 0, 0), (0, 0, 0), (0, 0, 0))
+If you would like additional explanation, you should check out
+3Blue1Brown's excellent video about 3D linear transformations:
+https://www.youtube.com/watch?v=rHLEWRxRGiM
 
-    // Instead we can use the Identity property.
-    var identityBasis = Basis.Identity;
-    GD.Print(identityBasis);;
-    // prints: ((1, 0, 0), (0, 1, 0), (0, 0, 1))
+Representing rotation in 3D (advanced)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Rotation in 3D
-~~~~~~~~~~~~~~
+The biggest difference between 2D and 3D transformation matrices is
+how you represent rotation by itself without the basis vectors.
 
-Rotation in 3D is more complex than in 2D (translation and scale are the
-same), because rotation is an implicit 2D operation. To rotate in 3D, an
-*axis*, must be picked. Rotation, then, happens around this axis.
+With 2D, we have an easy way (atan2) to switch between a transformation
+matrix and an angle. In 3D, we can't simply represent rotation as one
+number. There is something called Euler angles, which can represent
+rotations as a set of 3 numbers, however they are limited and not very
+useful, except for trivial cases.
 
-The axis for the rotation must be a *normal vector*. As in, a vector
-that can point to any direction, but length must be one (1.0).
+In 3D we do not typically use angles, we either use a transformation basis
+(used pretty much everywhere in Godot), or we use quaternions. Godot can
+represent quaternions using the :ref:`class_Quat` struct. My suggestion
+to you is to completely ignore how they work under-the-hood, because
+they are very complicated and unintuitive.
 
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    #rotate in Y axis
-    var m3 = Basis()
-    m3 = m3.rotated( Vector3(0,1,0), PI/2 )
-
- .. code-tab:: csharp
+However, if you really must know how it works, here are some great
+resources, which you can follow in order:
 
-    // rotate in Y axis
-    var m3 = Basis.Identity;
-    m3 = m3.Rotated(new Vector3(0, 1, 0), Mathf.Pi / 2);
+https://www.youtube.com/watch?v=mvmuCPvRoWQ
 
-Transform
-~~~~~~~~~
-
-To add the final component to the mix, Godot provides the
-:ref:`Transform <class_Transform>` type. Transform has two members:
-
--  *basis* (of type :ref:`Basis <class_Basis>`)
--  *origin* (of type :ref:`Vector3 <class_Vector3>`)
-
-Any 3D transform can be represented with Transform, and the separation
-of basis and origin makes it easier to work translation and rotation
-separately.
-
-An example:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    var t = Transform()
-    pos = t.xform(pos) # transform 3D position
-    pos = t.basis.xform(pos) # (only rotate)
-    pos = t.origin + pos # (only translate)
-
- .. code-tab:: csharp
+https://www.youtube.com/watch?v=d4EgbgTm0Bg
 
-    var t = new Transform(Basis.Identity, Vector3.Zero);
-    position = t.Xform(position); // transform 3D position
-    position = t.basis.Xform(position); // (only rotate)
-    position = t.origin + position; // (only translate)
+https://eater.net/quaternions