import bpy
import bmesh
import math as m
import fnmatch
character = "MyCharacter"
rig = f"{character}_Armature"
origin = (0,0,0)
emplist = bpy.data.collections['EmptyCollection'].all_objects
###############################################
#### Bone heirarchy construction functions ####
###############################################
def findEmpty(e):
for em in emplist:
if em.name == e:
return em.location
def SelectReturn(b):
for bone in Armature.data.edit_bones:
if fnmatch.fnmatchcase(bone.name, b):
return bone
def SelectListReturn(*args):
blist = []
for bone in Armature.data.edit_bones:
for a in args:
if fnmatch.fnmatchcase(bone.name, a):
blist.append(bone)
return blist
def SelectSymmetrize():
for bone in Armature.data.edit_bones:
if fnmatch.fnmatchcase(bone.name, '*l'):
bone.select = True
bone.select_head = True
bone.select_tail = True
bpy.ops.armature.symmetrize()
def AddConstraint(con, activeBone, subtar, polsubtar=None, limitcon=None, PHY_tar=None, COLL_tar=None):
## Keyword args (**kwargs) are checks. Different constraints have different data fields that need to be filled.
## if any of the **kwargs are true (not None) then the if conditionals will redirect the process to meet the
## needs of that constraint.
## Or.... I could just break these functions (methods in python) into different function for each use case
if limitcon != None:
if limitcon == 'LOC':
pbone = bpy.context.object.pose.bones[activeBone]
constraint = pbone.constraints.new(con)
elif limitcon == 'ROT':
pbone = bpy.context.object.pose.bones[activeBone]
constraint = pbone.constraints.new(con)
else:
pbone = bpy.context.object.pose.bones[activeBone]
if COLL_tar != None:
target = bpy.data.objects[COLL_tar]
subtarget = bpy.data.objects[COLL_tar].vertex_groups[subtar]
elif PHY_tar != None:
target = bpy.data.objects[PHY_tar]
subtarget = bpy.data.objects[PHY_tar].vertex_groups[subtar]
else:
target = bpy.context.scene.objects.get(rig)
subtarget = bpy.context.scene.objects.get(rig).pose.bones.get(subtar)
constraint = pbone.constraints.new(con)
constraint.target = target
constraint.subtarget = subtarget.name
if polsubtar != None:
poletarget = bpy.context.scene.objects.get(rig)
polesubtarget = bpy.context.scene.objects.get(rig).pose.bones.get(polsubtar)
constraint.pole_target = poletarget
constraint.pole_subtarget = polsubtar
return constraint
### def SymmetrizeConstraints() - I refactored from someone elses code I found on stackoverflow or one of those sites
### there's no point spending hours re-inventing the wheel when someone else already did it, and posted it.
### HINT, HINT...
def SymmetrizeConstraints():
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 and 'LIMIT_LOCATION' not in c.type:
setattr( nc, prop, getattr( c, prop.replace( 'max', 'min' ) ) * -1 )
elif 'min_' in prop and 'LIMIT_LOCATION' not in c.type:
setattr( nc, prop, getattr( c, prop.replace( 'min', 'max' ) ) * -1 )
elif prop == 'influence':
# Influence 0 becomes 1 and 1 becomes 0
setattr( nc, prop, abs( constrProp) )
elif prop == 'distance':
# distance stays the same
setattr( nc, prop, constrProp )
elif prop == 'rest_length':
# Copy original length
setattr( nc, prop, constrProp )
elif prop == 'head_tail':
# Copy original length
setattr( nc, prop, constrProp )
elif prop == 'chain_count':
# Copy original length
setattr( nc, prop, constrProp )
elif prop == 'iterations':
# Copy original length
setattr( nc, prop, constrProp )
elif prop == 'weight':
# Copy original length
setattr( nc, prop, constrProp )
elif type( constrProp ) in [ type( float() ), type( int() ) ] and prop != 'pole_angle' and c.type != 'LIMIT_LOCATION':
# Invert float and int values ( mult by -1 )
setattr( nc, prop, constrProp * -1 )
elif prop == 'pole_angle':
# invert original in degrees
if constrProp==m.radians(180):
setattr( nc, prop, m.radians(0))
elif constrProp==m.radians(0):
setattr( nc, prop, m.radians(180))
else:
# Copy all other values as they are
setattr( nc, prop, constrProp )
except:
pass
def constraintFix(bone, const, prop, value):
code = f"bpy.context.object.pose.bones['{bone}'].constraints['{const}'].{prop} = {value}"
exec(code)
##################################
#### Driver Linking Functions ####
##################################
## bone:bone the driver is on, drivenChannel:property channel to drive, tarBone:driving obj or bone
## tarChannel:driving channel on tarbone, space:coordinate space, exp:driver expression,
## custVar:custom driver var(ex. Distance), varCount:number of non-custom variables,
## package:string path to constraint property to add driver, channelIndex:vector channel (xyz) to add driver..if needed
## vtype:driver variable type, dtype:Driver type (first drop down menu in driver editor)
def AddDriver(bone, drivenChannel, tarBone, tarChannel, space, exp, custVar=[], varCount=1,\
package=None, channelIndex=-1, vtype='TRANSFORMS', dtype='SCRIPTED'):
if package!=None:
con = bpy.data.objects[rig]
driv = con.driver_add(package)
driv.driver.type = dtype
driv.driver.expression = exp
else:
driv = bpy.data.objects[rig].pose.bones[bone].driver_add(drivenChannel, channelIndex)
driv.driver.type = dtype
driv.driver.expression = exp
if varCount>1:
for v in range(varCount):
var = driv.driver.variables.new()
var.name = 'var'+str(v)
var.type = vtype
var.targets[0].id = bpy.data.objects[rig]
var.targets[0].bone_target = tarBone[v]
var.targets[0].transform_type = tarChannel[v]
var.targets[0].transform_space = space[v]
elif varCount==1:
var = driv.driver.variables.new()
var.name = 'var0'
var.type = vtype
var.targets[0].id = bpy.data.objects[rig]
var.targets[0].bone_target = tarBone
var.targets[0].transform_type = tarChannel
var.targets[0].transform_space = space
else:
pass
if custVar != []:
if len(custVar)==5:
var = driv.driver.variables.new()
var.name = 'var'+str(len(driv.driver.variables)-1)
var.type = custVar[0]
var.targets[0].id = bpy.data.objects[rig]
var.targets[0].bone_target = custVar[1]
var.targets[0].transform_space = custVar[2]
var.targets[1].id = bpy.data.objects[rig]
var.targets[1].bone_target = custVar[3]
var.targets[1].transform_space = custVar[4]
elif len(custVar)==3:
var = driv.driver.variables.new()
var.name = 'var'+str(len(driv.driver.variables)-1)
var.type = custVar[0]
var.targets[0].id = bpy.data.objects[rig]
var.targets[0].bone_target = custVar[1]
var.targets[1].id = bpy.data.objects[rig]
var.targets[1].bone_target = custVar[2]
else:
pass
return driv
def AddPropertyDriver(bone, drivenChannel, tarId, tarDataPath, exp,\
varCount=1, package=None, channelIndex=-1, vtype='SINGLE_PROP', dtype='SCRIPTED'):
if package!=None:
con = bpy.data.objects[rig]
driv = con.driver_add(package)
driv.driver.type = dtype
driv.driver.expression = exp
else:
driv = bpy.data.objects[rig].pose.bones[bone].driver_add(drivenChannel, channelIndex)
driv.driver.type = dtype
driv.driver.expression = exp
if varCount>1:
for v in range(varCount):
var = driv.driver.variables.new()
var.name = 'var'+str(v)
var.type = vtype
var.targets[v].id = bpy.data.objects[tarId[v]]
var.targets[v].data_path = tarDataPath[v]
else:
var = driv.driver.variables.new()
var.name = 'var'
var.type = vtype
var.targets[0].id = bpy.data.objects[tarId]
var.targets[0].data_path = tarDataPath
return driv
def Add_PHY_PropertyDriver(mesh, mod, prop, tarId, tarDataPath, vtype='SINGLE_PROP', dtype='AVERAGE'):
package = f'modifiers["{mod}"].settings.{prop}'
con = bpy.data.objects[mesh]
driv = con.driver_add(package)
driv.driver.type = dtype
var = driv.driver.variables.new()
var.name = 'var'
var.type = vtype
var.targets[0].id = bpy.data.objects[tarId]
var.targets[0].data_path = tarDataPath
return driv
############################################
########## Bone Physics Functions ##########
############################################
### months later after not looking at this code... I really should have commented this function more, really...
def add_Physics(parbone, PHYgroup):
bpy.context.scene.cursor.location = (0,0,0)
parentBoneLOC = bpy.data.objects[rig].matrix_world @ \
bpy.data.objects[rig].pose.bones[parbone].matrix.to_translation() ## get the world space location in ref. to parent bone
bpy.ops.mesh.primitive_cube_add(enter_editmode=True) ## create a primitive cube
PHy_mesh = 'PHY_mesh_'+ parbone ## save string name of current physics group to a var
bpy.context.active_object.name = PHy_mesh ## name the primitive cube as mesh of physics group
mesh = bmesh.from_edit_mesh(bpy.context.object.data) ## turn primitive cube to bmesh object
if mesh.select_mode != 'VERT':
bpy.ops.mesh.select_mode(type='VERT') ## switch to vertex mode of cube, now bmesh obj
bpy.ops.mesh.select_all(action='SELECT') ## select all verts of cube
bpy.ops.mesh.merge(type='CENTER') ## colapse/merge all verts down to one
bonelist = [] ## create empty list
mesh.verts.ensure_lookup_table() ## ensure the vert lookup table is up to date? read that you should do this frequent working with bmesh
for bone in bpy.data.objects[rig].pose.bones: ## loop thru all the pose bones of the rig and append() all bones of this physics group to
if PHYgroup in bone.name: ## the empty list you created
bonelist.append(bone.name)
fm = bpy.data.objects[rig].matrix_world @ bpy.data.objects[rig].pose.bones[bonelist[0]].matrix ## get worldspace location of first bone in list
fm = fm.to_translation() ## previous line converts localspace to worldspace....this function converts matrix coordinates to vector...
mesh.verts.ensure_lookup_table()
mesh.verts[0].co = fm ## relocate your single vert of mesh (simulation obj) to that bone, just the vert, not the obj
bpy.ops.object.vertex_group_add() ## create a new vertex group for this sim obj
bpy.context.object.vertex_groups.active.name = 'Vgrp_' + bonelist[0] ## name vertex group
bpy.ops.object.vertex_group_assign() ## assign single vert to vertex group
if len(bonelist) > 1: ## ok, this loop goes thru all the physic bones in our list, duplicates the sim obj *Vert*, now 1 obj 2 verts
for i in range(1, len(bonelist)): ## and so on for each iteration of the loop, removes the new vert from it's original vertex group and
mesh.verts.ensure_lookup_table() ## creates a new one, relocates new vert to the current bone being processed in the loop
mesh.verts[i-1].select_set(True) ## and loop thru again til done
bpy.ops.mesh.duplicate_move(MESH_OT_duplicate={"mode":1},\
TRANSFORM_OT_translate={"value":(0, 0, 0), "orient_axis_ortho":'X',\
"orient_type":'GLOBAL', "orient_matrix":((0, 0, 0), (0, 0, 0), (0, 0, 0)),\
"orient_matrix_type":'GLOBAL', "constraint_axis":(False, False, False),\
"mirror":False, "use_proportional_edit":False, "proportional_edit_falloff":'SMOOTH',\
"proportional_size":1, "use_proportional_connected":False, "use_proportional_projected":False,\
"snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False,\
"snap_normal":(0, 0, 0), "gpencil_strokes":False, "cursor_transform":False, "texture_space":False,\
"remove_on_cancel":False, "view2d_edge_pan":False, "release_confirm":False, "use_accurate":False,\
"use_automerge_and_split":False})
for v in mesh.verts:
v.select_set(False)
mesh.verts.ensure_lookup_table()
mesh.verts[i].select_set(True)
bpy.ops.object.vertex_group_remove_from()
bpy.ops.object.vertex_group_add()
bpy.context.object.vertex_groups.active.name = 'Vgrp_' + bonelist[i]
bpy.ops.object.vertex_group_assign()
fm = bpy.data.objects[rig].matrix_world\
@ bpy.data.objects[rig].pose.bones[bonelist[i]].matrix
fm = fm.to_translation()
mesh.verts.ensure_lookup_table()
mesh.verts[i].co = fm
bpy.ops.object.mode_set(mode='OBJECT') ## get out of edit mode, back to object
bpy.context.scene.cursor.location = parentBoneLOC ## move the 3d cursor to the parent bone of the physics bones hierarchy
bpy.ops.object.origin_set(type='ORIGIN_CURSOR') ## Set the origin of the sim obj to the location of parent bone
bpy.ops.object.select_all(action='DESELECT') ## deselect all
bpy.data.objects[PHy_mesh].select_set(True) ## select the simulation mesh
bpy.ops.object.modifier_add(type='SOFT_BODY') ## add a softbody modifier
bpy.data.objects[PHy_mesh].modifiers["Softbody"].settings.friction = 13 ## change friction settings
bpy.data.objects[PHy_mesh].modifiers["Softbody"].settings.mass = .2 ## ## change mass settings
bpy.ops.object.select_all(action='DESELECT') ## deselect all
bpy.data.objects[rig].select_set(True) ## select the rig object
bpy.context.view_layer.objects.active = bpy.data.objects[rig] ## make the rig object the active object
bpy.ops.object.mode_set(mode='POSE') ## switch to pose mode
for bone in bonelist: ## matching names and adding constraints to main and aim bones of current physics group
if fnmatch.fnmatchcase(bone, '*MAIN_l'):
for bon in bonelist:
if fnmatch.fnmatchcase(bon, '*AIM_l'):
boneVarDamp = AddConstraint('DAMPED_TRACK', bone, bon)
boneVarDamp.name = 'PHY_DAMP_' + bone
boneVarDamp.head_tail = 0
boneVarDamp.track_axis = 'TRACK_Y'
boneVarDamp.influence = 1
if fnmatch.fnmatchcase(bone, '*MAIN_r'):
for bon in bonelist:
if fnmatch.fnmatchcase(bon, '*AIM_r'):
boneVarDamp = AddConstraint('DAMPED_TRACK', bone, bon)
boneVarDamp.name = 'PHY_DAMP_' + bone
boneVarDamp.head_tail = 0
boneVarDamp.track_axis = 'TRACK_Y'
boneVarDamp.influence = 1
## copy location constraint of physics bone to sim mesh vert at its location
## so physics bones are following their respective vert via custom vertex groups we made for each vert
## and each vert is effected by softbody dynamics.... In short "makes things shake and jiggle with motion"
## other constraints keep the motion of the physics within realistic ranges
boneVarloc = AddConstraint('COPY_LOCATION', bone, 'Vgrp_' + bone, PHY_tar= PHy_mesh)
boneVarloc.name = 'PHY_LOC_' + bone
boneVarloc.use_x = True
boneVarloc.use_y = True
boneVarloc.use_z = True
boneVarloc.use_offset = False
boneVarloc.target_space = 'WORLD'
boneVarloc.owner_space = 'WORLD'
if 'MAIN_' in bone:
boneVarloc.influence = .7
else:
boneVarloc.influence = 1
boneVarLim = AddConstraint('LIMIT_LOCATION', bone, '', limitcon='LOC')
boneVarLim.name = 'PHY_LIM_' + bone
if 'AIM_' in bone:
param = {'min':[-.05,-.05,-.05], 'max':[.05,.05,.05]}
else:
param = {'min':[-.03,-.03,-.03], 'max':[.03,.03,.03]}
x, y, z = 0,1,2
for end in ['min','max']:
for ax in ['x','y','z']:
code = f"boneVarLim.use_{end}_{ax} = True\nboneVarLim.{end}_{ax} = {param.get(end)[eval(ax)]}"
exec(code)
### for some operations in bpy module you have to concatenate code into a string and run it thru an exec() function
### I don't know why, you just do...
### eval() takes a string literal and finds the corresponding variable with scope 'x' -> x -> which equals 0
boneVarLim.owner_space = 'LOCAL'
boneVarLim.influence = 1
bpy.ops.object.mode_set(mode='OBJECT', toggle=False) ## get out of edit mode, back to object
bpy.ops.object.select_all(action='DESELECT') ## deselect all
bpy.data.objects[rig].select_set(True) ## select the rig object
bpy.context.view_layer.objects.active = bpy.data.objects[rig] ## make the rig object the active object
bpy.ops.object.mode_set(mode='POSE') ## go to pose mode.....all this is just parenting the sim obj/mesh to the main bone
bpy.context.object.pose.bones[parbone].bone.select = True ## that was passed as the arg 'parbone' to this function
bpy.context.object.data.bones.active = bpy.context.object.pose.bones[parbone].bone
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[PHy_mesh].select_set(True)
bpy.data.objects[rig].select_set(True)
bpy.ops.object.parent_set(type='BONE', keep_transform=True)
### ALWAYS COMMENT YOUR CODE!!
#################################
############# Setup #############
#################################
#Set the Cursor to the world origin
bpy.context.scene.cursor.location = 0,0,0
#create an armature
bpy.ops.object.armature_add( enter_editmode=False )
#rename armature
bpy.data.objects['Armature'].name = rig
#assign armature object to variable
Armature = bpy.data.objects[rig]
ArmatureOps = bpy.ops.object
ArmEdit = Armature.data.edit_bones
ArmBones = Armature.data.bones
ArmPose = Armature.pose.bones
ArmContx = bpy.context.object
#select the armature
Armature.select_set(True)
#switch to editmode
ArmatureOps.mode_set(mode='EDIT', toggle=False)
#change bone viewport display type
bpy.data.armatures[bpy.context.object.data.name].display_type = 'STICK'
#delete default bone
ArmEdit.remove(ArmEdit[0])
############################################
############## BUILD SKELETON ##############
############################################
#add root
root = ArmContx.data.edit_bones.new('root')
root.head = origin
root.tail = (0,1,0)
#add gravity_ROOT
gravity_ROOT = ArmContx.data.edit_bones.new('gravity_ROOT')
gravity_ROOT.head = origin
gravity_ROOT.tail = findEmpty('gravity_ROOT_tail')
gravity_ROOT.roll = m.radians(0)
#add gravity_AIM
gravity_AIM = ArmContx.data.edit_bones.new('gravity_AIM')
gravity_AIM.head = findEmpty('gravity_AIM_head')
gravity_AIM.tail = findEmpty('gravity_AIM_tail')
gravity_AIM.roll = m.radians(0)
Armature.data.edit_bones['gravity_AIM'].parent = Armature.data.edit_bones['gravity_ROOT']
Armature.data.edit_bones['gravity_AIM'].use_connect = False
#add PROPERTIES
PROPERTIES = ArmContx.data.edit_bones.new('PROPERTIES')
PROPERTIES.head = findEmpty('PROPERTIES_head')
PROPERTIES.tail = findEmpty('PROPERTIES_tail')
PROPERTIES.roll = m.radians(89)
Armature.data.edit_bones['PROPERTIES'].parent = Armature.data.edit_bones['root']
Armature.data.edit_bones['PROPERTIES'].use_connect = False
#add pelvis
pelvis = ArmContx.data.edit_bones.new('pelvis')
pelvis.head = findEmpty('pelvis_head')
pelvis.tail = findEmpty('spine_01_head')
pelvis.roll = m.radians(89)
Armature.data.edit_bones['pelvis'].parent = Armature.data.edit_bones['root']
Armature.data.edit_bones['pelvis'].use_connect = False
#add spine_01
spine_01 = ArmContx.data.edit_bones.new('spine_01')
#....continues
## add physics bone groups
if True: ## Physics bones need special prefixes PHY_07_
#add PHY_07_bellyShake_MAIN_l
PHY_07_bellyShake_MAIN_l = ArmContx.data.edit_bones.new('PHY_07_bellyShake_MAIN_l')
PHY_07_bellyShake_MAIN_l.head = findEmpty('PHY_07_bellyShake_MAIN_l_head')
PHY_07_bellyShake_MAIN_l.tail = findEmpty('PHY_07_bellyShake_MAIN_l_tail')
PHY_07_bellyShake_MAIN_l.roll = m.radians(60)
Armature.data.edit_bones['PHY_07_bellyShake_MAIN_l'].parent = Armature.data.edit_bones['spine_02_PhyINTR_l']
Armature.data.edit_bones['PHY_07_bellyShake_MAIN_l'].use_connect = False
#add PHY_07_bellyShake_topOuter_l
PHY_07_bellyShake_topOuter_l = ArmContx.data.edit_bones.new('PHY_07_bellyShake_topOuter_l')
PHY_07_bellyShake_topOuter_l.head = findEmpty('PHY_07_bellyShake_topOuter_l_head')
PHY_07_bellyShake_topOuter_l.tail = findEmpty('PHY_07_bellyShake_topOuter_l_tail')
PHY_07_bellyShake_topOuter_l.roll = m.radians(60)
Armature.data.edit_bones['PHY_07_bellyShake_topOuter_l'].parent = Armature.data.edit_bones['PHY_07_bellyShake_MAIN_l']
Armature.data.edit_bones['PHY_07_bellyShake_topOuter_l'].use_connect = False
#add PHY_07_bellyShake_bottomOuter_l
PHY_07_bellyShake_bottomOuter_l = ArmContx.data.edit_bones.new('PHY_07_bellyShake_bottomOuter_l')
PHY_07_bellyShake_bottomOuter_l.head = findEmpty('PHY_07_bellyShake_bottomOuter_l_head')
PHY_07_bellyShake_bottomOuter_l.tail = findEmpty('PHY_07_bellyShake_bottomOuter_l_tail')
PHY_07_bellyShake_bottomOuter_l.roll = m.radians(60)
Armature.data.edit_bones['PHY_07_bellyShake_bottomOuter_l'].parent = Armature.data.edit_bones['PHY_07_bellyShake_MAIN_l']
Armature.data.edit_bones['PHY_07_bellyShake_bottomOuter_l'].use_connect = False
#add PHY_07_bellyShake_bottomInner_l
PHY_07_bellyShake_bottomInner_l = ArmContx.data.edit_bones.new('PHY_07_bellyShake_bottomInner_l')
PHY_07_bellyShake_bottomInner_l.head = findEmpty('PHY_07_bellyShake_bottomInner_l_head')
PHY_07_bellyShake_bottomInner_l.tail = findEmpty('PHY_07_bellyShake_bottomInner_l_tail')
PHY_07_bellyShake_bottomInner_l.roll = m.radians(60)
Armature.data.edit_bones['PHY_07_bellyShake_bottomInner_l'].parent = Armature.data.edit_bones['PHY_07_bellyShake_MAIN_l']
Armature.data.edit_bones['PHY_07_bellyShake_bottomInner_l'].use_connect = False
"""
more code in this section.
- All creating the bones of the rig
- setting the parent child relationship.
- Set non-deforming bones to false
- Set non-use inherit rotation to false
- Set non-use inherit scale to false
- changing rotation mode to either euler or quanternion
- adding controllers
Skipping a few sections to constraints.
example adding constraints
"""
# Symmetrize bones to right side
SelectSymmetrize()
#############################################
############## ADD CONSTRAINTS ##############
#############################################
#add constraints to------------ calf_IKCONST_l
calf_IKCONST_l = AddConstraint('IK', 'calf_l', 'ikHandleLeg_l', polsubtar='poleVectorLeg_l')
calf_IKCONST_l.name = 'calf_IKCONST_l'
calf_IKCONST_l.pole_angle = m.radians(0)
calf_IKCONST_l.chain_count = 2
calf_IKCONST_l.use_tail = True
calf_IKCONST_l.use_stretch = True
calf_IKCONST_l.influence = 1
#add constraints to------------ autoPoleNormLeg_0_DAMPTRK_l
autoPoleNormLeg_0_DAMPTRK_l = AddConstraint('DAMPED_TRACK', 'autoPoleNormLeg_0_l', 'autoPoleNormLeg_2_l')
autoPoleNormLeg_0_DAMPTRK_l.name = 'autoPoleNormLeg_0_DAMPTRK_l'
autoPoleNormLeg_0_DAMPTRK_l.head_tail = 0
autoPoleNormLeg_0_DAMPTRK_l.track_axis = 'TRACK_Y'
autoPoleNormLeg_0_DAMPTRK_l.influence = 1
if True:# always true. just for formating
#add constraints to------------ autoPoleNormLeg_1_COPLOC_01_l
autoPoleNormLeg_1_COPLOC_01_l = AddConstraint('COPY_LOCATION', 'autoPoleNormLeg_1_l', 'autoPoleNormLeg_0_l')
autoPoleNormLeg_1_COPLOC_01_l.name = 'autoPoleNormLeg_1_COPLOC_01_l'
autoPoleNormLeg_1_COPLOC_01_l.head_tail = 0
autoPoleNormLeg_1_COPLOC_01_l.use_x = True
autoPoleNormLeg_1_COPLOC_01_l.use_y = True
autoPoleNormLeg_1_COPLOC_01_l.use_z = True
autoPoleNormLeg_1_COPLOC_01_l.target_space = 'WORLD'
autoPoleNormLeg_1_COPLOC_01_l.owner_space = 'WORLD'
autoPoleNormLeg_1_COPLOC_01_l.influence = 1
#add constraints to------------ autoPoleNormLeg_1_COPLOC_02_l
autoPoleNormLeg_1_COPLOC_02_l = AddConstraint('COPY_LOCATION', 'autoPoleNormLeg_1_l', 'autoPoleNormLeg_2_l')
autoPoleNormLeg_1_COPLOC_02_l.name = 'autoPoleNormLeg_1_COPLOC_02_l'
autoPoleNormLeg_1_COPLOC_02_l.head_tail = 0
autoPoleNormLeg_1_COPLOC_02_l.use_x = True
autoPoleNormLeg_1_COPLOC_02_l.use_y = True
autoPoleNormLeg_1_COPLOC_02_l.use_z = True
autoPoleNormLeg_1_COPLOC_02_l.target_space = 'WORLD'
autoPoleNormLeg_1_COPLOC_02_l.owner_space = 'WORLD'
autoPoleNormLeg_1_COPLOC_02_l.influence = 0.5
#add constraints to------------ autoPoleNormLeg_1_COPROT_l
autoPoleNormLeg_1_COPROT_l = AddConstraint('COPY_ROTATION', 'autoPoleNormLeg_1_l', 'autoPoleNormLeg_2_l')
autoPoleNormLeg_1_COPROT_l.name = 'autoPoleNormLeg_1_COPROT_l'
autoPoleNormLeg_1_COPROT_l.use_x = True
autoPoleNormLeg_1_COPROT_l.use_y = True
autoPoleNormLeg_1_COPROT_l.use_z = True
autoPoleNormLeg_1_COPROT_l.target_space = 'WORLD'
autoPoleNormLeg_1_COPROT_l.owner_space = 'WORLD'
autoPoleNormLeg_1_COPROT_l.influence = 1
"""
more code in this section.
all more if this, adding contraints to bone
ie. IK, copy rotation, copy location, stretchto, damptrack, etc..
examples adding drivers
"""
#############################################
############## ADD DRIVERS ##################
#############################################
############## BONE DRIVERS ##################
#add driver to----------------- innerThighAdj_l
innerThighAdj_LocX_L = AddDriver("innerThighAdj_l", 'location', ['thigh_l', 'thigh_l'], ['ROT_X', 'ROT_Z'],\
['LOCAL_SPACE', 'LOCAL_SPACE'], 'abs(var0*.3) if var0>0 else (var1*.35)', varCount=2, channelIndex=0)
innerThighAdj_LocY_L = AddDriver("innerThighAdj_l", 'location', 'thigh_l', 'ROT_Z', 'LOCAL_SPACE', 'abs(var0*.45) if var0<0 else (var0*.45)', channelIndex=1)
innerThighAdj_LocZ_L = AddDriver("innerThighAdj_l", 'location', 'thigh_l', 'ROT_Z', 'LOCAL_SPACE', 'var0*.4 if var0<0 else -(var0*.3)', channelIndex=2)
innerThighAdj_LocX_R = AddDriver("innerThighAdj_r", 'location', ['thigh_r', 'thigh_r'], ['ROT_X', 'ROT_Z'],\
['LOCAL_SPACE', 'LOCAL_SPACE'], '-abs(var0*.3) if var0>0 else -(var1*.35)', varCount=2, channelIndex=0)
innerThighAdj_LocY_R = AddDriver("innerThighAdj_r", 'location', 'thigh_r', 'ROT_Z', 'LOCAL_SPACE', 'abs(var0*-.45) if var0>0 else -(var0*.45)', channelIndex=1)
innerThighAdj_LocZ_R = AddDriver("innerThighAdj_r", 'location', 'thigh_r', 'ROT_Z', 'LOCAL_SPACE', 'var0*-.4 if var0>0 else (var0*.3)', channelIndex=2)
#add driver to----------------- thigh_l
thigh_ScaleY_L = AddDriver('thigh_l', 'scale', 'thigh_l', 'ROT_Z', 'LOCAL_SPACE', 'max(1-abs(var0 *.03), .98)', channelIndex=1)
thigh_ScaleY_R = AddDriver('thigh_r', 'scale', 'thigh_r', 'ROT_Z', 'LOCAL_SPACE', 'max(1-abs(var0 *.03), .98)', channelIndex=1)
thigh_ScaleY_L = AddDriver('thigh_l', 'scale', 'thigh_l', 'ROT_Z', 'LOCAL_SPACE', 'max(1-abs(var0 *.03), .98)', channelIndex=1)
thigh_ScaleY_R = AddDriver('thigh_r', 'scale', 'thigh_r', 'ROT_Z', 'LOCAL_SPACE', 'max(1-abs(var0 *-.03), .98)', channelIndex=1)
### examples adding property drivers
############## PROPERTY DRIVERS ##################
#add driver to----------------- sideMoveHip_l
sideMoveHip_ScaleX_l = AddPropertyDriver('sideMoveHip_l', 'scale', 'PROPERTIES_SPHERE', '["Hip_Width"]', '(var*3) + 1.0', channelIndex=0)
sideMoveHip_ScaleX_r = AddPropertyDriver('sideMoveHip_r', 'scale', 'PROPERTIES_SPHERE', '["Hip_Width"]', '(var*3) + 1.0', channelIndex=0)
sideMoveHip_ScaleZ_l = AddPropertyDriver('sideMoveHip_l', 'scale', 'PROPERTIES_SPHERE', '["Hip_Width"]', '(var*3) + 1.0', channelIndex=2)
sideMoveHip_ScaleZ_r = AddPropertyDriver('sideMoveHip_r', 'scale', 'PROPERTIES_SPHERE', '["Hip_Width"]', '(var*3) + 1.0', channelIndex=2)
### examples adding constraint drivers
############## CONSTRAINT DRIVERS ##################
#add driver to----------------- pelvis_LIMLOC_DRIV
pelvis_LIMLOC_DRIV_package = 'pose.bones["pelvis"].constraints["pelvis_LIMLOC"].influence'
pelvis_LIMLOC_DRIV = AddPropertyDriver('pelvis', '', ['PROPERTIES_SPHERE', 'PROPERTIES_SPHERE'], ['["Hip_Width"]', '["Thigh_Size"]'],\
'-4.0 if var0>.05 or var1>.1 else -5', varCount=2, package=pelvis_LIMLOC_DRIV_package)
### Utility code for mirroring skin weights from left to right
#########################################
############## UTILITY ##################
#########################################
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects['body1'].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects['body1']
bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
for grp in bpy.context.object.vertex_groups:
if grp.name[-2:] == '_r':
other_side = bpy.context.object.vertex_groups[grp.name].index
bpy.context.object.vertex_groups.active_index = other_side
bpy.ops.object.vertex_group_remove(all=False, all_unlocked=False)
for grp in bpy.context.object.vertex_groups:
if grp.name[-2:] == '_l':
act = bpy.context.object.vertex_groups[grp.name].index
bpy.context.object.vertex_groups.active_index = act
bpy.ops.object.vertex_group_copy()
bpy.ops.object.vertex_group_mirror(use_topology=False)
if 'PHY_' in grp.name:
for i in range(1,12,2):
if grp.name[4:6] == f'0{i}':
new_name = f'PHY_0{i+1}' + grp.name[6:-2] + '_r'
elif grp.name[4:6] == f'{i}':
new_name = f'PHY_{i+1}' + grp.name[6:-2] + '_r'
else:
new_name = grp.name[:-2]+'_r'
bpy.context.object.vertex_groups[grp.name+'_copy'].name = new_name
else:
pass
## Hide all bones that are not controller bones used to animate
#########################################################################
###### add property driver to show/hide the non-controller bones ########
#########################################################################
con_list = ['spine_02', 'pelvis', 'ikHandleArm_r', 'ikHandleArm_l', 'poleVectorLeg_r', 'poleVectorLeg_l',\
'neck_02', 'spine_04', 'foot_CON_l', 'footRoll_CON_l', 'foot_CON_r', 'footRoll_CON_r',\
'poleVectorArm_l', 'poleVectorArm_r', 'hand_l', 'hand_r']
hidden_bones = {}
for bone in bpy.context.object.pose.bones:
if bone.name not in con_list:
drive = bpy.data.objects[rig].pose.bones[bone.name].bone.driver_add('hide')
drive.driver.type = 'SCRIPTED'
drive.driver.expression = 'var'
var = drive.driver.variables.new()
var.name = 'var'
var.type = 'SINGLE_PROP'
var.targets[0].id = bpy.data.objects['PROPERTIES_SPHERE']
var.targets[0].data_path = '["_INTERNAL_VISIBILITY"]'
hidden_bones[f"hide_{bone.name}"] = drive
## example adding Physics
#############################################
############## ADD PHYSICS ##################
#############################################
add_Physics('spine_02_PhyINTR_l', 'PHY_07')
add_Physics('spine_02_PhyINTR_r', 'PHY_08')
## adding physics drivers
#add driver to----------------- PHY_mesh_spine_02_PhyINTR_FRICTION_DRIV_l
PHY_mesh_spine_02_PhyINTR_FRICTION_DRIV_l = Add_PHY_PropertyDriver("PHY_mesh_spine_02_PhyINTR_l", 'Softbody',\
'friction', 'PROPERTIES_SPHERE', '["phyFRICTION_stomach"]')
#add driver to----------------- PHY_mesh_spine_02_PhyINTR_FRICTION_DRIV_r
PHY_mesh_spine_02_PhyINTR_FRICTION_DRIV_r = Add_PHY_PropertyDriver("PHY_mesh_spine_02_PhyINTR_r", 'Softbody',\
'friction', 'PROPERTIES_SPHERE', '["phyFRICTION_stomach"]')
#add driver to----------------- PHY_mesh_spine_02_PhyINTR_MASS_DRIV_l
PHY_mesh_spine_02_PhyINTR_MASS_DRIV_l = Add_PHY_PropertyDriver("PHY_mesh_spine_02_PhyINTR_l", 'Softbody',\
'mass', 'PROPERTIES_SPHERE', '["phyMASS_stomach"]')
#add driver to----------------- PHY_mesh_spine_02_PhyINTR_MASS_DRIV_r
PHY_mesh_spine_02_PhyINTR_MASS_DRIV_r = Add_PHY_PropertyDriver("PHY_mesh_spine_02_PhyINTR_r", 'Softbody',\
'mass', 'PROPERTIES_SPHERE', '["phyMASS_stomach"]')
## Final clean up stuff
#####################################################################################
###### change Rest Pose, add Armature Modifiers, Bone Parent other meshes ###########
#####################################################################################
## set rest pose for major driving bones
bpy.ops.object.posemode_toggle()
restlist = ['thigh_l', 'thigh_r', 'calf_l', 'calf_r']
for bone in bpy.context.object.pose.bones:
bone.bone.select = False
for res in restlist:
bpy.context.object.pose.bones[res].bone.select = True
bpy.ops.pose.armature_apply(selected=True)
## reset rest length of all stretch to constraints
for bone in bpy.data.objects[rig].pose.bones:
for const in bone.constraints:
if const.type == 'STRETCH_TO':
bpy.context.object.data.bones.active = bone.bone
bpy.ops.constraint.stretchto_reset(constraint=const.name, owner='BONE')
## apply armature modifier to character mesh objects
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
meshlist = bpy.data.collections[character].all_objects
for mes in meshlist:
mes.modifiers['Armature'].object = bpy.data.objects[rig]
## bone parent the characters eyes and the property sphere to the head bone
for o in bpy.data.objects:
if 'Auto_Eye' in o.name or 'PROPERTIES_SPHERE' in o.name:
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[rig].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects[rig]
bpy.ops.object.mode_set(mode='POSE')
bpy.context.object.pose.bones['head'].bone.select = True
bpy.context.object.data.bones.active = bpy.context.object.pose.bones['head'].bone
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects[o.name].select_set(True)
bpy.data.objects[rig].select_set(True)
bpy.ops.object.parent_set(type='BONE', keep_transform=True)
bpy.data.objects['PROPERTIES_SPHERE'].select_set(False)
#######################################################################
#######################################################################
#######################################################################
0>