This page has been translated automatically.
Video Tutorials
Interface
Essentials
Advanced
How To
Professional (SIM)
UnigineEditor
Interface Overview
Assets Workflow
Settings and Preferences
Working With Projects
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Lighting
Sandworm
Using Editor Tools for Specific Tasks
Extending Editor Functionality
Built-in Node Types
Nodes
Objects
Effects
Decals
Light Sources
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
Programming
Setting Up Development Environment
Usage Examples
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
Materials and Shaders
Rebuilding the Engine Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
Content Creation
Content Optimization
Materials
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials
Warning! This version of documentation is OUTDATED, as it describes an older SDK version! Please switch to the documentation for the latest SDK version.
Warning! This version of documentation describes an old SDK version which is no longer supported! Please upgrade to the latest SDK version.

Matrix Transformations

A lot of calculations in UNIGINE are performed by using matrices. Actually, matrix transformations are one of the main concepts of 3D engines. This article contains an explanation of matrix transformations with usage examples.

See Also#

Transformations#

In simple terms, a matrix in 3D graphics is an array of numbers arranged in rows and columns:

Usually, 4x4 matrices are used. Such size (4x4) of matrices is caused by the translation state in 3D space. When you put a new node into the world, it has a 4x4 world transform matrix that defines the position of the node in the world.

In UNIGINE, matrices are column major (column-oriented). Hence, the first column of the transform matrix represents the X vector of the local coordinate system (v1), the second represents the Y vector (v2), the third represents the Z vector (v3), and the fourth represent the translation vector t. First three columns show directions of local coordinate axes (rotation) and the scale of the origin. The last column contains the translation of the local origin relatively to the world origin.

Identity Matrix#

The world origin has the following matrix:

This matrix is called identity matrix, a matrix with ones on the main diagonal, and zeros elsewhere. If a matrix is multiplied by the identity matrix, that won't change anything: the resulting matrix will be the same as it was before multiplying.

If the local origin has the identity matrix, it means the local origin and the world origin are coincident.

Rotation#

To change the orientation of the local origin, the first three columns of the matrix should be changed.

To rotate the origin along different axes, you should use proper matrices:

In the matrices given above, α is a rotation angle along the axis.

The next matrix shows the rotation of the local origin along the Y axis at 45 degrees:

Translation#

The last column of the transform matrix shows the position of the local origin in the world relatively to the world origin. The next matrix shows the translation of the origin. The translation vector t is (3, 0, 2).

Scaling#

The length of the vector shows the scale coefficient along the axis.

To calculate the vector length (also known as magnitude), you should find a square root of the sum of the squares of vector components. The formula is the following:

Vector Length
|vector length| = √(x² + y² + z²)

The following matrix scales the local origin up to 2 units along all axes.

Cumulating Transformations#

The order of matrix transformations in code is very important.

If you want to implement a sequence of cumulating transformations, the transformations order in code should be as follows:

Transformation order
TransformedVector = TransformationMatrixN * ... * TransformationMatrix2 * TransformationMatrix1 * Vector

Transformations are applied one by one starting from TransformationMatrix1 and ending with TransformationMatrixN.

Example#

This example shows the difference between two orders of matrix transformations.

The code example below gets the material ball object. In the first case, rotation is followed by translation and in the second case, translation is followed by rotation.

In the AppWorldLogic.cs file, do the following:

  • Define the Node instance for the material ball.
  • Enable the visualizer by passing show_visualizer 1 command to the run() function of the Console class.
  • Get the material ball from the Editor.
  • Create new rotation and translation matrices.
  • Calculate the new transformation matrix and apply it to the material ball.
  • Render the world origin by using renderVector() method of the Visualizer class.
