14
$\begingroup$

How do I convert from object-space to world-space?

I suspect it is:

vert_os = obj.vertices[foo]
vert_ws = vert_os.getPositionFromMatrix(obj.matrixWorld)

but does the matrix also handle translations, or do I need to handle that separately?

Can someone link to the relevant documentation?

$\endgroup$
29
$\begingroup$

Multiply the world matrix by the object-space vector for world space coordinate:

import bpy

ob = bpy.data.objects['Cube']

v = ob.data.vertices[0].co
mat = ob.matrix_world

# Multiply matrix by vertex
loc = mat * v

# Don't do the reverse!
loc = v * mat  # wrong!

`Object.matrix_world is a 4x4 transformation matrix. It contains translation, rotation and scale. A 4x4 matrix can also be used for mirroring and shearing (not covered in my answer).

[a] [b] [c] [d]
[e] [f] [g] [h]
[i] [j] [k] [l]
[m] [n] [o] [p]

The translation is stored in the first 3 rows of the 4th column of the matrix (d, h, l):

mat.col[3][:3]

You can also use:

# Create new Vector object
mat.to_translation()

# Access the original matrix' translation
# (assignments will change the matrix, thus the object location!)
mat.translation

Rotation and scale are sort of combined. The rotation is stored in a, b, c, e, f, g, i, j and k. The scale is stored in a, f and k. The values in a, f and k of the rotation are taken and multiplied by the scale factor to store both pieces of information.

To get only the rotation, you need to normalize the 3x3 matrix:

mat.to_3x3().normalized()

To get only the scale, you can use the utility method:

mat.to_scale()

Or manually, normalize the matrix and divide each of the un-normalized by the normalized components (a, f, k):

# you could do this manually like
# vec / sqrt(vec.x**2 + vec.y**2 + vec.z**2) for every matrix column
nmat = mat.normalized()

scale = Vector((mat[0][0] / nmat[0][0], mat[1][1] / nmat[1][1], mat[2][2] / nmat[2][2]))

If you need the world coordinates of all vertices, it's more efficient to use the transform() method:

me.transform(mat)

It will apply the transformation matrix to the mesh, so multiply the world matrix with all vertex coordinates. You may wonder about the change in orientation of a mesh object in viewport if you do the above. It can be fixed by resetting the matrix_world (otherwise the transformation will be done twice):

ob.matrix_world = Matrix()  # identity matrix
$\endgroup$
  • $\begingroup$ Maybe you mean v = ob.data.vertices[0].co instead of v = ob.data.vertices[0]. $\endgroup$ – isar Jul 5 '15 at 19:25
14
$\begingroup$

This is fairly simple and applies to any data (curves, armatures, lattice ...)

v_co_world = obj.matrix_world * obj.data.vertices[0].co

Its also handy to be able to do the reverse, get a point in worldspace relative to the vertex.

# get the cursor in object space
# (so you can compare it to the vertices locations
#  without first having to transform them into worldspace).
v_co_object = obj.matrix_world.inverted() * scene.cursor_location
$\endgroup$
  • $\begingroup$ For me, the camera's matrix_world was an identity matrix, so to get the coordinates of a point relative to the camera, I needed camera.matrix_basis.inverted() * mathutils.Vector([0,0,0]) $\endgroup$ – colllin Apr 12 at 22:06
4
$\begingroup$

If you're brave enough to use numpy you can get all the vertex coords as a numpy array about a thousand times as fast as any other python method:

import numpy as np


    def get_co(ob, arr=None):
    """Returns vertex coords as N x 3"""
    c = len(ob.data.vertices)
    if arr is None:    
        arr = np.zeros(c * 3, dtype=np.float32)
    ob.data.vertices.foreach_get('co', arr.ravel())
    arr.shape = (c, 3)
    return arr


def get_proxy_co(ob, arr):
    """Returns vertex coords with modifier effects as N x 3"""
    me = ob.to_mesh(bpy.context.scene, True, 'PREVIEW')
    c = len(me.vertices)
    me.vertices.foreach_get('co', arr.ravel())
    bpy.data.meshes.remove(me)
    arr.shape = (c, 3)
    return arr


def apply_transforms(ob, co):
    """Get vert coords in world space"""
    m = np.array(ob.matrix_world)    
    mat = m[:3, :3].T # rotates backwards without T
    loc = m[:3, 3]
    return co @ mat + loc


def revert_transforms(ob, co):
    """Set world coords on object. 
    Run before setting coords to deal with object transforms
    if using apply_transforms()"""
    m = np.linalg.inv(ob.matrix_world)    
    mat = m[:3, :3].T # rotates backwards without T
    loc = m[:3, 3]
    return co @ mat + loc
$\endgroup$
  • $\begingroup$ A thousand times faster? $\endgroup$ – batFINGER Sep 20 '17 at 5:52
  • 1
    $\begingroup$ I just did a speed check. against loop and list comprehension. on an object with 60,000 verts, numpy was only 20 times faster. Sorry about the exaggeration. $\endgroup$ – Rich Colburn Sep 20 '17 at 17:03
  • $\begingroup$ 20 is still good. Care to add test result to answer? $\endgroup$ – batFINGER Sep 20 '17 at 17:06
  • $\begingroup$ I'm not good at posting here. Is there a way I can send you my code? $\endgroup$ – Rich Colburn Sep 20 '17 at 17:09
  • 1
    $\begingroup$ I posted the code on git link $\endgroup$ – Rich Colburn Sep 21 '17 at 17:48
4
$\begingroup$

In blender 2.8 use @ operator for matrix multiplication

for example

transformed_vertex =obj.matrix_world @ obj.data.vertices[0].co
$\endgroup$
  • $\begingroup$ I'm using Blender 2.8--this worked for me without importing NumPy. Using * failed. $\endgroup$ – Joseph Brandenburg May 23 at 22:27
  • 1
    $\begingroup$ @ is a operator in python. For more info PEP465 $\endgroup$ – Binoy Pilakkat May 30 at 8:26
2
$\begingroup$

If you have your coords in a flat numpy.array from foreach_get("co", coords), the following dot product of the world matrix with the local coordinates will get their their global version:

import numpy as np

def to_global(local_coords, world_matrix):

    # Reshape coords to Nx3 matrix
    local_coords.shape = (-1, 3)

    # Add an extra 1.0s column (for matrix dot product)
    local_coords = np.c_[local_coords, np.ones(local_coords.shape[0])]

    # Then:
    # Dot product matrix with the coords transpose
    # Keep the first 3 rows (x,y,z)
    # Transpose result to Nx3
    # Flatten
    global_coords = np.dot(world_matrix, local_coords.T)[0:3].T.reshape((-1))

    return global_coords

To use:

# Get the local coords
local_coords = np.zeros(len(my_obj.data.vertices) * 3) 
my_obj.data.vertices.foreach_get('co', local_coords)

# Convert to global
global_coords = to_global(local_coords, my_obj.matrix_world)

On my machine and a mesh with 491,526 verts, the function takes 8.1 ms, or about 0.16ms per 10k verts:

>>> timeit.timeit(lambda: to_global(local_coords, my_obj.matrix_world), number=1000) / 1000.0
0.008153792893048377
$\endgroup$
1
$\begingroup$
import bpy

obj = bpy.data.objects['Cube']

wm = obj.matrix_world
print( wm )
for v in obj.data.vertices:
   world = wm * v.co
   print(world)

The world matrix contains the transformations for location,rotation and scale as in the Properties Panel:

enter image description here

The relevant documentation on Math Types & Utilities mathutils

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.