Hi there !
A few years ago I used to be using a little script found here in order to reset every “Stretch to” constraints of an armature, even on unselected/offlayered bones :
import bpy
for b in bpy.context.active_object.pose.bones:
for c in b.constraints:
if c.type == "STRETCH_TO":
c.rest_length = 0
It has been very useful, especially when you drastically edit the bones positions of an already finished rig (i.e. when you reuse the same rig for similar but different models).
I would like a very similar script, but with “set inverse” for CHILD_OF constraint.
But I barely know Blender’s API and every try haven’t got anything working nor going the right direction.
My last attempt looked like that:
import bpy
for b in bpy.context.active_object.pose.bones:
for c in b.constraints:
if c.type == "CHILD_OF":
context_py = bpy.context.copy()
context_py["constraint"] = constraint
bpy.ops.constraint.childof_set_inverse(context_py, constraint="Child Of")
And got this error:
Traceback (most recent call last):
File "\Text", line 7, in <module>
NameError: name 'constraint' is not defined
import bpy
for b in bpy.context.active_object.pose.bones:
for c in b.constraints:
if c.type == "CHILD_OF" and c.target is not None:
c.inverse_matrix = c.target.matrix_world.inverted()
No errors returned, it executes propperly, but simply doesn’t affects the rig.
import bpy
for b in bpy.context.active_object.pose.bones:
for c in b.constraints:
if c.type == "CHILD_OF":
context_py = bpy.context.copy()
context_py["constraint"] = c
bpy.ops.constraint.childof_set_inverse(context_py, constraint="Child Of", owner='BONE')
And it works, but only on the active bone. How can I make it work on the whole armature ? Even on offlayered bones ?
The reason your first error (post#1) occurs is because you set context_py[‘constraint’] to “constraint” instead of “c”…because you never defined “constraint”.
The second version works when you have use_location and use_rotation enabled.
I don’t know what you’re targeting which is what the “c.target” is looking at.
I use empty objects that I created during the op.
If you are targeting “bones”, then you have to use the subtarget.
target = c.target
subtarget = c.subtarget
if target and target.pose is not None and subtarget:
subtarget = c.target.pose.bones.get(subtarget, None)
if target is None:
continue
if subtarget:
# target.matrix_world is used for when the object itself is tranformed
# note the inverted() function may be wrong, for this combination
# but the point is not
c.inverse_matrix = target.matrix_world() * subtarget.matrix.inverted()
else:
c.inverse_matrix = target.matrix_world.inverted()
You probably haven’t ran into an error on your 3rd version but for the specific reason that all your constraints have the same name as the active one’s.
What this operator does is basically click the set_inverse button on the active bone/object
What you have to do is set the active object/bone BEFORE clicking set inverse:
active = b.id_data.data.bones.active
b.id_data.data.bones.active = src.bone
# b.id_data = pose_bone's object
# I prefer to not lock code to active anything, only selected
# plus would transfer easier to multi-objects usage
...
# inverse constraints actions
...
b.id_data.data.bones.active = active # don't change active bone
The set_inverse operator is slower but I don’t know the math to set it correctly for the second version.
By that, I mean if you enable location and disable rotation, the current inverse math value is incorrect, but the set_inverse() op sets it correctly. (note: when the location is actually being used, which it isn’t when a bone is connected to another)
I really do want this to work on bones only, and in pose mode. Ideally on every bones of the armature, even on disabled layers (but if it’s really too complicated, working on all the selected bones is enough).
I tried to integrate your suggestions into my script. But I did not have convincing results. My poor python skills really struggle to follow :’)
One of the attempts:
import bpy
for b in bpy.context.active_object.pose.bones:
for c in b.constraints:
if c.type == "CHILD_OF" and c.target is not None:
target = c.target
subtarget = c.subtarget
if target and target.pose is not None and subtarget:
subtarget = c.target.pose.bones.get(subtarget, None)
if target is None:
continue
if subtarget:
# target.matrix_world is used for when the object itself is tranformed
# note the inverted() function may be wrong, for this combination
# but the point is not
c.inverse_matrix = target.matrix_world() * subtarget.matrix.inverted()
else:
c.inverse_matrix = target.matrix_world.inverted()```
It gives that error:
Traceback (most recent call last):
File "C:\Users\3D3\Desktop\child_of2.blend\set_inverse_chil_of.py", line 16, in <module>
TypeError: 'Matrix' object is not callable
import bpy
ob = bpy.context.active_object
# Take a copy of current layers
org_layers = ob.data.layers[:]
# Show all layers
for i in range(len(org_layers)):
ob.data.layers[i] = True
for b in ob.pose.bones:
for c in b.constraints:
if c.type == "CHILD_OF":
context_py = bpy.context.copy()
context_py["constraint"] = c
ob.data.bones.active = b.bone
bpy.ops.constraint.childof_set_inverse(context_py, constraint="Child Of", owner='BONE')
# Reset back to orginal layer state
for i in range(len(org_layers)):
ob.data.layers[i] = org_layers[i]
Working straight away, even on bones hidden in a layer. Yay \o/
Your script worked for me on multiple objects then it didn’t…
It works on the active objects but the other selected ones aren’t affected.
The weird thing is the original object i copied the constraint from is always affected even if it’s not active.
At some point it works on all selected objects but when i tried to change the name of the constraint to affect second Child Of constraint, it again only worked on the active one.
I don’t know how to script :(but i might have to learn, i really need to Set inverse often.
Here’s another example, There you can see the last created bone never works unless it is active…
Notice also that as soon as theres a Child Of constraint with another name, the script stops working.
Well one problem was that i was running 2.92.
Another was the script was set to constrain=“Child of”,. Changing it to constraint=c.name, worked.
That made it for me but it Set Inverse all the Child of constraints.
It would’ve been nice to only affect selected bones and to be able to specify the constraints affected.
I tried changing the name of the constraint but it doesnt really works.
Hi, I tried to see how to fix this. And I also removed the part where it showed all bone layers back and forth, and made it work on selection only.
I can’t find how to make it work when the constraint’s name isn’t “Child Of”, will check this later.
But can you try this version at least? I made it work on 2.91.
import bpy
ob = bpy.context.active_object
sb = bpy.context.selected_pose_bones
for b in sb:
for c in b.constraints:
if c.type == "CHILD_OF":
# if sb.constraints.name != "Child_Of":
# continue
context_py = bpy.context.copy()
context_py["constraint"] = c
ob.data.bones.active = b.bone
bpy.ops.constraint.childof_set_inverse(context_py, constraint="Child Of", owner='BONE')
``
Yup. I’ll try to check this with some people more experienced than me with bpy. I’m also working on a rework & compilation of all my rigging/animation addons, so it might take a while. But surely it will get there.