Source code (C#)
// AppWorldLogic.cs file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Unigine;

namespace UnigineApp
{
	class AppWorldLogic : WorldLogic
	{
		Node material_ball;

		/* ... */

		public override bool Init()
		{
			// enable the visualizer for world origin rendering
			Console.Run("show_visualizer 1");

			// get the material ball
			material_ball = World.GetNodeByName("material_ball");

			// create rotation and translation matrices
			dmat4 rotation_matrix = new dmat4(MathLib.RotateZ(-90.0f));
			dmat4 translation_matrix = new dmat4(MathLib.Translate(new vec3(0.0f, 3.0f, 0.0f)));

			// create a new transformation matrix for the material ball
			// by multiplying the current matrix by rotation and translation matrices
			dmat4 transform = translation_matrix * rotation_matrix * material_ball.Transform;

			// set the transformation matrix to the material ball
			material_ball.Transform = transform;

			return true;
		}

		public override bool Update()
		{
			// render world origin
			Visualizer.RenderVector(new dvec3(0.0f,0.0f,0.1f), new dvec3(1.0f,0.0f,0.1f), new vec4.RED);
			Visualizer.RenderVector(new dvec3(0.0f,0.0f,0.1f), new dvec3(0.0f,1.0f,0.1f), new vec4.GREEN);
			Visualizer.RenderVector(new dvec3(0.0f,0.0f,0.1f), new dvec3(0.0f,0.0f,1.1f), new vec4.BLUE);

			return true;
		}

		/* ... */
	}
}

To change the order, just change the line of cumulating transformations:

Source code (C#)
dmat4 transform = rotation_matrix * translation_matrix * material_ball.Transform;

The result would be different. The pictures below show the difference (camera is located at the same place).

Order: rotation and translation
Order: translation and rotation

The pictures above show the position of the meshes related to the world origin.

Matrix Hierarchy#

One more important concept is the matrix hierarchy. When a node is added into the world as a child of another node, it has a transformation matrix that is related to the parent node. That is why the Node class distinguishes between the functions getTransform(), setTransform() and getWorldTransform(), setWorldTransform(), which return the local and the world transformation matrices respectively.

Notice
If the added node has no parent, this node uses the World transformation matrix.

What is the reason of using a matrix hierarchy? To move a node relative to another node. And when you move a parent node, child nodes will also be moved.

Parent origin is the same with the world origin
Parent origin has been moved and the child origin has also been moved

Pictures above show the main point of the matrix hierarchy. When the parent origin (node) is moved, the chlld origin will also be moved and the local transformation matrix of the child would not be changed. But the world transformation matrix of the child will be changed. If you need the world transformation matrix of the child related to the world origin, you should use the getWorldTransform(), setWorldTransform() functions; in case, when you need the local transformation matrix of the child related to the parent, you should use the getTransform(), setTransform() functions.

Example#

The following example shows how important the matrix hierarchy is.

In this example, we get the node from and clone it. Then we change transformation matrices of these nodes. We review two cases:

  1. The two nodes are independent.
  2. One node is the child of the other.

In the AppWorldLogic.cs, implement the following code:

Source code (C#)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Unigine;

namespace UnigineApp
{
	class AppWorldLogic : WorldLogic
	{
		Node material_ball_child;
		Node material_ball_parent;

		/* ... */

		public override bool Init()
		{
			// enable the visualizer for world origin rendering
			Console.Run("show_visualizer 1");

			// get the material ball and clone it
			material_ball_child = World.GetNodeByName("material_ball");
			material_ball_parent = material_ball_child.Clone();

			// make the one node the child of another
			material_ball_parent.AddChild(material_ball_child);

			// create rotation and translation matrices for the first material_ball
			dmat4 rotation_matrix = new dmat4(MathLib.RotateZ(-90.0f));
			dmat4 translation_matrix = new dmat4(MathLib.Translate(new vec3(0.0f, 3.0f, 0.0f)));

			// create translation matrix for the second (parent) material ball
			dmat4 translation_matrix_parent = new dmat4(MathLib.Translate(new vec3(0.5f, 0.0f, 1.0f)));

			// create a new transformation matrix for the material ball
			// by multiplying the current matrix by rotation and translation matrices
			dmat4 transform = rotation_matrix * translation_matrix * material_ball_child.Transform;
			dmat4 transform_parent = translation_matrix_parent * material_ball_parent.Transform;

			// set the transformation matrix to the material ball
			material_ball_child.Transform = transform;
			material_ball_parent.Transform = transform_parent;

			return true;
		}

		public override bool Update()
		{
			// render world origin
			Visualizer.RenderVector(new dvec3(0.0f,0.0f,0.1f), new dvec3(1.0f,0.0f,0.1f), new vec4.RED);
			Visualizer.RenderVector(new dvec3(0.0f,0.0f,0.1f), new dvec3(0.0f,1.0f,0.1f), new vec4.GREEN);
			Visualizer.RenderVector(new dvec3(0.0f,0.0f,0.1f), new dvec3(0.0f,0.0f,1.1f), new vec4.BLUE);

			return true;
		}

		/* ... */
	}
}

If you comment the following line:

Source code (C#)
// make the one node the child of another
material_ball_parent.AddChild(material_ball_child);

you get a different result:

Parent-child nodes
Nodes are independent

When nodes are independent, they have different local and world transformation matrices. In case of parent-child nodes, the child's local transformation matrix remains the same after moving, but the world transformation matrix will be changed (you can check it by using the debug profiler).

Last update: 2022-12-14
Build: ()