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+:
- Create a
Script
tab. You probably already have one.
- Load up the Python script into the window.
- Select your armature and go into Pose Mode with it.
- Run the script with the
Run Script
button in the upper right.
- 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!