|
@@ -77,10 +77,15 @@ Well, let's take the point from top tip of the ship as reference:
|
|
|
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
|
|
@@ -89,10 +94,15 @@ 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!
|
|
|
|
|
@@ -151,13 +161,20 @@ 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.
|
|
@@ -186,11 +203,17 @@ Rotation
|
|
|
|
|
|
Rotating Transform2D is done by using the "rotated" function:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var m = Transform2D()
|
|
|
m = m.rotated(PI/2) # rotate 90°
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ var m = new Transform2D();
|
|
|
+ m = m.Rotated(Mathf.PI / 2); // rotate 90°
|
|
|
+
|
|
|
.. image:: img/tutomat12.png
|
|
|
|
|
|
Translation
|
|
@@ -199,12 +222,20 @@ Translation
|
|
|
There are two ways to translate a Transform2D, the first one is just moving
|
|
|
the origin:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# Move 2 units to the right
|
|
|
var m = Transform2D()
|
|
|
m = m.rotated(PI/2) # rotate 90°
|
|
|
- m[2]+=Vector2(2,0)
|
|
|
+ m[2] += Vector2(2,0)
|
|
|
+
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // Move 2 units to the right
|
|
|
+ var m = new Transform2D();
|
|
|
+ m = m.Rotated(Mathf.PI / 2); // rotate 90°
|
|
|
+ m[2] += new Vector2(2, 0);
|
|
|
|
|
|
.. image:: img/tutomat13.png
|
|
|
|
|
@@ -215,20 +246,33 @@ matrix (towards where the *basis* is oriented), there is the
|
|
|
:ref:`Transform2D.translated() <class_Transform2D_translated>`
|
|
|
method:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# 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) )
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // Move 2 units towards where the basis is oriented
|
|
|
+ var m = new Transform2D();
|
|
|
+ m = m.Rotated(Mathf.PI / 2); // rotate 90°
|
|
|
+ m = m.Translated(new Vector2(2, 0));
|
|
|
+
|
|
|
.. image:: img/tutomat14.png
|
|
|
|
|
|
You could also transform the global coordinates to local coordinates manually:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
- var local_pos = this_transform.xform_inv(point)
|
|
|
+ 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.
|
|
|
|
|
@@ -247,12 +291,19 @@ 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:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# Make the basis twice its size.
|
|
|
var m = Transform2D()
|
|
|
m = m.scaled( Vector2(2,2) )
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // Make the basis twice its size.
|
|
|
+ var m = new Transform2D();
|
|
|
+ m = m.Scaled(new Vector2(2, 2));
|
|
|
+
|
|
|
.. image:: img/tutomat15.png
|
|
|
|
|
|
These kind of operations in matrices are accumulative. It means every
|
|
@@ -273,21 +324,25 @@ 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.
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var new_pos = m.xform(pos)
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ var newPosition = m.Xform(position);
|
|
|
+
|
|
|
And only for basis (no translation):
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var new_pos = m.basis_xform(pos)
|
|
|
|
|
|
-Post - multiplying is also valid:
|
|
|
-
|
|
|
-::
|
|
|
+ .. code-tab:: csharp
|
|
|
|
|
|
- var new_pos = m * pos
|
|
|
+ var newPosition = m.BasisXform(position);
|
|
|
|
|
|
Inverse transform
|
|
|
-----------------
|
|
@@ -295,21 +350,25 @@ Inverse transform
|
|
|
To do the opposite operation (what we did up there with the rocket), the
|
|
|
"xform_inv" method is used:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var new_pos = m.xform_inv(pos)
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ var newPosition = m.XformInv(position);
|
|
|
+
|
|
|
Only for Basis:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var new_pos = m.basis_xform_inv(pos)
|
|
|
|
|
|
-Or pre-multiplication:
|
|
|
-
|
|
|
-::
|
|
|
+ .. code-tab:: csharp
|
|
|
|
|
|
- var new_pos = pos * m
|
|
|
+ var newPosition = m.BasisXformInv(position);
|
|
|
|
|
|
Orthonormal matrices
|
|
|
--------------------
|
|
@@ -324,11 +383,17 @@ matrices. For this, these cases an affine inverse must be computed.
|
|
|
The transform, or inverse transform of an identity matrix will return
|
|
|
the position unchanged:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# Does nothing, pos is unchanged
|
|
|
pos = Transform2D().xform(pos)
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // Does nothing, position is unchanged
|
|
|
+ position = new Transform2D().Xform(position);
|
|
|
+
|
|
|
Affine inverse
|
|
|
--------------
|
|
|
|
|
@@ -337,22 +402,38 @@ 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:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var mi = m.affine_inverse()
|
|
|
- var pos = m.xform(pos)
|
|
|
+ pos = m.xform(pos)
|
|
|
pos = mi.xform(pos)
|
|
|
# pos is unchanged
|
|
|
|
|
|
-If the matrix is orthonormal, then:
|
|
|
+ .. code-tab:: csharp
|
|
|
|
|
|
-::
|
|
|
+ var mi = m.AffineInverse();
|
|
|
+ position = m.Xform(position);
|
|
|
+ position = mi.Xform(position);
|
|
|
+ // position is unchanged
|
|
|
|
|
|
+If the matrix is orthonormal, then:
|
|
|
+
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
+
|
|
|
# if m is orthonormal, then
|
|
|
pos = mi.xform(pos)
|
|
|
# is the same is
|
|
|
pos = m.xform_inv(pos)
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // if m is orthonormal, then
|
|
|
+ position = mi.Xform(position);
|
|
|
+ // is the same is
|
|
|
+ position = m.XformInv(position);
|
|
|
+
|
|
|
Matrix multiplication
|
|
|
---------------------
|
|
|
|
|
@@ -364,31 +445,54 @@ order.
|
|
|
|
|
|
Example:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var m = more_transforms * some_transforms
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ var m = moreTransforms * someTransforms;
|
|
|
+
|
|
|
To make it a little clearer, this:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
pos = transform1.xform(pos)
|
|
|
pos = transform2.xform(pos)
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ position = transform1.Xform(position);
|
|
|
+ position = transform2.Xform(position);
|
|
|
+
|
|
|
Is the same as:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# note the inverse order
|
|
|
pos = (transform2 * transform1).xform(pos)
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // note the inverse order
|
|
|
+ position = (transform2 * transform1).Xform(position);
|
|
|
+
|
|
|
However, this is not the same:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# yields a different results
|
|
|
pos = (transform1 * transform2).xform(pos)
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // yields a different results
|
|
|
+ position = (transform1 * transform2).Xform(position);
|
|
|
+
|
|
|
Because in matrix math, A * B is not the same as B * A.
|
|
|
|
|
|
Multiplication by inverse
|
|
@@ -396,51 +500,85 @@ Multiplication by inverse
|
|
|
|
|
|
Multiplying a matrix by its inverse, results in identity:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# No matter what A is, B will be identity
|
|
|
- B = A.affine_inverse() * A
|
|
|
+ var B = A.affine_inverse() * A
|
|
|
+
|
|
|
+ .. 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()
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // B will be equal to A
|
|
|
+ var B = A * new Transform2D();
|
|
|
+
|
|
|
Matrix tips
|
|
|
-----------
|
|
|
|
|
|
When using a transform hierarchy, remember that matrix multiplication is
|
|
|
reversed! To obtain the global transform for a hierarchy, do:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var global_xform = parent_matrix * child_matrix
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ var globalTransform = parentMatrix * childMatrix;
|
|
|
+
|
|
|
For 3 levels:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var global_xform = gradparent_matrix * parent_matrix * child_matrix
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ var globalTransform = grandparentMatrix * parentMatrix * childMatrix;
|
|
|
+
|
|
|
To make a matrix relative to the parent, use the affine inverse (or
|
|
|
regular inverse for orthonormal matrices).
|
|
|
|
|
|
-::
|
|
|
+.. 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
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // transform B from a global matrix to one local to A
|
|
|
+ var bLocalToA = A.AffineInverse() * B;
|
|
|
+
|
|
|
Revert it just like the example above:
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# transform back local B to global B
|
|
|
- var B = A * B_local_to_A
|
|
|
+ B = A * B_local_to_A
|
|
|
+
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // 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.
|
|
@@ -458,22 +596,38 @@ 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;
|
|
|
+
|
|
|
Basis is also initialized to Identity by default:
|
|
|
|
|
|
.. image:: img/tutomat17.png
|
|
@@ -488,12 +642,19 @@ same), because rotation is an implicit 2D operation. To rotate in 3D, an
|
|
|
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).
|
|
|
|
|
|
-::
|
|
|
+.. tabs::
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
#rotate in Y axis
|
|
|
var m3 = Basis()
|
|
|
m3 = m3.rotated( Vector3(0,1,0), PI/2 )
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
+
|
|
|
+ // rotate in Y axis
|
|
|
+ var m3 = new Basis();
|
|
|
+ m3 = m3.Rotated(new Vector3(0, 1, 0), Mathf.PI / 2);
|
|
|
+
|
|
|
Transform
|
|
|
---------
|
|
|
|
|
@@ -509,9 +670,17 @@ 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
|
|
|
+
|
|
|
+ var t = new Transform();
|
|
|
+ position = t.Xform(position); // transform 3D position
|
|
|
+ position = t.basis.Xform(position); // (only rotate)
|
|
|
+ position = t.origin + position; // (only translate)
|