Vector Transformations with World Matrices

From Space Engineers Wiki
Jump to navigation Jump to search

At a Glance

  • World matrices map a vector from an entity’s local space (body frame) to the world space (world frame)
  • Vectors in Space Engineers are considered row major when doing operations with matrices

Intro

Vector transformations are a bit difficult to understand, so I figured I’d put together a little guide to help y’all use this incredibly helpful tool in your programs :)

Transformations are an incredibly powerful tool that can help simplify 3D problems!

World vs. Body Coordinate Frames

The important coordinate frames that I will speak of come in 2 varieties: World and Body.

A World frame is a coordinate system that stays stationary. The origin does not move, nor do the X ,Y, or Z axes. This is often called the “ground frame” in Earth based engineering applications. The GPS in-game uses the world frame for example.

A Body frame is a coordinate system that is affixed to a particular body (or entity) within the world frame. The origin of this Body frame is free to translate in the world frame, and the local x, y, and z axes of the Body frame can rotate relative to the stationary World frame. This is also sometimes called a “local” frame since it is local to a particular body.

Matrix01.jpg

Direction Vectors vs. Position Vectors

Now let’s clarify the difference between position vectors and direction vectors.

A direction vector simply defines a magnitude and a direction. Examples of direction vectors are velocity and acceleration. They define magnitudes (speed and acceleration) and the direction that that points.

A position vector is a special kind of direction vector that is used to define a point in space measured from the origin of a coordinate system. GPS position is a good example of this.

In other words: all position vectors are direction vectors, but not all direction vectors are position vectors.

Often times we want the position of an object relative to another object.

Matrix02.jpg

In this example, Position A and Position B are position vectors in the world frame. Direction A to B is a direction vector in the world frame that specifies the direction towards B from A.

To get this direction vector we simply subtract! <Direction vector> = <End position vector> - <Start position vector>

Thus: <Direction A to B> = <Position B> - <Position A>

Composition of a World Matrix

A Space Engineers world matrix maps a vector from a entity’s body space to world space. (Blocks and CubeGrids have world matrices as well as most other objects in the game!)

Matrix03.png

The first 3 rows of the World Matrix specify the orientation of the body’s direction vectors in world space. The 4th row specifies the location of the body frame within the world frame. (The 0’s and the 1 in the last column are simply placeholders since Vrage’s matrices are 4x4)

We can use this information to easily convert between body and world frames!

Matrix04.png

Also, in local space, the components of the vector mean specific things. In Space Engineers: +X is Right, +Y is Up, and +Z is Backwards in local space. I prefer Left-Up-Forward notation, but the math is the same as Right-Backwards-Up with some sign changes (as seen in the figures above). Since the world matrix is given to you by the game in most instances, you need not worry about this too much; it is merely two ways to represent the same information. The math is mostly abstracted away from you within the Vector3D.Transform and Vector3D.TransformNormal methods to be introduced soon…

Transformations!!!

Now, you may be asking “What the heck is a Transformation”? Sounds pretty scary and complex, right? Nope!

In Space Engineers, a transformation is simply a matrix-vector multiplication!

Vector transformations allow you to do cool things like convert a world velocity to a block relative local velocity!

Matrix05.png

(Note that the math above is for direction vectors)

Right-Backwards-Up Notation

The math is exactly the same, we just use the opposite directions to get rid of negative signs.

Matrix6.png

In General:

  • Use Vector3D.TransformNormal() for direction vectors (velocity, acceleration, relative positions)
  • Use Vector3D.Transform() for position vectors (but not world to body position transforms)

Body Direction to World Direction

For direction vectors we use Vector3D.TransformNormal() and the world matrix.

Vector3D bodyDirection = new Vector3D(1,2,3);
IMyTerminalBlock referenceBlock = SomeFunctionToAssignThisBlock();

// Convert the local vector to a world direction vector
Vector3D worldDirection = Vector3D.TransformNormal(bodyDirection, referenceBlock.WorldMatrix);

World Direction to Body Direction

To go from world direction to body directions, we use Vector3D.TransformNormal() and the transposed world matrix.

Vector3D worldDirection = new Vector3D(69,69,69); //giggle
IMyTerminalBlock referenceBlock = SomeFunctionToAssignThisBlock();

// Convert worldDirection into a local direction
Vector3D bodyVector = Vector3D.TransformNormal(worldDirection, MatrixD.Transpose(referenceBlock.WorldMatrix)); // Note that we transpose to go from world -> body

Body Position to World Position

To transform a body position to a world position, we use Vector3D.Transform() and the world matrix.

Vector3D bodyPosition = new Vector3D(1,2,3);
IMyTerminalBlock referenceBlock = SomeFunctionToAssignThisBlock();

Vector3D worldPosition = Vector3D.Transform(bodyPosition, referenceBlock.WorldMatrix); // Note no longer TransformNormal

Note how we used Transform instead of TransformNormal because this was a position vector.

Alternative Method

Vector3D bodyPosition = new Vector3D(1,2,3);
IMyTerminalBlock referenceBlock = SomeFunctionToAssignThisBlock();

// Get world position of the local origin
Vector3D referenceWorldPosition = referenceBlock.WorldMatrix.Translation;

// Convert the local vector to a world direction vector
Vector3D worldDirection = Vector3D.TransformNormal(bodyPosition, referenceBlock.WorldMatrix);

// Combine origin position and world direction
Vector3D worldPosition = referenceWorldPosition + worldDirection;

World Position to Body Position

This is a special case!

In order to avoid performance heavy math, we change the world position vector into a world direction vector before our transform. This allows us to avoid using a matrix inversion (avoid these for better performance in your codes).

In this method we use Vector3D.TransformNormal() and a transposed world matrix.

Vector3D worldPosition = new Vector3D(69,69,69); // Giggle...
IMyTerminalBlock referenceBlock = SomeFunctionToAssignThisBlock();

Vector3D referenceWorldPosition = referenceBlock.WorldMatrix.Translation; // block.WorldMatrix.Translation is the same as block.GetPosition() btw

// Convert worldPosition into a world direction
Vector3D worldDirection = worldPosition - referenceWorldPosition ; // This is a vector starting at the reference block pointing at your desired position

// Convert worldDirection into a local direction
Vector3D bodyPosition = Vector3D.TransformNormal(worldDirection, MatrixD.Transpose(referenceBlock.WorldMatrix)); // Note that we transpose to go from world -> body

Alternative Method (NOT RECOMMENDED)

In this method we use Vector3D.Transform() and an INVERTED world matrix.

Vector3D worldPosition = new Vector3D(69,69,69); // Giggle...
IMyTerminalBlock referenceBlock = SomeFunctionToAssignThisBlock();

Vector3D bodyPosition= Vector3D.Transform(localVector, MatrixD.Invert(referenceBlock.WorldMatrix)); // Note no longer TransformNormal
// Also, matrix inversions are bad!

This is notably less performant than the above method since it needs a full matrix inversion as opposed to a simple matrix transpose.



A big thank you goes to Whiplash141 for writing this tutorial. This tutorial was adapted from the original on Malware's MDK wiki, by Malware.