2
$\begingroup$

I wrote an operator to create an empty object and then make it a parent to the currently selected object. However, when I assign the parent object, the child object moves unless it is at the origin. How can I keep that from happening? Here is some code that demonstrates the problem:


class ObjectTest(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "myops.object_test"
    bl_label = "Array of current object"
    
    def execute(self,context):

        origobj = bpy.context.active_object
        bpy.context.scene.cursor.location = origobj.location

        bpy.ops.object.empty_add()
        objparent=bpy.context.active_object
        objparent.name=origobj.name+"Array"
        origobj.parent=objparent
        return {'FINISHED'}
    
def register():
    bpy.utils.register_class(ObjectTest)


def unregister():
    bpy.utils.unregister_class(ObjectTest)


if __name__ == "__main__":
    register()

I would like the empty parent object and the selected child object to be at the same location, not necessarily the origin. I am trying to make a group of objects that can be moved together, and a parent seems like the best way. I was using Blender 2.83, but I recently updated to 2.9. Thanks for your help.

$\endgroup$
1
2
$\begingroup$

Set matrix_parent_inverse

See Does a child object inherit the matrix from the parent?

Key here is that the origin of local space is calculated from the basis matrix (what you see in the transform UI) and its parent inverse matrix.

There are a number of ways to achieve desired result:

  • set the empties matrix world to that of the original
  • and set both the matrix_basis and matrix_parent_inverse of original to identity. Equivalent to the one case that "didn't move"

By default blender sets the matrix parent inverse, so when parenting is removed the object maintains its prior transform. Using first method will snap them back to origin.

  • Set parent matrix world to childs.
  • Maintain the current location by setting childs matrix_parent_inverse to its inverted basis matrix.

Test script, method 2.

import bpy

class ObjectTest(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.test"
    bl_label = "Array of current object"
    
    def execute(self, context):

        ob = context.active_object

        bpy.ops.object.empty_add()
        mt = context.active_object
        mt.name = f"{ob.name}Array"
        mt.matrix_world = ob.matrix_world
        ob.parent = mt
        ob.matrix_parent_inverse = ob.matrix_basis.inverted()
        return {'FINISHED'}
    
def register():
    bpy.utils.register_class(ObjectTest)


def unregister():
    bpy.utils.unregister_class(ObjectTest)


if __name__ == "__main__":
    register()

Consider extending this such to selected objects such that can add an empty parent to each, or one parent at active object and all other selected also become children.

$\endgroup$
2
  • $\begingroup$ Thanks so much for your help. I have not yet had a chance to try it out, but it seems like it will solve my problem. I have not heard of the basis matrix before, so also, thanks for the references to more info about it. $\endgroup$
    – SJK
    Nov 20 '20 at 14:55
  • $\begingroup$ My code adds additional objects to the parent as they are created to make a group of objects that can be moved around together. Your answer explains something that I did not understand, so I should be able to get the code to work now. Thanks again. $\endgroup$
    – SJK
    Nov 20 '20 at 15:06
0
$\begingroup$

you need relative transformation matrix which I have explained here: How can I manually calculate bpy.types.PoseBone.matrix using Blender's Python API?

$\endgroup$
1
  • $\begingroup$ Thanks. Your explanation of the coordinate systems is helpful. $\endgroup$
    – SJK
    Nov 21 '20 at 2:38

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.