This page will guide you through some of the most important uses of matrices in 3D graphics.
A matrix is basically an array of number arranged in a rectangular pattern. The matrices used in Cinema 4D, represented by the class
Matrix
, consist of three rows and four columns, i.e. they have the dimension 3x4. The first row is always “1, 0, 0, 0”, which means that there are 12 numbers that are used. These numbers are grouped into four vectors, one for the remaining numbers in each column. The four vectors are called
off
, v1, v2 and v3:
Together these four vectors can be used to represent the coordinate system for a Cinema 4D object. A coordinate system consists of three axes, one for each coordinate (X, Y and Z). The system also has a base position, from which the three axes originate. This base position is stored in off , and the three axis vectors are stored in v1, v2 and v3.
off position v1 X-axis v2 Y-axis v3 Z-axis
A matrix is enough to store all the three important properties of an object: position, rotation and scale. (The position is stored in the first vector, the rotation is given by the direction of the three axis vectors and the scale is given by their length.) Therefore each object has a matrix that you can get with
BaseObject.GetMl()
. This surely seems like a convenient way to group position, rotation and scale. But
BaseObject.SetRelRot()
and
BaseObject.SetRelScale()
work just fine already. Why do we need to use matrices?
To compare two objects in different hierarchies, what we need is a way to get their global coordinates. This is where matrix multiplication comes in. Multiplying two matrices is not just a matter of multiplying the corresponding numbers; it is much more complicated than that. In fact, matrix multiplication is not even commutative: a*b is not the same as b*a . Fortunately you do not have to know how to multiply two matrices to do it.
If matrix Mp is the parent’s matrix, and Mc is the child’s matrix, then the product Mp*Mc gives a matrix that represents the child’s position and rotation, but in the coordinate system of the parent. This means that getting a child’s global matrix is just a matter of multiplying it with each of its parents’ matrices.
Fortunately you do not have to do this multiplication yourself. Each object has these predefined functions:
GetMl() # Returns the matrix in local coordinates GetMg() # Returns the matrix in global coordinates GetUpMg() # Returns the parent's matrix in global coordinates
For example, if you have an object named ‘MyObject’ that’s hidden deep within the object hierarchy, then the following code will print its global position:
obj = doc.SearchObject("MyObject") mat = obj.GetMg() # the global matrix globalPos = mat.off #get the position from the matrix print "Global pos= ", globalPos
Since matrix contains the global matrix, the position stored in off will be the global position. Easy!
To calculate the distance between two objects in different hierarchies, just use their global positions:
p1 = op1.GetMg().off p2 = op2.GetMg().off dist = (p2-p1).GetLength()
If you remember to always do your calculations in global world coordinates, then your users will never get any unforseen problems with hierarchies.
What if you have a position in global coordinates and want to move an object to that position? Then you need a transformation back from global coordinates to the object’s local position coordinates. Mathematically this is done by multiplication by the global matrix’ inverse. If Mg is a matrix in global space and Mag is object a’s global matrix, then Mag-1*Mg equals the first matrix’ in a’s local coordinates.
Fortunately you do not have to do this manually either, since these functions do it for you:
SetMl(ml) # Sets the object's local matrix SetMg(mg) # Sets the object's global matrix
If you use
BaseObject.SetMg()
, Cinema 4D will automatically transform the matrix to the local coordinate system before setting it.
For example if you wanted to write a hierarchy safe midpoint plugin you could do like this:
p1 = op1.GetMg().off # The global position of the first object p2 = op2.GetMg().off # The global position of the second object mid = (p1 + p2) / 2.0 # The midpoint (in global coordinates still) m = obj.GetMg() # The global matrix of the object to place in between m.off = mid # Set its position to the midpoint (global coordinates) obj.SetMg(m) # Sets the global matrix of op to m, placing it between op1 and op2
OK, so the position was easy to get with Matrix.off . But what about the rotation and the scale. How do we get and set the global rotation and scale from a global matrix? The answer is that this is slightly more complicated, but that one can easily write functions that make it easy.
Getting the scale is just a matter of measuring the length of each of the axis vectors:
m = obj.GetMg() scale = c4d.Vector( m.v1.GetNormalized(), m.v2.GetNormalized(), m.v3.GetNormalized())
Setting the scale is done by normalizing each vector and the multiplying it with the wanted scale:
m = obj.GetMg() m.v1 = m.v1 * scale.x m.v2 = m.v2 * scale.y m.v3 = m.v3 * scale.z obj.SetMg(m)
To get the rotation from a matrix there is the member function
MatrixToHPB()
:
m = obj.GetMg() rot = utils.MatrixToHPB(m)
Setting the rotation is done with the member function
HPBToMatrix()
.
Note
Please remember, Cinema 4D handles rotation in radians. The rotation must be in radians.
Unfortunately this function returns the position and scale, so those need to be stored away:
m = obj.GetMg() pos = m.off scale = c4d.Vector( m.v1.GetLength(), m.v2.GetLength(), m.v3.GetLength()) m = utils.HPBToMatrix(rot) m.off = pos m.v1 = m.v1.GetNormalized() * scale.x m.v2 = m.v2.GetNormalized() * scale.y m.v3 = m.v3.GetNormalized() * scale.z obj.SetMg(m)
Collecting everything we have learned so far we can write these six convenience functions for setting and getting global position, rotation and scale data:
GetGlobalPosition(obj) # Returns the global position of obj GetGlobalRotation(obj) # Returns the global rotation of obj GetGlobalScale(obj) # Returns the global scale of obj SetGlobalPosition(obj, pos) # Sets the global position of obj to pos SetGlobalRotation(obj, rot) # Sets the global rotation of obj to rot SetGlobalScale(obj, scale) # Sets the global scale of obj to scale def GetGlobalPosition(obj): return obj.GetMg().off def GetGlobalRotation(obj): return utils.MatrixToHPB(obj.GetMg()) def GetGlobalScale(obj): m = obj.GetMg() return c4d.Vector( m.v1.GetLength(), m.v2.GetLength(), m.v3.GetLength()) def SetGlobalPosition(obj, pos): m = obj.GetMg() m.off = pos obj.SetMg(m) def SetGlobalRotation(obj, rot): """ Please remember, Cinema 4D handles rotation in radians. Example for H=10, P=20, B=30: import c4d from c4d import utils #... hpb = c4d.Vector(utils.Rad(10), utils.Rad(20), utils.Rad(30)) SetGlobalRotation(obj, hpb) #object's rotation is 10, 20, 30 """ m = obj.GetMg() pos = m.off scale = c4d.Vector( m.v1.GetLength(), m.v2.GetLength(), m.v3.GetLength()) m = utils.HPBToMatrix(rot) m.off = pos m.v1 = m.v1.GetNormalized() * scale.x m.v2 = m.v2.GetNormalized() * scale.y m.v3 = m.v3.GetNormalized() * scale.z obj.SetMg(m) def SetGlobalScale(obj, scale): m = obj.GetMg() m.v1 = m.v1.GetNormalized() * scale.x m.v2 = m.v2.GetNormalized() * scale.y m.v3 = m.v3.GetNormalized() * scale.z obj.SetMg(m)