import bpy from bpy.types import Constraint, PoseBone rig = bpy.data.objects["infantry"] BGRP_SPECIAL = 2 BGRP_TWEAK = 3 BGRP_EXTRA = 4 ## FIX PARENTS def reparent(b: str, newParent: str = 'DEF-spine'): rig.data.edit_bones[b].parent = rig.data.edit_bones[newParent] def fix(b: str): p = rig.data.edit_bones[b].parent.name.replace('ORG','DEF') reparent(b, p) # Switch to EDIT mode to modify bone hiearchy to be proper for Unreal bpy.ops.object.mode_set(mode='EDIT') # List of bones that need to be reparented to their deforming counterparts fixList = [ # Hands 'DEF-thumb01.L', 'DEF-index01.L', 'DEF-middle01.L', 'DEF-ring01.L', 'DEF-pinky01.L', # Arms 'shoulder.L', 'breast.L', # Legs 'DEF-thigh.L', ] # Iterate through and fix each bone for item in fixList: fix(item) if item.endswith('.L'): # If it's left-sided, fix right-side counterparts too fix(item.replace('.L','.R')) # Pelvis parents work a little differently reparent('DEF-pelvis.L', 'DEF-spine') reparent('DEF-pelvis.R', 'DEF-spine') # Deform parents on palms are actually parented to originals, so skip that layer reparent('DEF-palm.01.L', 'DEF-hand.L') reparent('DEF-palm.02.L', 'DEF-hand.L') reparent('DEF-palm.03.L', 'DEF-hand.L') reparent('DEF-palm.04.L', 'DEF-hand.L') reparent('DEF-palm.01.R', 'DEF-hand.R') reparent('DEF-palm.02.R', 'DEF-hand.R') reparent('DEF-palm.03.R', 'DEF-hand.R') reparent('DEF-palm.04.R', 'DEF-hand.R') ## Force axe to be exported by setting it as a deform bone #rig.data.edit_bones['axe'].use_deform = True ## Switch into POSE mode to set up animation constraints bpy.ops.object.mode_set(mode='POSE') ### Declare Functions for animation constraints def arrcopy(copyTo, copyFrom): for i in range(min(len(copyTo), len(copyFrom))): copyTo[i] = copyFrom[i] def drv_blend_1D(shape_target: str, key_target: str, bone_name: str, max_x: float, prop: str = 'LOC_Y', formula: str = ''): block = bpy.data.shape_keys[shape_target].key_blocks[key_target] bmin = block.slider_min bmax = block.slider_max block.driver_remove('value') fcurve = block.driver_add('value') d = fcurve.driver d.type = 'SCRIPTED' if formula == '': d.expression = str(bmax / max_x) + '*x' else: d.expression = formula v = d.variables.new() v.name = 'x' v.type = 'TRANSFORMS' t = v.targets[0] t.id = rig t.bone_target = bone_name t.transform_type = prop t.transform_space = 'LOCAL_SPACE' def drv_constraint_1D(c: Constraint, bone_name: str, max_x: float, prop: str = 'LOC_Y', formula: str = ''): bmin = 0.0 bmax = 1.0 c.driver_remove('influence') fcurve = c.driver_add('influence') d = fcurve.driver d.type = 'SCRIPTED' if formula == '': d.expression = str(bmax / max_x) + '*x' else: d.expression = formula v = d.variables.new() v.name = 'x' v.type = 'TRANSFORMS' t = v.targets[0] t.id = rig t.bone_target = bone_name t.transform_type = prop t.transform_space = 'LOCAL_SPACE' def drv_toggle_1D(a: PoseBone, b: PoseBone, slider: PoseBone): pass def constrain_slider(b: PoseBone, minimum: float = 0, maximum: float = 0.06): c = b.constraints.new('LIMIT_LOCATION') c.use_min_y = True c.use_max_y = True c.min_y = minimum c.max_y = maximum c.use_transform_limit = True c.owner_space = 'LOCAL' def cpy_transforms(b: PoseBone, subtarget, influence = 1.0): constraint = b.constraints.new('COPY_TRANSFORMS') constraint.target = rig constraint.subtarget = subtarget # note subtarget uses name not object constraint.target_space = 'POSE' constraint.owner_space = 'POSE' constraint.influence = influence return constraint ## Fix widget scalings, and constrain them for b in rig.pose.bones: ## MENUS / WIDGETS if b.name.startswith("menu_"): b.use_custom_shape_bone_size = False b.custom_shape_translation = (0.0, 0.03, 0.0) b.custom_shape_scale_xyz = (0.06, 0.06, 0.0) arrcopy(b.lock_scale, [False, True, False]) b.bone_group_index = BGRP_EXTRA elif b.name.startswith("slider1d"): b.use_custom_shape_bone_size = False b.custom_shape_scale_xyz = (0.02, 0.003, 0.005) arrcopy(b.lock_location, [True, False, True]) arrcopy(b.lock_rotation, [True, True, True]) arrcopy(b.lock_scale, [True, True, True]) b.lock_rotation_w = True b.bone_group_index = BGRP_SPECIAL ## WEAPONS elif b.name.endswith("_slide"): b.bone_group_index = BGRP_TWEAK b.custom_shape_scale_xyz = (2.0, 2.0, 2.0) arrcopy(b.lock_location, [True, False, True]) arrcopy(b.lock_scale, [True, True, True]) arrcopy(b.lock_rotation, [False, False, True]) ### BATON elif b.name.startswith("grip_baton"): b.custom_shape_scale_xyz = (0.2, 2.6, 0.2) b.custom_shape_translation = (0.0, 0.25, 0.0) if b.name.endswith("_constrained"): b.custom_shape_scale_xyz *= 0.9 b.bone_group_index = BGRP_EXTRA elif b.name == "baton_handR_grip": b.custom_shape_scale_xyz = (0.2, 1.0, 0.8) ### GUN elif b.name.startswith("grip_gun"): b.custom_shape_scale_xyz = (0.1, 1.5, 0.2) b.custom_shape_translation = (0.0, 0.185, 0.0) elif b.name == "gun_ammunition": b.custom_shape_scale_xyz = (0.4, 1.0, 0.4) b.custom_shape_translation = (0.0, 0.06, 0.0) elif b.name == "gun_trigger": b.custom_shape_scale_xyz = (0.3, 1.0, 0.7) b.custom_shape_translation = (0.0, 0.015, 0.0) arrcopy(b.lock_rotation, [False, True, True]) arrcopy(b.lock_location, [True, True, True]) arrcopy(b.lock_scale, [True, True, True]) elif b.name == "gun_muzzle": b.custom_shape_scale_xyz = (0.1, 1.1, 0.2) b.custom_shape_translation = (0.0, 0.14, 0.0) arrcopy(b.lock_location, [True, False, True]) arrcopy(b.lock_rotation, [True, True, True]) arrcopy(b.lock_scale, [True, False, True]) b.lock_rotation_w = True ## BODY elif b.name == "neck.001": arrcopy(b.lock_location, [True, True, True]) # Constrain slider widgets constrain_slider(rig.pose.bones.get('slider1d_baton_constrain'), 0.0, 0.06) constrain_slider(rig.pose.bones.get('slider1d_baton_handIK'), 0.0, 0.06) constrain_slider(rig.pose.bones.get('slider1d_gun_handRIK'), 0.0, 0.06) constrain_slider(rig.pose.bones.get('slider1d_gun_handLIK'), 0.0, 0.06) ## Switch Baton Grip's bound parent, default parent to arm, but can switch to separate control baton_grip = rig.pose.bones.get("grip_baton") cpy_transforms(baton_grip, 'grip_baton_constrained', 1.0) # Default to hand grip bg_constraint = cpy_transforms(baton_grip, 'grip_baton_free', 0.0) drv_constraint_1D(bg_constraint, 'slider1d_baton_constrain', 0.06) ## Align right hand to baton grip rightHand = rig.pose.bones.get("MCH-upper_arm_ik_target.R") bh_constraint = cpy_transforms(rightHand, 'baton_handR_grip', 0.0) # Default to hand grip drv_constraint_1D(bh_constraint, 'slider1d_baton_handIK', 0.06) # Gun gun_grip = rig.pose.bones.get("grip_gun") cpy_transforms(gun_grip, 'grip_gun_free', 1.0) # Inherit transforms, but be tree-agnostic for IK ## Align hands to gun grip ghr_constraint = cpy_transforms(rightHand, 'gun_handR_grip', 0.0) drv_constraint_1D(ghr_constraint, 'slider1d_gun_handRIK', 0.06) leftHand = rig.pose.bones.get("MCH-upper_arm_ik_target.L") ghl_constraint = cpy_transforms(leftHand, 'gun_handL_grip', 0.0) drv_constraint_1D(ghl_constraint, 'slider1d_gun_handLIK', 0.06) ### Set up smear effect drivers for axe #drv_blend_1D('Key', 'smear_down', 'slider1d_axe_smear', 0.03) #drv_blend_1D('Key', 'smear_up', 'slider1d_axe_smear', -0.03)