Changes to Matrices for Blender 2.62

Hi All,

I thought I would post this thread to update everyone on some changes to matrices for Blender 2.62.

Introduction
It has often been a point of some confusion among new Blender users that matrices do not behave as is “normal” in mathematics. The common response to this is that Blender uses column major storage for matrices. While this did cause some of the issues, the situation was further complicated by the fact that printing matrices shows the matrix columns as rows.

While the issue of column major storage is important in C where the layout of values in memory is pertinent, in Python, layout in memory is not nearly as important. As such, so as to comply with the standard mathematical of indexing the row first, some changes have been made in SVN r42816. The changes are been detailed below.

Uneffected
Most uses of matrices are uneffected by the changes. This includes;

  • Matrix Classmethods such as Matrix.Rotation(), Matrix.Translation() etc.
  • Matrix multiplication with vectors and other matrices.
  • Matrix methods such as matrix.identity(), matrix.transpose(), matrix.to_translation() etc

In most scripts this will mean the majority, if not all, uses of matrices will be unchanged.

Changes
Element Indexing

The biggest change to matrices is that now rows are indexed before the column. As such, consider the following matrix:


matrix = [1  2  3]
         [4  5  6]
         [7  8  9]

So to access 4, we take the second row, first column:

matrix[1][0] == 4

Previously, the column index came first.

Matrix Initialisation
This is also important for manually initialising the matrix yourself, previously a list of all columns was supplied. Now a list of rows is supplied. For the matrix above, to create this matrix in Blender you would now do the following:


matrix = Matrix(((1, 2, 3), (4, 5, 6), (7, 8, 9)))

Previously you would create it as:

matrix = Matrix(((1, 4, 7), (2, 5, 8), (3, 6, 9)))

Matrix Column Access
Since the order has been changed,

matrix[0]

now returns the first row instead of the first column as it did previously.

However, if you require access to the matrix columns you can do so as follows

matrix.col[0]

Matrix Translation Component
If you need to get or set the translation component of a 4 x 4 matrix, then you can now use,

matrix.translation

Previously the translation component was accessed by using:

matrix[3][0:3]

Matrix Length
The length of a matrix (as calculated by len()) now returns the number of rows in the matrix, previously this was the number of columns.

Effect on Scripts
These changes will effect the function of some scripts which access matrix elements or initialise matrices manually. However, in updating many of the scripts included with Blender, many scripts were found not to need any changes at all. See the link below for a cheat sheet for script writers and list of gotchas which apply.

Additional Information
For additional information including a cheat sheet, see here http://wiki.blender.org/index.php/User:TrumanBlending/Matrix_Indexing

Cheers,
Truman

Truman, thanks a lot!
It was indeed ‘confusing’ to think differently to what one was accustomed.

Just discovered in Blender 2.61 (compiled yesterday W32 mingw cmake)

A while it was not allowed to use vector * matrix; but it is now NICE different:

Assume M as 3x3 matrix and V a 3D vector (row? coulumn?)!!!

in M * V ; V is used as a column vector/matrix
in V * M ; V is used as a row vector/matrix (and again allowed)

But in general the two resulting vectors will be DIFFERENT!

(M*V)transposed ==> V(tra) * M(tr) is now in Blender theoretically!! V * M.transpose()
Helaas M.transpose() delivers nothing so you have (at least in my Bl version)
work like this exampe:

>>> m
Matrix(((1.0, 1.0, 0.0),
(0.0, 3.0, 0.0),
(0.0, 0.0, 4.0)))

>>> v
Vector((1.0, 1.0, 1.0))

>>> m*v
Vector((2.0, 3.0, 4.0))

>>> v*m
Vector((1.0, 4.0, 4.0))

>>> m.transpose() #before using M as transpose

>>> m*v
Vector((1.0, 4.0, 4.0))

>>> v*m
Vector((2.0, 3.0, 4.0))

this change also impact any blender >2.5 game I guess ? (omg)
what about game matrices and bricks ?
also, is there any consequences on quaternion/euler methods ?

Yes this will potentially impact on games, however, since no classmethods or methods are changed, only direct element manipulation is changed.

This should not have any impact on euler/quaternion/matrix classmethods/matrix methods.

Cheers,
Truman

thanks for having this update on mathutil
very usefull to know abou it

i would suggest to make this a sticky at top of page if possible at least it would be easier to find this info !

thanks

Hi All,

All trunk addons (except for those we’re waiting to hear from the authors, of which there are only 2) have been updated for the matrix changes. If bugs are found please report here and we can determine if this is related to the matrix changes or not.

Cheers,
Truman

PS See here for the list of scripts and their status http://wiki.blender.org/index.php/User:TrumanBlending/Matrix_Indexing#Scripts_to_be_Changed_.2F_Checked

