Adding an object where an other object is already located

Hi all,
I have an object O which is a child. I want to put an empty in O. I don’t wont the empty to be the child of anything thus I need the global coordinates of O.

First unsuccessful try:
Moving the 3D cursor to the object and read the coordinates of the 3D cursor. But there is a poll exception when I try to move the 3d cursor with the snap_cursor_to_active() because of an unappropriate context.

Second unsuccessful try:
Using the matrices. But the code below with Blender 2.63 shows that the matrices are unreliable. The 2 empties a and b are at the same global position but the matrices are completly different depending on whether the empty is defined using a location or a translation. Moreover the identity
matrix_local = matrix_parent_inverse x matrix_basis
is not true, in contradiction with the claim I found here http://wiki.blender.org/index.php/User:Pepribal/Ref/Appendices/ParentInverse

So my question is : what is the reliable way to get the global coordinate of an object.


bpy.ops.object.add(type='EMPTY')
a=bpy.context.object
a.location=Vector((1,2,3))
bpy.context.scene.update
print ("Matrice A")
print ("matrix_basis")
print(a.matrix_basis)
print ("matrix_local")
print(a.matrix_local)
print ("matrix_parent_inverse")
print(a.matrix_parent_inverse)
print ("matrix_world")
print(a.matrix_world)



print ("Matrice B")
bpy.ops.object.add(type='EMPTY')
b=bpy.context.object
bpy.ops.transform.translate(value=Vector((1,2,3)))
bpy.context.scene.update
print ("matrix_basis")
print(b.matrix_basis)
print ("matrix_local")
print(b.matrix_local)
print ("matrix_parent_inverse")
print(b.matrix_parent_inverse)
print ("matrix_world")
print(b.matrix_world)

dunno how you would compute a global coord from parent / local / basis matrices, but this seems to work fine:

ob = bpy.context.object
loc = ob.matrix_world.to_translation()

empty = bpy.data.objects.new("Empty", None)
bpy.context.scene.objects.link(empty)
bpy.context.scene.update()

Thanks for the answer. Unfortunatly, your code works only in one of the 2 cases above : in case b, this is OK, in case a, matrix_world is identity and the code returns an empty on the origin.

what do you do with loc ?

@sc3sc3: good point, forgot to translate the empty to that location OMG
there needs to be a

empty.location = loc

@laurent49:

the code below with Blender 2.63 shows that the matrices are unreliable

That’s not true, it IS reliable. It’s not Blender’s fault that both empties end up at different locations, it is actually like it should be.

The transform.translate() operator performs a relative translation, while location = (1,2,3) sets the location to an absolute value.

Now what happens is this:
1.) You create an empty, it gets created at cursor location!
2.) You set the location to an absolute location
3.) Everything is fine - empty at (1,2,3)

4.) You create another empty, it gets created at cursor location!
5.) You call the translate-operator and move the empty by (1,2,3) units
6.) The empty ends up at bpy.context.scene.cursor_location + (1,2,3)

If you wanna use the transform operator to set a global coordinate, either create the empty at (0,0,0) or move it to the origin before calling the op.

As a side-note, what are the equivalent operator/method/functions (I’m new to Python, so I’m not sure which term is correct) for rotation and scale?

all functions in bpy.ops.* are operators, they represent user actions (and therefore rely on a certain context, like there has to be a selected object of type mesh for instance if you wanna use the bpy.ops.mesh.* functions)

if you use the bmesh module, I’d call the functions of an bmesh object “methods”, as this is commonly used for functions in object oriented programming

All standard API functions, like bpy.data.objects.new(…) can be called functions (sometimes referred to as “low-level function calls” in Blender python development)

location, scale and rotation are neither operators, methods nor functions, they are (RNA) properties.

getting and setting example:

import bpy
from mathutils import Vector

# take active object
ob = bpy.context.object

# get object's location
print(ob.location)

# set new, absolute location
ob.location = (1,2,3) # fine here, but nicer to nicer to use Vector types
print(ob.location)

# set relative location
ob.location += Vector((1,2,3)) # Vector type is required here for += operator (mathematical operator, not bpy.ops.*!)

# Scale
print(ob.scale)

# Rotation mode
print("Mode:", ob.rotation_mode)

# Default should be quaternion.
# You should usually check for the mode first and then access the appropriate rotation property
#  - or use matrix_world.to_3x3() / matrix_world.to_4x4() / matrix_world.to_quaternion() instead
print(ob.rotation_quaternion)

The problem was not with the translate function which works as expected for a translation, but with matrix_world which gave me 2 different results for the same object in the affine space. Helped by your remark, I finally understood that matrix_world is not a function of the object, but depends on the ojbect AND the 3D cursor. Thus provided that the 3D cursor is at the expected position, matrix_world is reliable. (This is maybe a stupid rephrasing of what you said).

Once understood, I could write a slight modification of your code which gives the correct answer for the global coordinate of my object in both case a and b. The code follows.

Thank you for your help.


empty = bpy.data.objects.new("Empty", None)
empty.location=Vector((0,0,0))
# The aim of the above two lines is to put the 3D cursor in the origin
# empty can be deleted afterwards if not useful 
# There is maybe a simpler way to move the 3d cursor on the origin
bpy.context.scene.update()
# the above line to be sure that matrix_word has been updated before using it 
globalCoordinate =myObject.matrix_world.to_translation()

Thanks once again, CoDEman. For whatever reason, I was thinking Python used different terminology from other OOP languages.

If you’re recommending calling them bmesh methods, is that to say that the bmesh module contains one or more classes?

matrix_world is not a function of the object, but depends on the ojbect AND the 3D cursor

matrix_world is a property, not function, that’s right. But it doesn’t depend on 3d cursor in any way. It’s bpy.ops.object.add() which adds a new object at 3d cursor by default. If you use bpy.data.objects.new() instead, or specify location=(0,0,0) for the object.add() operator, then the object will be created at 0,0,0 and a relative transformation via transform.translate() will move the object to the expected location and matrices will be equal.

For bmesh module, see:
http://www.blender.org/documentation/blender_python_api_2_65_10/bmesh.html

Arghh. You’re right. In the very first example of code posted above, I wrote update (without parenthesis) instead of update(). The updating was not done and the results of matrix_world were silly. Sorry for all this mess starting from this typo.

With this typo corrected, the code to get the global coordinate of an object is easy and natural:


bpy.context.scene.update()
globalCoordinates = myObject.matrix_world.to_translation()