0

I am making a scene procedurally with python, and recently switched to the copy() method instead of duplicate_move(). With duplicate_move, all the children of the object reproduced faithfully and their positions relative to the parent were correct. However, with copy(), the children did not get included, so I had to make a series of nested loops going to the depth I needed for my model (four layers) and manually copy, assign parent, and link to scene with each child. This worked, with the exception that the positions of many of the children were wrong, they appeared to be offset vertically by some amount, but their transform data in the inspector panel appears to be the same.

Before we get into what I may have done wrong in my nested loops, is there any way to automatically do a deep copy of the entire hierarchy of an object using the copy() method?

EDIT: Sorry, forgot to include code. Here is the core of what I am currently doing.

actor_copy = actor.copy()
actor_copy.name = str(ID)
scene.objects.link(actor_copy)
if len(actor.particle_systems)>0:
    ps = actor.particle_systems[0].settings.copy()
    actor_copy.particle_systems[0].settings = ps
for child in actor.children:        
    child_copy = child.copy()
    child_copy.parent = actor_copy
    scene.objects.link(child_copy)        
    if len(child.particle_systems)>0:
        ps = child.particle_systems[0].settings.copy()
        child_copy.particle_systems[0].settings = ps
    for subchild in child.children:
        subchild_copy = subchild.copy()
        subchild_copy.parent = child_copy
        scene.objects.link(subchild_copy)
        if len(subchild.particle_systems)>0:
            ps = subchild.particle_systems[0].settings.copy()
            subchild_copy.particle_systems[0].settings = ps
        for sub2child in subchild.children:
            sub2child_copy = sub2child.copy()
            sub2child_copy.parent = subchild_copy
            scene.objects.link(sub2child_copy)
            if len(sub2child.particle_systems)>0:
                ps = sub2child.particle_systems[0].settings.copy()
                sub2child_copy.particle_systems[0].settings = ps
            for sub3child in sub2child.children:
                sub3child_copy = sub3child.copy()
                sub3child_copy.parent = sub2child_copy
                scene.objects.link(sub3child_copy)
                if len(sub3child.particle_systems)>0:
                    ps = sub3child.particle_systems[0].settings.copy()
                    sub3child_copy.particle_systems[0].settings = ps

Apologies for the hack job, time was short and I'm still hoping this can all be thrown away in favor of the right pre-existing function call. But no, I did not know about matrix_parent_inverse, that sounds very promising, thank you!

batFINGER
  • 84,216
  • 10
  • 108
  • 233

2 Answers2

5

Set the matrix_parent_inverse of the copy.

Thought I'd add a supplementary answer, using recursion to get all the children up to a certain level. Without it the code is going to get long and unwieldy for more levels.

import bpy

def copy_ob(ob, parent, collection=bpy.context.scene.collection): # copy ob copy = ob.copy() copy.parent = parent copy.matrix_parent_inverse = ob.matrix_parent_inverse.copy() # copy particle settings for ps in copy.particle_systems: ps.settings = ps.settings.copy() collection.objects.link(copy) return copy

def tree_copy(ob, root_parent, levels=3): def recurse(ob, parent, root_parent, depth): if depth > levels: return copy = copy_ob(ob, parent) if root_parent is not None: copy.parent = root_parent

    for child in ob.children:
        recurse(child, copy, None, depth + 1)

recurse(ob, ob.parent, root_parent, 0)

#test tree_copy(bpy.context.object, None)

batFINGER
  • 84,216
  • 10
  • 108
  • 233
0

The answer is that copy() does not include the matrix_parent_inverse property of children, and this does not get set automatically just because you say .parent = parent object, so you have to do it yourself manually. My above (still really lazy) code now looks like this:

actor_copy = actor.copy()
actor_copy.name = str(ID)
scene.objects.link(actor_copy)    
if len(actor.particle_systems)>0:
    ps = actor.particle_systems[0].settings.copy()
    actor_copy.particle_systems[0].settings = ps

for child in actor.children:        
    child_copy = child.copy()
    child_copy.parent = actor_copy
    child_copy.matrix_parent_inverse = child.matrix_parent_inverse
    scene.objects.link(child_copy)        
    if len(child.particle_systems)>0:
        ps = child.particle_systems[0].settings.copy()
        child_copy.particle_systems[0].settings = ps

    for subchild in child.children:
        subchild_copy = subchild.copy()
        subchild_copy.parent = child_copy
        subchild_copy.matrix_parent_inverse = subchild.matrix_parent_inverse
        scene.objects.link(subchild_copy)
        if len(subchild.particle_systems)>0:
            ps = subchild.particle_systems[0].settings.copy()
            subchild_copy.particle_systems[0].settings = ps

        for sub2child in subchild.children:
            sub2child_copy = sub2child.copy()
            sub2child_copy.parent = subchild_copy
            sub2child_copy.matrix_parent_inverse = sub2child.matrix_parent_inverse
            scene.objects.link(sub2child_copy)
            if len(sub2child.particle_systems)>0:
                ps = sub2child.particle_systems[0].settings.copy()
                sub2child_copy.particle_systems[0].settings = ps

            for sub3child in sub2child.children:
                sub3child_copy = sub3child.copy()
                sub3child_copy.parent = sub2child_copy
                sub3child_copy.matrix_parent_inverse = sub3child.matrix_parent_inverse
                scene.objects.link(sub3child_copy)
                if len(sub3child.particle_systems)>0:
                    ps = sub3child.particle_systems[0].settings.copy()
                    sub3child_copy.particle_systems[0].settings = ps