2024-02-07 13:20:07 -05:00
|
|
|
import bpy
|
|
|
|
from bpy.types import Constraint, PoseBone
|
|
|
|
|
2024-02-19 13:20:17 -05:00
|
|
|
rig = bpy.data.objects["infantry"]
|
2024-02-20 22:19:11 -05:00
|
|
|
BGRP_SPECIAL = 2
|
|
|
|
BGRP_TWEAK = 3
|
|
|
|
BGRP_EXTRA = 4
|
2024-02-07 13:20:07 -05:00
|
|
|
|
|
|
|
## 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')
|
|
|
|
|
2024-02-19 13:20:17 -05:00
|
|
|
## Force axe to be exported by setting it as a deform bone
|
|
|
|
#rig.data.edit_bones['axe'].use_deform = True
|
2024-02-07 13:20:07 -05:00
|
|
|
|
2024-02-19 13:20:17 -05:00
|
|
|
## Switch into POSE mode to set up animation constraints
|
2024-02-07 13:20:07 -05:00
|
|
|
bpy.ops.object.mode_set(mode='POSE')
|
|
|
|
|
2024-02-19 13:20:17 -05:00
|
|
|
### Declare Functions for animation constraints
|
2024-02-07 13:20:07 -05:00
|
|
|
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'
|
2024-02-20 22:19:11 -05:00
|
|
|
def drv_toggle_1D(a: PoseBone, b: PoseBone, slider: PoseBone):
|
2024-02-20 19:21:41 -05:00
|
|
|
pass
|
2024-02-07 13:20:07 -05:00
|
|
|
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'
|
2024-02-19 13:20:17 -05:00
|
|
|
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
|
2024-02-20 19:21:41 -05:00
|
|
|
return constraint
|
2024-02-07 13:20:07 -05:00
|
|
|
|
2024-02-19 13:20:17 -05:00
|
|
|
## Fix widget scalings, and constrain them
|
2024-02-07 13:20:07 -05:00
|
|
|
for b in rig.pose.bones:
|
2024-02-20 19:21:41 -05:00
|
|
|
## MENUS / WIDGETS
|
2024-02-07 13:20:07 -05:00
|
|
|
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])
|
2024-02-20 22:19:11 -05:00
|
|
|
b.bone_group_index = BGRP_EXTRA
|
2024-02-07 13:20:07 -05:00
|
|
|
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
|
2024-02-20 22:19:11 -05:00
|
|
|
b.bone_group_index = BGRP_SPECIAL
|
2024-02-20 19:21:41 -05:00
|
|
|
|
|
|
|
## WEAPONS
|
2024-02-20 22:19:11 -05:00
|
|
|
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
|
2024-02-20 19:21:41 -05:00
|
|
|
elif b.name.startswith("grip_baton"):
|
2024-02-19 13:20:17 -05:00
|
|
|
b.custom_shape_scale_xyz = (0.2, 2.6, 0.2)
|
|
|
|
b.custom_shape_translation = (0.0, 0.25, 0.0)
|
2024-02-20 22:19:11 -05:00
|
|
|
if b.name.endswith("_constrained"):
|
2024-02-20 19:21:41 -05:00
|
|
|
b.custom_shape_scale_xyz *= 0.9
|
2024-02-20 22:19:11 -05:00
|
|
|
b.bone_group_index = BGRP_EXTRA
|
|
|
|
elif b.name == "baton_handR_grip":
|
|
|
|
b.custom_shape_scale_xyz = (0.2, 1.0, 0.8)
|
2024-02-21 13:18:18 -05:00
|
|
|
|
|
|
|
### 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
|
2024-02-20 19:21:41 -05:00
|
|
|
|
|
|
|
## BODY
|
2024-02-19 13:20:17 -05:00
|
|
|
elif b.name == "neck.001":
|
|
|
|
arrcopy(b.lock_location, [True, True, True])
|
2024-02-07 13:20:07 -05:00
|
|
|
|
|
|
|
|
2024-02-20 22:19:11 -05:00
|
|
|
# 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)
|
2024-02-21 13:18:18 -05:00
|
|
|
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)
|
2024-02-07 13:20:07 -05:00
|
|
|
|
2024-02-21 13:18:18 -05:00
|
|
|
## Switch Baton Grip's bound parent, default parent to arm, but can switch to separate control
|
2024-02-19 13:20:17 -05:00
|
|
|
baton_grip = rig.pose.bones.get("grip_baton")
|
2024-02-20 22:19:11 -05:00
|
|
|
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)
|
|
|
|
|
2024-02-21 13:18:18 -05:00
|
|
|
## Align right hand to baton grip
|
2024-02-20 22:19:11 -05:00
|
|
|
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)
|
2024-02-19 13:20:17 -05:00
|
|
|
|
2024-02-21 13:18:18 -05:00
|
|
|
# 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)
|
|
|
|
|
2024-02-19 13:20:17 -05:00
|
|
|
### 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)
|