2

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.

Emir
  • 5,701
  • 1
  • 14
  • 31
SJK
  • 169
  • 1
  • 7

2 Answers2

2

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.

batFINGER
  • 84,216
  • 10
  • 108
  • 233
  • 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. – SJK Nov 20 '20 at 14:55
  • 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. – SJK Nov 20 '20 at 15:06
0

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