0
$\begingroup$

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, in pose mode, 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

I also got this proposition from another guy:

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()

With this one, no errors returned, it executes properly but simply doesn’t affects the rig.

If someone could help me, that would be kind.

Edit: this is one of the tracks if followed, but hasn't worked for me. Also, it looks like it's for object mode and for active selection only, while I'm going for pose mode and effect on the whole armature.

EDIT 2: SOLUTION

A big thank to Konybravo for this piece of code:

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/ And thank y'all for helping me those days.

$\endgroup$
4
  • 2
    $\begingroup$ The error tells you what is wrong: the variable constraint is not recognized, because you named it just c (for c in b.constraints:). So change your code to: context_py["constraint"] = c $\endgroup$ – Jaroslav Jerryno Novotny Nov 13 '18 at 13:02
  • $\begingroup$ Ok. So now i have this : python 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 ? $\endgroup$ – L0Lock Nov 13 '18 at 13:51
  • 2
    $\begingroup$ For override method try adding context_py["active_pose_bone"] = b in pose bone loop. $\endgroup$ – batFINGER Nov 14 '18 at 7:10
  • $\begingroup$ Tried it, still only affects the active bone. $\endgroup$ – L0Lock Nov 14 '18 at 10:09
2
$\begingroup$

Toggle a constraint property

As mentioned, simply changing the inverse_matrix property does nought. If another property of the constraint is toggled, for example the target it appears to work as expected, using the new value assigned to inverse_matrix

Test script, tested on simple example of single bone and default cube parent. Set cmd to 'CLEAR' for clear and 'SET' for set.

EDIT: code to interpolate based on constraint influence when set.

import bpy
from mathutils import Matrix

context = bpy.context

ob = context.object
constraints = [(pb, c) for pb in ob.pose.bones
        for c in pb.constraints if c.type == 'CHILD_OF']

cmd = 'SET' # or 'SET'       
for pb, c in constraints:

    if cmd == 'CLEAR':
        c.inverse_matrix = Matrix.Identity(4)    

    elif cmd == 'SET': 
        if c.target:
            M = ob.convert_space(pose_bone=pb, 
                    matrix=c.target.matrix_world,
                    from_space='WORLD',
                    to_space='POSE')
            P = Matrix.Identity(4).lerp(M, c.influence)
            c.inverse_matrix = P.inverted()
    # toggle a property
    target = c.target
    c.target = None
    c.target = target
    #pb.constraints.update()

why is this so? Constraint properties that are set by a user (esp via UI) update the data appropriately on change.

An operator for each has been chosen to crunch the numbers , and handle the necessary updating. Perhaps a patch to make the python attribute update on change, not sure..

$\endgroup$
1
  • $\begingroup$ A big thank to Konybravo for this piece of code. Working straight away, even on bones hidden in a layer. Yay \o/ And thank y'all for helping me those days. $\endgroup$ – L0Lock Nov 15 '18 at 19:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.