Well, just an update to post my script.
As you can guess, I solved the problems. Some of them, anyways - a lot of this armature / pose stuff is hard to get at. It won’t copy everything, but as close as I could get. And it probably doesn’t follow Blender conventions, but it should be clear enough to be used by other people.
So, here it is:
from Blender import *
##
## bonecopy 0.05 by Simon Bækdahl Nielsen
## Do with this script whatever you like.
## It will duplicate an entire armature, and keep relative
## parents and constraint targets.
## For now, it doesn't copy armature
## layers and custom bone objects, but
## will keep as many settings as I could manage.
##
## Usage: Select your armature in object mode,
## open this script in Blender's text editor,
## then enter your settings below, and press alt-P to run it.
##
## Be sure to save a copy of your work first!
##
## I hope it works for you, but there are no guarantees.
##
## setup
numberOfCopies = 1
offsetX = 0
offsetY = 0
offsetZ = 3
## Add the names of bones you don't want copied to this list,
## in the form ["bonename1", "bonename2", etc]. Duplicated
## children of bones in this list will also be children of the
## original bone.
dontCopy = []
## end setup
##
## functions
##
def copyBone(bone, appendToName):
editBone = Armature.Editbone()
editBone.tail = bone.tail #Mathutils.Vector(3,4,0)
editBone.head = bone.head #Mathutils.Vector(0,0,10)
newBoneName = bone.name + appendToName
arm.bones[newBoneName] = editBone
return newBoneName
def connectBone(origBone, newBone, appendToName):
newOptions = origBone.options
try:
origParent = origBone.parent.name
#print "origParent: ", origParent
#need to check for non-copied parents
if dontCopy.count(origParent) != 0:
# parent is in dontCopy list, use original
newParent = origParent
#but make sure it's not connected
#print "options before: ", newOptions
modifiedOptions = []
for option in newOptions:
#print "option: ", option
if option != Armature.CONNECTED:
modifiedOptions.append(option)
newOptions = modifiedOptions
#print "options after: ", newOptions
else:
# parent has been copied, use copy
newParent = origParent + appendToName
print "parenting: ", newBone.name, "to: ", newParent
newBone.parent = arm.bones[newParent]
except:
error = 1
newBone.options = newOptions
newBone.deformDist = origBone.deformDist
newBone.roll = origBone.roll
try:
newBone.subdivision = origBone.subdivision
except:
error=1
newBone.weight = origBone.weight
def translateBone(bone):
bone.tail = bone.tail + translateVector
bone.head = bone.head + translateVector
def copyConstraint(originalBone, newBone, appendToName):
try:
#print "limitmax: ", originalBone.limitmax
newBone.limitmax = originalBone.limitmax
except:
error = 1
try:
newBone.limitmin = originalBone.limitmin
except:
error = 1
try:
for constraint in originalBone.constraints:
currentConstraint = newBone.constraints.append(constraint.type)
print 'Copying constraint: ', constraint, " from: ", originalBone.name, " to: ", newBone.name
#get the constants corresponding to constraint settings
for constraintKey in Constraint.Settings.values():
try:
#copy the settings
currentConstraint[constraintKey] = constraint[constraintKey]
except:
error = 1
#we need to check for target bone, and change it
try:
if constraint[Constraint.Settings.BONE] != "" :
if dontCopy.count(constraint[Constraint.Settings.BONE]) != 0:
#target is in nocopy list, keep original
newTargetBoneName = constraint[Constraint.Settings.BONE]
else:
newTargetBoneName = constraint[Constraint.Settings.BONE] + appendToName
print "Setting target bone: ", newTargetBoneName
currentConstraint[Constraint.Settings.BONE] = newTargetBoneName
except:
error = 1
except:
error = 1
##
## main ######
##
print
print "***********************"
print "Beginning bonecopy 0.05"
print
ob = Object.GetSelected()
if len(ob) <> 1:
print "please select exactly one armature"
else:
firstOb = ob[0]
if firstOb.getType() <> "Armature":
print "Selected object is not an armature."
else:
arm = Armature.Get(firstOb.getName())
print "Duplicating bones in armature: ", arm
print
#make a list of the existing bones
originalBoneNames = []
for bone in arm.bones.values():
if dontCopy.count(bone.name) == 0:
originalBoneNames.append(bone.name)
#make the armature editable
arm.makeEditable()
#copy the bones:
translateVectorStep = Mathutils.Vector(offsetX,offsetY,offsetZ)
translateVector = Mathutils.Vector(0,0,0)
copiedBones = []
#loop through number of copies
for i in range (0,numberOfCopies):
appendToName = ".copy" + str(i)
translateVector = translateVector + translateVectorStep
# loop through original bones
for boneName in originalBoneNames:
newBoneName = copyBone(arm.bones[boneName], appendToName)
translateBone(arm.bones[newBoneName])
copiedBones.append(newBoneName)
print "duplicating: ",boneName," to: ", newBoneName
#connect the bones - set parents and options
#loop through number of copies
print
print "Connecting bones and duplicating settings."
print
for i in range (0,numberOfCopies):
appendToName = ".copy" + str(i)
#loop through original bones
for boneName in originalBoneNames:
newBoneName = boneName + appendToName
connectBone(arm.bones[boneName], arm.bones[newBoneName], appendToName)
#save changes
arm.update()
#make a list of the posebones
pose = firstOb.getPose()
print
print "Duplicating constraints and pose mode settings for pose: ", pose
print
originalPoseBoneNames = []
for poseBone in pose.bones.values():
if dontCopy.count(poseBone.name) == 0:
originalPoseBoneNames.append(poseBone.name)
#make the armature editable
arm.makeEditable()
#copy the constraints
#loop through number of copies
for i in range (0,numberOfCopies):
appendToName = ".copy" + str(i)
#loop through original constraints
#print "keys: ", originalConstraints.values()
#print "values: ", originalConstraints.keys()
#values = originalConstraints.values();
for poseBoneName in originalPoseBoneNames:
newPoseBoneName = poseBoneName + appendToName
copyConstraint(pose.bones[poseBoneName], pose.bones[newPoseBoneName], appendToName)
#save changes
arm.update()
print
print "bonecopy done"
print
# Notes: It's a bit messy, I know.
# Would like to keep:
# Armature layers
# Pose mode things (locks, limits (tried that), custom objects, segments)
# Constraint influence
#
# There may be some bugs, but it works for me. (Blender 2.42A, OSX / PPC, Python 2.4)