3
$\begingroup$

How do I set an object parent without moving the child location as relative

Lets say I have an object relationship

- first
  - second
    - third

And I have absolute position/location/coordinates

first: 1.0, 0.0, 0.0
second: 0.0, 1.0, 0.0
third: 0.0, 0.0, 1.0

I read that we're supposed to use magic setter named matrix_parent_invert, like so child.matrix_parent_inverse = parent.matrix_world.inverted()

so as follows is a quick script to do that, and my object positions remain relative with various order of operations of the following"

import bpy

first = bpy.data.objects.new('first', None)
first.location = (1., 0., 0.)
bpy.context.collection.objects.link(first)


snd = bpy.data.objects.new('second', None)
snd.location = (0., 1., 0.)
snd.parent = first
snd.matrix_parent_inverse = first.matrix_world.inverted()
bpy.context.collection.objects.link(snd)


trd = bpy.data.objects.new('third', None)
trd.location = (0., 0., 1.)
trd.parent = snd
trd.matrix_parent_inverse = snd.matrix_world.inverted()
bpy.context.collection.objects.link(trd)
expectation enter image description here
reality enter image description here
$\endgroup$

2 Answers 2

3
$\begingroup$

Since updating the View Layer can be too slow, here's how you would do it in a more performant way:

import bpy
from mathutils import Euler, Matrix, Vector

first = bpy.data.objects.new('first', None)
location = (1, 0, 0)
rotation = Euler((0, 0,0))
scale = Vector((1, 1, 1))  # Vector needed because of to_4d()
T = Matrix.Translation(location)
R = rotation.to_matrix().to_4x4() 
S = Matrix.Diagonal(scale.to_4d()) 
M = T @ R @ S
first.matrix_world = M  # just using T would work but I wanted to show how to deal with rotations and scale
bpy.context.collection.objects.link(first)

snd = bpy.data.objects.new('second', None)
snd.parent = first
snd.matrix_world = Matrix.Translation((0, 1, 0))
bpy.context.collection.objects.link(snd)

trd = bpy.data.objects.new('third', None)
trd.parent = snd
trd.matrix_world = Matrix.Translation((0, 0, 1))
bpy.context.collection.objects.link(trd)
$\endgroup$
1
  • 1
    $\begingroup$ oh wow, you were not kidding about updating the view layer being slow. 3s -> 183s for a few thousand parents. oof $\endgroup$ Commented Jun 4, 2022 at 5:20
3
$\begingroup$

When you parent a object from interface, matrix_local and matrix_parent_inverse of the child object will change.

But setting the parent from a script without using bpy.ops, it doesn't change matrix_local and matrix_parent_inverse. So, you need to change them manually.

import bpy

first = bpy.data.objects.new('first', None)
first.location = (1., 0., 0.)
bpy.context.collection.objects.link(first)


snd = bpy.data.objects.new('second', None)
snd.location = (0., 1., 0.)
snd.parent = first
bpy.context.view_layer.update() # <--- important
snd.matrix_parent_inverse = first.matrix_world.inverted()
bpy.context.collection.objects.link(snd)


trd = bpy.data.objects.new('third', None)
trd.location = (0., 0., 1.)
trd.parent = snd
bpy.context.view_layer.update() # <--- important
trd.matrix_parent_inverse = snd.matrix_world.inverted()
bpy.context.collection.objects.link(trd)
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .