7
$\begingroup$

I've created some shapekeys driven by the bone controllers for my character. Then I gave constraints to the bones on the left side. Now I want to copy and paste them to the right side. Two questions:

  • how to do it?
  • is there a fast way to copy all of the constraints from left to right side?

enter image description here

$\endgroup$

4 Answers 4

5
$\begingroup$

You can use the script below. It's pretty quick and dirty, but works. The value of copyFrom in line 3 determines if you're copying from left-->right (if its value is "L") or right-->left ("R").

The armature must be selected for this to work.

import bpy

copyFrom = "L"
pBones = [ b for b in bpy.context.object.pose.bones if b.name[-2:] == ".%s" % copyFrom ]

for b in pBones:
    for c in b.constraints:
        otherSide = "R" if copyFrom == "L" else "L"
        otherBone = bpy.context.object.pose.bones[ 
            b.name.replace( ".%s" % copyFrom, ".%s" % otherSide ) 
        ]

        nc = otherBone.constraints.new( c.type )

        for prop in dir( c ):
            # This is fairly ugly and dirty, but quick and does the trick...
            try:

                setattr( nc, prop, getattr( c, prop ) )
            except:
                pass

EDITED: As requested, here's the 3rd version of this script that: 1. Inverts the values of properties (turns target bone names from L to R and vice versa) 2. Switches between min and max values in properties between sides. 3. Turns influence 1 into 0 and 0 into 1. 4. For other numerical props that aren't min/max/influence, multiplies values by -1.

Works as described in this gif made by @Gonzou.

import bpy

copyFrom = "L"
pBones = [ b for b in bpy.context.object.pose.bones if b.name[-2:] == ".%s" % copyFrom ]

for b in pBones:
    for c in b.constraints:
        otherSide = "R" if copyFrom == "L" else "L"
        otherBone = bpy.context.object.pose.bones[ 
            b.name.replace( ".%s" % copyFrom, ".%s" % otherSide ) 
        ]

        nc = otherBone.constraints.new( c.type )

        for prop in dir( c ):
            # This is fairly ugly and dirty, but quick and does the trick...
            try:
                constrProp = getattr( c, prop )
                if type( constrProp ) == type(str()) and constrProp[-2:] in [".L", ".R"]:
                    # Replace string property values from L to R and vice versa
                    oppositeVal = constrProp.replace(".L", ".R") if constrProp[-2:] == ".L" else constrProp.replace(".R", ".L")
                    setattr( nc, prop, oppositeVal )
                elif 'max_' in prop:
                    setattr( nc, prop, getattr( c, prop.replace( 'max', 'min' ) ) )
                elif 'min_' in prop:
                    setattr( nc, prop, getattr( c, prop.replace( 'min', 'max' ) ) )
                elif prop == 'influence':
                    # Influence 0 becomes 1 and 1 becomes 0
                    setattr( nc, prop, abs( constrProp - 1 ) )
                elif type( constrProp ) in [ type( float() ), type( int() ) ]:
                    # Invert float and int values ( mult by -1 )
                    setattr( nc, prop, constrProp * -1 )
                else:
                    # Copy all other values as they are
                    setattr( nc, prop, constrProp )
            except:
                pass
$\endgroup$
8
  • $\begingroup$ Works great @TLousky :). Thanks! Is there a way to apply opposed constraints' values to the opposed side? $\endgroup$
    – Paul Gonet
    Nov 17, 2015 at 22:40
  • $\begingroup$ Sure, @Gonzou. Inverse numerical values ( X -1) or bone references? (left to right)? or both? $\endgroup$
    – TLousky
    Nov 17, 2015 at 22:42
  • $\begingroup$ Wow! Amazing edit! This is what I wanted :). The last option I would like to ask you for is a script that flips the values in a way shown in this .gif: giphy.com/gifs/… $\endgroup$
    – Paul Gonet
    Nov 17, 2015 at 23:47
  • 1
    $\begingroup$ I visited your blog BTW. Really interesting stuff you've got there :O. Amazing works! $\endgroup$
    – Paul Gonet
    Nov 17, 2015 at 23:56
  • 1
    $\begingroup$ You're right. I've missed that one O.o. Once again thank you VERY VERY MUCH for your help! $\endgroup$
    – Paul Gonet
    Nov 19, 2015 at 15:42
12
$\begingroup$

Blender has a built in way to copy bone constrains, but it only copies from one bone.

In pose mode, select the bone you want to copy the constraint to, then select the bone with the constraint (that way it is the active bone).

Then press Space to bring up the search box and type in "copy c", select "Copy Constraints to Selected Bones".

$\endgroup$
3
  • 3
    $\begingroup$ Thanks @David♦. This works great! I also found out, that the option you mentioned can be found in the Pose Mode menu (Pose-->Constraints-->Copy Constraints to Selected Bones) $\endgroup$
    – Paul Gonet
    Nov 18, 2015 at 0:10
  • $\begingroup$ @Gonzou PS you do not need the "@ david" whenever the system does not prompt it. (the author always gets pinged) How do comment @ replies work? $\endgroup$
    – David
    Nov 18, 2015 at 0:27
  • $\begingroup$ Good to know. Thanks for a tip. I always wondered how it works BTW. $\endgroup$
    – Paul Gonet
    Nov 18, 2015 at 0:43
0
$\begingroup$

Improving on TLousky's answer.

The problem: you don't always want to invert the sense of the influence. I don't know why this made sense originally. So I just ripped it out and made influence the same. That didn't work, however, as some bones really flew off into crazy-land when I did that. But all the various COPY_* constraints need to have influence left alone. So I did some if/else logic to take care of that case.

Also, many hundreds of my bones are named eg bone.R.001 instead of bone.001.R. This script failed in that case.

Also, I didn't know how to use it. How to use it in Blender 2.80+:

  1. Create a Script tab. You probably already have one.
  2. Load up the Python script into the window.
  3. Select your armature and go into Pose Mode with it.
  4. Run the script with the Run Script button in the upper right.
  5. If you have a complex armature, it can take 30s to a minute to run. Be patient. For some reason you don't get any output until it's done running, so the output is just for debugging after a run is over.

Here's the improved script.

import bpy

"""
the 3rd version of this script that:

1. Inverts the values of properties (turns target bone names from L to R and
   vice versa)
2. Switches between min and max values in properties between sides.
3. Turns influence 1 into 0 and 0 into 1.
4. For other numerical props that aren't min/max/influence, multiplies values
   by -1.
"""

def print(data):
    for window in bpy.context.window_manager.windows:
        screen = window.screen
        for area in screen.areas:
            if area.type == 'CONSOLE':
                override = {'window': window, 'screen': screen, 'area': area}
                bpy.ops.console.scrollback_append(override, text=str(data), type="OUTPUT")

print ("STARTING ======")

copyFrom = "L"
pBones = [ b for b in bpy.context.object.pose.bones if ".%s" % copyFrom in b.name ]
for b in pBones:
    otherSide = "R" if copyFrom == "L" else "L"
    otherBone = bpy.context.object.pose.bones[
        b.name.replace( ".%s" % copyFrom, ".%s" % otherSide )
    ]
    #print(f"Removing Constraints on otherBone={otherBone}")
    [ otherBone.constraints.remove( c ) for c in otherBone.constraints ]
    for c in b.constraints:
        print(f"constraints  b={b} / c={c} / otherBone={otherBone}")
        nc = otherBone.constraints.new( c.type )

        for prop in dir( c ):
            # This is fairly ugly and dirty, but quick and does the trick...
            try:
                constrProp = getattr( c, prop )
                if type( constrProp ) == type(str()) and (".L" in constrProp or ".R" in constrProp):
                    # Replace string property values from L to R and vice versa
                    oppositeVal = constrProp.replace(".L", ".R") if ".L" in constrProp[-2:] else constrProp.replace(".R", ".L")
                    setattr( nc, prop, oppositeVal )
                elif 'max_' in prop:
                    setattr( nc, prop, getattr( c, prop.replace( 'max', 'min' ) ) )
                elif 'min_' in prop:
                    setattr( nc, prop, getattr( c, prop.replace( 'min', 'max' ) ) )
                elif prop == 'influence':
                    # Influence 0 becomes 1 and 1 becomes 0 - why?
                    # if this is a Copy Rotation/Location/Scale/etc constraint, we want to keep influence
                    if c.type[:5] == "COPY_":
                        setattr( nc, prop, abs( constrProp ) )
                    else:
                        setattr( nc, prop, abs( constrProp - 1 ) )
                elif type( constrProp ) in [ type( float() ), type( int() ) ]:
                    # Invert float and int values ( mult by -1 )
                    setattr( nc, prop, constrProp * -1 )
                else:
                    # Copy all other values as they are
                    setattr( nc, prop, constrProp )
            except:
                pass

print ("FINISHED ======")

This version of the script works for me much better, so I'm sharing it to the world!

$\endgroup$
1
  • $\begingroup$ Note: this seems to completely break any IK constraints on bones. Pole angles are reversed, all influences zero. Also seems to break limit location/rotation constraints and stretch-to constraints. So... use with caution. $\endgroup$ Mar 4, 2020 at 2:44
0
$\begingroup$

NOTE - You can also use a Blender inbuilt function called "Edit_Mode > Symmetrize"

OR

import bpy

armature = bpy.context.active_object.data

if not bpy.context.mode == 'EDIT_ARMATURE':
    armature.select = True
    bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.armature.select_all()
bpy.ops.armature.symmetrize()
$\endgroup$

You must log in to answer this question.

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