any chance we will have matrix with higher dim then 3 or 4 ?

happy new year

Hi Ricky,

Not at the moment. The aim is not to make mathutils a replacement for NumPy and SciPy, for large matrices and higher matrix functions, NumPy and SciPy are currently still the best choice. Arbitrary size matrices would be quite useful, so it may be added in the future.

Cheers,
Truman

i got numpy but not certain about scipy
i think this one has to be built locally on the your PC!

i think it would be nice that mathutil had these higher dim matrix to keep coding in blender only with no external module
like in the case you need to solve NXN equations real numbers or complex numbers if possible!
it would be a nice addition

mind you i found an algo to do that which is short and python only no external modules
but i heard that numpy scipy are a lot faster then mathutil !
not certain if this is the case but if it is why not replace mathutil functions by those of numpy or scipy !

at least it might make blender faster and numpy is free soft also
so should not be a problem !

happy 2.6

No, I have Scipy for Python 3.2 (sourceforge, today!) and it works if installed to Python 3.2 and then the scipy directory copied to
2.61/scripts/addons :wink:
numpy analog!

W32 versions!

So all math is available for us :evilgrin:

i did see the numpy part and got it
and don’t think this part included the scipy part !
but may be i’m wrong

where can i download this numpy +scypi
and is it only one dowload ow 2 download ?

thanks

You should copy it to ~/.blender/2.61/scripts/modules not the addons directory… You’ll need to create the directory if it’s not already there…

Does this fix the Matrix associativity bug that is present in 2.57? I have some scripts that have works-arounds for this.

http://projects.blender.org/tracker/index.php?func=detail&aid=27813&group_id=9&atid=498

and

http://projects.blender.org/tracker/?func=detail&atid=498&aid=28161&group_id=9

Hi kastoria,

In short, yes the problem has been solved. However, I believe this was fixed prior to the 2.61 release and not by these changes. I tested this using the 2.61 release, if you could also confirm that this is now correct that would be fantastic.

Cheers,
Truman

Ok, I just verified that this is fixed in 2.61. I guess it is time to “fix” my scripts and upgrade.

@Truman, if you or anyone has time, can you take a look at this def, from the 3Delight exporter and tell if it will be affected by these changes? The 3Delight exporter no longer works correctly in 2.6.1. It works fine in 2.6 and I think these matrix changes might be the culprit.


def rib(v):
    
    # float, int
    if type(v) in (float, int, mathutils.Vector, mathutils.Color):
        vlen = 1
        
        if hasattr(v, '__len__'):
            vlen = len(v)
        if vlen > 1:
            return '[ ' + ' '.join([str(i) for i in v]) + ' ]'

        else:
            return str(v)
    
    # string
    elif type(v) == str:
        return '"%s"' % v
        
    # list, tuple
    elif type(v) in (list, tuple):
        return "[ " + " ".join(str(i) for i in v) + " ]"
    
    # matrix
    elif type(v) == mathutils.Matrix:
        return '[ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]' % \
            (v[0][0], v[0][1], v[0][2], v[0][3], \
            v[1][0], v[1][1], v[1][2], v[1][3], \
            v[2][0], v[2][1], v[2][2], v[2][3], \
            v[3][0], v[3][1], v[3][2], v[3][3])

I guess it is as simple as flipping around the rows and columns.


def rib(v):
    # float, int
    if type(v) in (float, int, mathutils.Vector, mathutils.Color):
        vlen = 1
        
        if hasattr(v, '__len__'):
            vlen = len(v)
        if vlen > 1:
            return '[ ' + ' '.join([str(i) for i in v]) + ' ]'

        else:
            return str(v)

    # string
    elif type(v) == str:
        return '"%s"' % v
        
    # list, tuple
    elif type(v) in (list, tuple):
        return "[ " + " ".join(str(i) for i in v) + " ]"

    # matrix
    elif type(v) == mathutils.Matrix:
        #Row by column.
        return '[ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]' % \
            (v[0][0], v[1][0], v[2][0], v[3][0], \
            v[0][1], v[1][1], v[2][1], v[3][1], \
            v[0][2], v[1][2], v[2][2], v[3][2], \
            v[0][3], v[1][3], v[2][3], v[3][3])
        '''
        #Column by row.
        return '[ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]' % \
            (v[0][0], v[0][1], v[0][2], v[0][3], \
            v[1][0], v[1][1], v[1][2], v[1][3], \
            v[2][0], v[2][1], v[2][2], v[2][3], \
            v[3][0], v[3][1], v[3][2], v[3][3])
        '''

Hi Atom,

That’s exactly the fix and should be the only change required by the new matrix code. Great stuff!

Cheers,
Truman