diff --git a/config/userpref.blend b/config/userpref.blend index b719e02..61641a2 100644 --- a/config/userpref.blend +++ b/config/userpref.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c52c4b86cbfb74020d6a19fded4ae035e8f180453790ddfc1ec1d5cb404eb985 -size 281014 +oid sha256:bbf8c7bb53478cabe089fd7b00ce4a63117ea464b1eb089cfe012e71b367aacc +size 280494 diff --git a/scripts/addons/cc_blender_tools-2_4_0/.gitignore b/scripts/addons/cc_blender_tools-2_4_2/.gitignore similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/.gitignore rename to scripts/addons/cc_blender_tools-2_4_2/.gitignore diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Arms.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Arms.json new file mode 100644 index 0000000..5717b8e --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Arms.json @@ -0,0 +1,173 @@ +{ + "Bones": { + "CC_Base_L_Clavicle": 1.0, + "CC_Base_L_Upperarm": 1.0, + "CC_Base_L_Forearm": 1.0, + "CC_Base_L_ForearmTwist01": 1.0, + "CC_Base_L_ForearmTwist02": 1.0, + "CC_Base_L_ElbowShareBone": 1.0, + "CC_Base_L_Hand": 1.0, + "CC_Base_L_Pinky1": 1.0, + "CC_Base_L_Pinky2": 1.0, + "CC_Base_L_Pinky3": 1.0, + "CC_Base_L_Ring1": 1.0, + "CC_Base_L_Ring2": 1.0, + "CC_Base_L_Ring3": 1.0, + "CC_Base_L_Mid1": 1.0, + "CC_Base_L_Mid2": 1.0, + "CC_Base_L_Mid3": 1.0, + "CC_Base_L_Index1": 1.0, + "CC_Base_L_Index2": 1.0, + "CC_Base_L_Index3": 1.0, + "CC_Base_L_Thumb1": 1.0, + "CC_Base_L_Thumb2": 1.0, + "CC_Base_L_Thumb3": 1.0, + "CC_Base_L_UpperarmTwist01": 1.0, + "CC_Base_L_UpperarmTwist02": 1.0, + "CC_Base_R_Clavicle": 1.0, + "CC_Base_R_Upperarm": 1.0, + "CC_Base_R_Forearm": 1.0, + "CC_Base_R_ElbowShareBone": 1.0, + "CC_Base_R_ForearmTwist01": 1.0, + "CC_Base_R_ForearmTwist02": 1.0, + "CC_Base_R_Hand": 1.0, + "CC_Base_R_Ring1": 1.0, + "CC_Base_R_Ring2": 1.0, + "CC_Base_R_Ring3": 1.0, + "CC_Base_R_Mid1": 1.0, + "CC_Base_R_Mid2": 1.0, + "CC_Base_R_Mid3": 1.0, + "CC_Base_R_Thumb1": 1.0, + "CC_Base_R_Thumb2": 1.0, + "CC_Base_R_Thumb3": 1.0, + "CC_Base_R_Index1": 1.0, + "CC_Base_R_Index2": 1.0, + "CC_Base_R_Index3": 1.0, + "CC_Base_R_Pinky1": 1.0, + "CC_Base_R_Pinky2": 1.0, + "CC_Base_R_Pinky3": 1.0, + "CC_Base_R_UpperarmTwist01": 1.0, + "CC_Base_R_UpperarmTwist02": 1.0, + "hand_tweak.L": 1.0, + "forearm_tweak.L": 1.0, + "forearm_tweak.L.001": 1.0, + "upper_arm_tweak.L.001": 1.0, + "upper_arm_parent.L": 1.0, + "upper_arm_fk.L": 1.0, + "forearm_fk.L": 1.0, + "hand_fk.L": 1.0, + "upper_arm_ik.L": 1.0, + "upper_arm_tweak.L": 1.0, + "f_index.01_master.L": 1.0, + "thumb.01_master.L": 1.0, + "f_index.01.L": 1.0, + "f_index.02.L": 1.0, + "f_index.03.L": 1.0, + "f_index.01.L.001": 1.0, + "thumb.01.L": 1.0, + "thumb.02.L": 1.0, + "thumb.03.L": 1.0, + "thumb.01.L.001": 1.0, + "f_middle.01_master.L": 1.0, + "f_middle.01.L": 1.0, + "f_middle.02.L": 1.0, + "f_middle.03.L": 1.0, + "f_middle.01.L.001": 1.0, + "f_ring.01_master.L": 1.0, + "f_ring.01.L": 1.0, + "f_ring.02.L": 1.0, + "f_ring.03.L": 1.0, + "f_ring.01.L.001": 1.0, + "f_pinky.01_master.L": 1.0, + "f_pinky.01.L": 1.0, + "f_pinky.02.L": 1.0, + "f_pinky.03.L": 1.0, + "f_pinky.01.L.001": 1.0, + "palm.L": 1.0, + "hand_tweak.R": 1.0, + "forearm_tweak.R": 1.0, + "forearm_tweak.R.001": 1.0, + "upper_arm_tweak.R.001": 1.0, + "upper_arm_parent.R": 1.0, + "upper_arm_fk.R": 1.0, + "forearm_fk.R": 1.0, + "hand_fk.R": 1.0, + "upper_arm_ik.R": 1.0, + "upper_arm_tweak.R": 1.0, + "f_index.01_master.R": 1.0, + "thumb.01_master.R": 1.0, + "f_index.01.R": 1.0, + "f_index.02.R": 1.0, + "f_index.03.R": 1.0, + "f_index.01.R.001": 1.0, + "thumb.01.R": 1.0, + "thumb.02.R": 1.0, + "thumb.03.R": 1.0, + "thumb.01.R.001": 1.0, + "f_middle.01_master.R": 1.0, + "f_middle.01.R": 1.0, + "f_middle.02.R": 1.0, + "f_middle.03.R": 1.0, + "f_middle.01.R.001": 1.0, + "f_ring.01_master.R": 1.0, + "f_ring.01.R": 1.0, + "f_ring.02.R": 1.0, + "f_ring.03.R": 1.0, + "f_ring.01.R.001": 1.0, + "f_pinky.01_master.R": 1.0, + "f_pinky.01.R": 1.0, + "f_pinky.02.R": 1.0, + "f_pinky.03.R": 1.0, + "f_pinky.01.R.001": 1.0, + "palm.R": 1.0, + "shoulder.L": 1.0, + "shoulder.R": 1.0, + "hand_ik.L": 1.0, + "upper_arm_ik_target.L": 1.0, + "hand_ik.R": 1.0, + "upper_arm_ik_target.R": 1.0, + "clavicle_l": 1.0, + "upperarm_l": 1.0, + "upperarm_twist_01_l": 1.0, + "lowerarm_l": 1.0, + "lowerarm_twist_01_l": 1.0, + "hand_l": 1.0, + "middle_01_l": 1.0, + "middle_02_l": 1.0, + "middle_03_l": 1.0, + "index_01_l": 1.0, + "index_02_l": 1.0, + "index_03_l": 1.0, + "ring_01_l": 1.0, + "ring_02_l": 1.0, + "ring_03_l": 1.0, + "pinky_01_l": 1.0, + "pinky_02_l": 1.0, + "pinky_03_l": 1.0, + "thumb_01_l": 1.0, + "thumb_02_l": 1.0, + "thumb_03_l": 1.0, + "clavicle_r": 1.0, + "upperarm_r": 1.0, + "upperarm_twist_01_r": 1.0, + "lowerarm_r": 1.0, + "lowerarm_twist_01_r": 1.0, + "hand_r": 1.0, + "middle_01_r": 1.0, + "middle_02_r": 1.0, + "middle_03_r": 1.0, + "ring_01_r": 1.0, + "ring_02_r": 1.0, + "ring_03_r": 1.0, + "thumb_01_r": 1.0, + "thumb_02_r": 1.0, + "thumb_03_r": 1.0, + "index_01_r": 1.0, + "index_02_r": 1.0, + "index_03_r": 1.0, + "pinky_01_r": 1.0, + "pinky_02_r": 1.0, + "pinky_03_r": 1.0 + }, + "Keys": {} +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Facial Expressions.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Facial Expressions.json new file mode 100644 index 0000000..7d1d634 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Facial Expressions.json @@ -0,0 +1,840 @@ +{ + "Bones": { + "CTRL_L_brow_down": 1.0, + "CTRL_R_brow_down": 1.0, + "CTRL_L_brow_lateral": 1.0, + "CTRL_R_brow_lateral": 1.0, + "CTRL_L_brow_raiseIn": 1.0, + "CTRL_R_brow_raiseIn": 1.0, + "CTRL_L_brow_raiseOut": 1.0, + "CTRL_R_brow_raiseOut": 1.0, + "CTRL_L_eye_blink": 1.0, + "CTRL_R_eye_blink": 1.0, + "CTRL_L_eye_cheekRaise": 1.0, + "CTRL_R_eye_cheekRaise": 1.0, + "CTRL_L_eye_lidPress": 1.0, + "CTRL_R_eye_lidPress": 1.0, + "CTRL_L_eye_squintInner": 1.0, + "CTRL_R_eye_squintInner": 1.0, + "CTRL_C_eye": 1.0, + "CTRL_C_eye_parallelLook": 1.0, + "CTRL_L_eye": 1.0, + "CTRL_R_eye": 1.0, + "CTRL_L_eye_pupil": 1.0, + "CTRL_R_eye_pupil": 1.0, + "CTRL_L_ear_up": 1.0, + "CTRL_R_ear_up": 1.0, + "CTRL_L_nose": 1.0, + "CTRL_R_nose": 1.0, + "CTRL_L_nose_wrinkleUpper": 1.0, + "CTRL_R_nose_wrinkleUpper": 1.0, + "CTRL_L_mouth_suckBlow": 1.0, + "CTRL_R_mouth_suckBlow": 1.0, + "CTRL_C_mouth": 1.0, + "CTRL_L_mouth_cornerDepress": 1.0, + "CTRL_R_mouth_cornerDepress": 1.0, + "CTRL_L_mouth_cornerPull": 1.0, + "CTRL_R_mouth_cornerPull": 1.0, + "CTRL_L_mouth_dimple": 1.0, + "CTRL_R_mouth_dimple": 1.0, + "CTRL_L_mouth_lowerLipDepress": 1.0, + "CTRL_R_mouth_lowerLipDepress": 1.0, + "CTRL_L_mouth_sharpCornerPull": 1.0, + "CTRL_R_mouth_sharpCornerPull": 1.0, + "CTRL_L_mouth_stretch": 1.0, + "CTRL_R_mouth_stretch": 1.0, + "CTRL_L_mouth_stretchLipsClose": 1.0, + "CTRL_R_mouth_stretchLipsClose": 1.0, + "CTRL_L_mouth_upperLipRaise": 1.0, + "CTRL_R_mouth_upperLipRaise": 1.0, + "CTRL_L_mouth_funnelD": 1.0, + "CTRL_R_mouth_funnelD": 1.0, + "CTRL_L_mouth_funnelU": 1.0, + "CTRL_R_mouth_funnelU": 1.0, + "CTRL_L_mouth_lipBiteD": 1.0, + "CTRL_R_mouth_lipBiteD": 1.0, + "CTRL_L_mouth_lipBiteU": 1.0, + "CTRL_R_mouth_lipBiteU": 1.0, + "CTRL_L_mouth_lipsBlow": 1.0, + "CTRL_R_mouth_lipsBlow": 1.0, + "CTRL_L_mouth_lipsPressU": 1.0, + "CTRL_R_mouth_lipsPressU": 1.0, + "CTRL_L_mouth_pressD": 1.0, + "CTRL_R_mouth_pressD": 1.0, + "CTRL_L_mouth_pressU": 1.0, + "CTRL_R_mouth_pressU": 1.0, + "CTRL_L_mouth_purseD": 1.0, + "CTRL_R_mouth_purseD": 1.0, + "CTRL_L_mouth_purseU": 1.0, + "CTRL_R_mouth_purseU": 1.0, + "CTRL_L_mouth_tightenD": 1.0, + "CTRL_R_mouth_tightenD": 1.0, + "CTRL_L_mouth_tightenU": 1.0, + "CTRL_R_mouth_tightenU": 1.0, + "CTRL_L_mouth_towardsD": 1.0, + "CTRL_R_mouth_towardsD": 1.0, + "CTRL_L_mouth_towardsU": 1.0, + "CTRL_R_mouth_towardsU": 1.0, + "CTRL_L_mouth_lipsTogetherD": 1.0, + "CTRL_R_mouth_lipsTogetherD": 1.0, + "CTRL_L_mouth_lipsTogetherU": 1.0, + "CTRL_R_mouth_lipsTogetherU": 1.0, + "CTRL_C_mouth_stickyD": 1.0, + "CTRL_C_mouth_stickyU": 1.0, + "CTRL_L_mouth_lipSticky": 1.0, + "CTRL_R_mouth_lipSticky": 1.0, + "CTRL_L_mouth_stickyInnerD": 1.0, + "CTRL_R_mouth_stickyInnerD": 1.0, + "CTRL_L_mouth_stickyInnerU": 1.0, + "CTRL_R_mouth_stickyInnerU": 1.0, + "CTRL_L_mouth_stickyOuterD": 1.0, + "CTRL_R_mouth_stickyOuterD": 1.0, + "CTRL_L_mouth_stickyOuterU": 1.0, + "CTRL_R_mouth_stickyOuterU": 1.0, + "CTRL_C_jaw": 1.0, + "CTRL_C_jaw_fwdBack": 1.0, + "CTRL_C_jaw_openExtreme": 1.0, + "CTRL_L_jaw_ChinRaiseD": 1.0, + "CTRL_R_jaw_ChinRaiseD": 1.0, + "CTRL_L_jaw_ChinRaiseU": 1.0, + "CTRL_R_jaw_ChinRaiseU": 1.0, + "CTRL_L_jaw_chinCompress": 1.0, + "CTRL_R_jaw_chinCompress": 1.0, + "CTRL_L_jaw_clench": 1.0, + "CTRL_R_jaw_clench": 1.0, + "CTRL_C_neck_swallow": 1.0, + "CTRL_L_neck_mastoidContract": 1.0, + "CTRL_R_neck_mastoidContract": 1.0, + "CTRL_L_neck_stretch": 1.0, + "CTRL_R_neck_stretch": 1.0, + "CTRL_neck_digastricUpDown": 1.0, + "CTRL_neck_throatExhaleInhale": 1.0, + "CTRL_neck_throatUpDown": 1.0, + "CTRL_C_head_turn": 1.0, + "CTRL_C_head_move": 1.0, + "CTRL_C_head_tilt": 1.0, + "CTRL_C_head_fwdBack": 1.0, + "CTRL_L_eye_eyelidD": 1.0, + "CTRL_R_eye_eyelidD": 1.0, + "CTRL_L_eye_eyelidU": 1.0, + "CTRL_R_eye_eyelidU": 1.0, + "CTRL_L_eye_faceScrunch": 1.0, + "CTRL_R_eye_faceScrunch": 1.0, + "CTRL_L_eyelashes_tweakerIn": 1.0, + "CTRL_R_eyelashes_tweakerIn": 1.0, + "CTRL_L_eyelashes_tweakerOut": 1.0, + "CTRL_R_eyelashes_tweakerOut": 1.0, + "CTRL_L_nose_nasolabialDeepen": 1.0, + "CTRL_R_nose_nasolabialDeepen": 1.0, + "CTRL_L_mouth_pushPullD": 1.0, + "CTRL_R_mouth_pushPullD": 1.0, + "CTRL_L_mouth_pushPullU": 1.0, + "CTRL_R_mouth_pushPullU": 1.0, + "CTRL_C_mouth_lipShiftD": 1.0, + "CTRL_C_mouth_lipShiftU": 1.0, + "CTRL_L_mouth_lipsRollD": 1.0, + "CTRL_R_mouth_lipsRollD": 1.0, + "CTRL_L_mouth_lipsRollU": 1.0, + "CTRL_R_mouth_lipsRollU": 1.0, + "CTRL_L_mouth_corner": 1.0, + "CTRL_R_mouth_corner": 1.0, + "CTRL_L_mouth_cornerSharpnessD": 1.0, + "CTRL_R_mouth_cornerSharpnessD": 1.0, + "CTRL_L_mouth_cornerSharpnessU": 1.0, + "CTRL_R_mouth_cornerSharpnessU": 1.0, + "CTRL_L_mouth_lipsTowardsTeethD": 1.0, + "CTRL_R_mouth_lipsTowardsTeethD": 1.0, + "CTRL_L_mouth_lipsTowardsTeethU": 1.0, + "CTRL_R_mouth_lipsTowardsTeethU": 1.0, + "CTRL_L_mouth_thicknessD": 1.0, + "CTRL_R_mouth_thicknessD": 1.0, + "CTRL_L_mouth_thicknessInwardD": 1.0, + "CTRL_R_mouth_thicknessInwardD": 1.0, + "CTRL_L_mouth_thicknessInwardU": 1.0, + "CTRL_R_mouth_thicknessInwardU": 1.0, + "CTRL_L_mouth_thicknessU": 1.0, + "CTRL_R_mouth_thicknessU": 1.0, + "CTRL_C_tongue_bendTwist": 1.0, + "CTRL_C_tongue_inOut": 1.0, + "CTRL_C_tongue_move": 1.0, + "CTRL_C_tongue_press": 1.0, + "CTRL_C_tongue_roll": 1.0, + "CTRL_C_tongue_thickThin": 1.0, + "CTRL_C_tongue_tipMove": 1.0, + "CTRL_C_tongue_wideNarrow": 1.0, + "CTRL_C_teethD": 1.0, + "CTRL_C_teethU": 1.0, + "CTRL_C_teeth_fwdBackD": 1.0, + "CTRL_C_teeth_fwdBackU": 1.0, + "CTRL_eye_pupil": 1.0, + "CTRL_R_mouth_tighten": 1.0, + "CTRL_L_mouth_tighten": 1.0, + "CTRL_C_jaw_chinRaise": 1.0, + "CTRL_C_mouth_lipsTogether": 1.0, + "CTRL_R_mouth_press": 1.0, + "CTRL_L_mouth_press": 1.0, + "CTRL_C_tongue": 1.0, + "CTRL_C_tongue_tip": 1.0, + "CTRL_mouth_shrugDropU": 1.0, + "CTRL_mouth_shrugDropD": 1.0, + "CTRL_C_tongue_narrowWide": 1.0, + "CTRL_R_eyelashU": 1.0, + "CTRL_R_eyelashD": 1.0, + "CTRL_L_eyelashU": 1.0, + "CTRL_L_eyelashD": 1.0, + "CTRL_C_mouth_thickness": 1.0, + "CTRL_brow_raiseIn": 1.0, + "CTRL_mouth_purse_Std": 1.0, + "CTRL_mouth_funnel_Std": 1.0, + "CTRL_mouth_lipBiteU_Std": 1.0, + "CTRL_mouth_lipBiteD_Std": 1.0, + "CTRL_C_jaw_fwdBack_Std": 1.0, + "CTRL_mouth_puff_Std": 1.0, + "CTRL_C_tongue_Out_Std": 1.0, + "CTRL_C_tongue_tip_upDown_Std": 1.0, + "CTRL_C_tongue_roll_Std": 1.0, + "CTRL_mouth_shrugU_Std": 1.0, + "CTRL_mouth_shrugD_Std": 1.0, + "CTRL_mouth_lipsRollU_Std": 1.0, + "CTRL_mouth_lipsRollD_Std": 1.0, + "CTRL_R_mouth_corner_Std": 1.0, + "CTRL_L_mouth_corner_Std": 1.0 + }, + "Keys": { + "Brow_Compress_L": 1.0, + "Brow_Compress_R": 1.0, + "Brow_Drop_L": 1.0, + "Brow_Drop_R": 1.0, + "Brow_Raise_Inner_L": 1.0, + "Brow_Raise_Inner_R": 1.0, + "Brow_Raise_Outer_L": 1.0, + "Brow_Raise_Outer_R": 1.0, + "Cheek_Puff_L": 1.0, + "Cheek_Puff_R": 1.0, + "Cheek_Raise_L": 1.0, + "Cheek_Raise_R": 1.0, + "Cheek_Suck_L": 1.0, + "Cheek_Suck_R": 1.0, + "Ear_Down_L": 1.0, + "Ear_Down_R": 1.0, + "Ear_Out_L": 1.0, + "Ear_Out_R": 1.0, + "Ear_Up_L": 1.0, + "Ear_Up_R": 1.0, + "Eye_Blink_L": 1.0, + "Eye_Blink_R": 1.0, + "Eye_L_Look_Down": 1.0, + "Eye_L_Look_L": 1.0, + "Eye_L_Look_R": 1.0, + "Eye_L_Look_Up": 1.0, + "Eye_Pupil_Contract": 1.0, + "Eye_Pupil_Dilate": 1.0, + "Eye_R_Look_Down": 1.0, + "Eye_R_Look_L": 1.0, + "Eye_R_Look_R": 1.0, + "Eye_R_Look_Up": 1.0, + "Eye_Squint_L": 1.0, + "Eye_Squint_R": 1.0, + "Eye_Wide_L": 1.0, + "Eye_Wide_R": 1.0, + "Eyelash_Lower_Down_L": 1.0, + "Eyelash_Lower_Down_R": 1.0, + "Eyelash_Lower_Up_L": 1.0, + "Eyelash_Lower_Up_R": 1.0, + "Eyelash_Upper_Down_L": 1.0, + "Eyelash_Upper_Down_R": 1.0, + "Eyelash_Upper_Up_L": 1.0, + "Eyelash_Upper_Up_R": 1.0, + "Head_Backward": 1.0, + "Head_Forward": 1.0, + "Head_L": 1.0, + "Head_R": 1.0, + "Head_Tilt_L": 1.0, + "Head_Tilt_R": 1.0, + "Head_Turn_Down": 1.0, + "Head_Turn_L": 1.0, + "Head_Turn_R": 1.0, + "Head_Turn_Up": 1.0, + "Jaw_Backward": 1.0, + "Jaw_Down": 1.0, + "Jaw_Forward": 1.0, + "Jaw_L": 1.0, + "Jaw_Open": 1.0, + "Jaw_R": 1.0, + "Jaw_Up": 1.0, + "Mouth_Blow_L": 1.0, + "Mouth_Blow_R": 1.0, + "Mouth_Chin_Up": 1.0, + "Mouth_Close": 1.0, + "Mouth_Contract": 1.0, + "Mouth_Dimple_L": 1.0, + "Mouth_Dimple_R": 1.0, + "Mouth_Down": 1.0, + "Mouth_Down_Lower_L": 1.0, + "Mouth_Down_Lower_R": 1.0, + "Mouth_Drop_Lower": 1.0, + "Mouth_Drop_Upper": 1.0, + "Mouth_Frown_L": 1.0, + "Mouth_Frown_R": 1.0, + "Mouth_Funnel_Down_L": 1.0, + "Mouth_Funnel_Down_R": 1.0, + "Mouth_Funnel_Up_L": 1.0, + "Mouth_Funnel_Up_R": 1.0, + "Mouth_L": 1.0, + "Mouth_Lower_L": 1.0, + "Mouth_Lower_R": 1.0, + "Mouth_Press_L": 1.0, + "Mouth_Press_R": 1.0, + "Mouth_Pucker_Down_L": 1.0, + "Mouth_Pucker_Down_R": 1.0, + "Mouth_Pucker_Up_L": 1.0, + "Mouth_Pucker_Up_R": 1.0, + "Mouth_Pull_Lower_L": 1.0, + "Mouth_Pull_Lower_R": 1.0, + "Mouth_Pull_Upper_L": 1.0, + "Mouth_Pull_Upper_R": 1.0, + "Mouth_Push_Lower_L": 1.0, + "Mouth_Push_Lower_R": 1.0, + "Mouth_Push_Upper_L": 1.0, + "Mouth_Push_Upper_R": 1.0, + "Mouth_R": 1.0, + "Mouth_Roll_In_Lower_L": 1.0, + "Mouth_Roll_In_Lower_R": 1.0, + "Mouth_Roll_In_Upper_L": 1.0, + "Mouth_Roll_In_Upper_R": 1.0, + "Mouth_Roll_Out_Lower_L": 1.0, + "Mouth_Roll_Out_Lower_R": 1.0, + "Mouth_Roll_Out_Upper_L": 1.0, + "Mouth_Roll_Out_Upper_R": 1.0, + "Mouth_Shrug_Lower": 1.0, + "Mouth_Shrug_Upper": 1.0, + "Mouth_Smile_L": 1.0, + "Mouth_Smile_R": 1.0, + "Mouth_Smile_Sharp_L": 1.0, + "Mouth_Smile_Sharp_R": 1.0, + "Mouth_Stretch_L": 1.0, + "Mouth_Stretch_R": 1.0, + "Mouth_Tighten_L": 1.0, + "Mouth_Tighten_R": 1.0, + "Mouth_Up": 1.0, + "Mouth_Up_Upper_L": 1.0, + "Mouth_Up_Upper_R": 1.0, + "Mouth_Upper_L": 1.0, + "Mouth_Upper_R": 1.0, + "Neck_Swallow_Down": 1.0, + "Neck_Swallow_Up": 1.0, + "Neck_Tighten_L": 1.0, + "Neck_Tighten_R": 1.0, + "Nose_Crease_L": 1.0, + "Nose_Crease_R": 1.0, + "Nose_Nostril_Dilate_L": 1.0, + "Nose_Nostril_Dilate_R": 1.0, + "Nose_Nostril_Down_L": 1.0, + "Nose_Nostril_Down_R": 1.0, + "Nose_Nostril_In_L": 1.0, + "Nose_Nostril_In_R": 1.0, + "Nose_Nostril_Raise_L": 1.0, + "Nose_Nostril_Raise_R": 1.0, + "Nose_Sneer_L": 1.0, + "Nose_Sneer_R": 1.0, + "Nose_Tip_Down": 1.0, + "Nose_Tip_L": 1.0, + "Nose_Tip_R": 1.0, + "Nose_Tip_Up": 1.0, + "Tongue_Bulge_L": 1.0, + "Tongue_Bulge_R": 1.0, + "Tongue_Down": 1.0, + "Tongue_Enlarge": 1.0, + "Tongue_Extend": 1.0, + "Tongue_In": 1.0, + "Tongue_L": 1.0, + "Tongue_Mid_Up": 1.0, + "Tongue_Narrow": 1.0, + "Tongue_Out": 1.0, + "Tongue_R": 1.0, + "Tongue_Roll": 1.0, + "Tongue_Tip_Down": 1.0, + "Tongue_Tip_L": 1.0, + "Tongue_Tip_R": 1.0, + "Tongue_Tip_Up": 1.0, + "Tongue_Twist_L": 1.0, + "Tongue_Twist_R": 1.0, + "Tongue_Up": 1.0, + "Tongue_Wide": 1.0, + "Brow_Down_L": 1.0, + "Brow_Down_R": 1.0, + "Brow_Lateral_L": 1.0, + "Brow_Lateral_R": 1.0, + "Brow_Raise_In_L": 1.0, + "Brow_Raise_In_R": 1.0, + "C_BlinkL": 1.0, + "C_BlinkL_CheekRaiseL": 1.0, + "C_BlinkL_LookDownL": 1.0, + "C_BlinkL_LookLeftL": 1.0, + "C_BlinkL_LookRightL": 1.0, + "C_BlinkL_LookUpL": 1.0, + "C_BlinkL_SquintInnerL": 1.0, + "C_BlinkL_SquintInnerL_CheekRaiseL": 1.0, + "C_BlinkR": 1.0, + "C_BlinkR_CheekRaiseR": 1.0, + "C_BlinkR_LookDownR": 1.0, + "C_BlinkR_LookLeftR": 1.0, + "C_BlinkR_LookRightR": 1.0, + "C_BlinkR_LookUpR": 1.0, + "C_BlinkR_SquintInnerR": 1.0, + "C_BlinkR_SquintInnerR_CheekRaiseR": 1.0, + "C_CheekBlowL_CheekBlowR": 1.0, + "C_CheekRaiseL_CornerPullL": 1.0, + "C_CheekRaiseL_SquintInnerL": 1.0, + "C_CheekRaiseR_CornerPullR": 1.0, + "C_CheekRaiseR_SquintInnerR": 1.0, + "C_CornerPullL_CornerPullR": 1.0, + "C_CornerPullL_DimpleL": 1.0, + "C_CornerPullL_JawOpen": 1.0, + "C_CornerPullL_JawOpen_LowerLipDepressL": 1.0, + "C_CornerPullL_LowerLipDepressL": 1.0, + "C_CornerPullL_SharpCornerPullL": 1.0, + "C_CornerPullL_StretchL": 1.0, + "C_CornerPullL_StretchL_JawOpen": 1.0, + "C_CornerPullL_UpperLipRaiseL": 1.0, + "C_CornerPullRL_StretchRL_JawOpen": 1.0, + "C_CornerPullRL_StretchRL_JawOpen_LowerLipDepressRL": 1.0, + "C_CornerPullR_DimpleR": 1.0, + "C_CornerPullR_JawOpen": 1.0, + "C_CornerPullR_JawOpen_LowerLipDepressR": 1.0, + "C_CornerPullR_LowerLipDepressR": 1.0, + "C_CornerPullR_SharpCornerPullR": 1.0, + "C_CornerPullR_StretchR": 1.0, + "C_CornerPullR_StretchR_JawOpen": 1.0, + "C_CornerPullR_UpperLipRaiseR": 1.0, + "C_DimpleL_JawOpen": 1.0, + "C_DimpleL_LowerLipDepressL": 1.0, + "C_DimpleL_UpperLipRaiseL": 1.0, + "C_DimpleR_JawOpen": 1.0, + "C_DimpleR_LowerLipDepressR": 1.0, + "C_DimpleR_UpperLipRaiseR": 1.0, + "C_FunnelDL_CornerPullL": 1.0, + "C_FunnelDL_LipsPurseDL_CornerPullL": 1.0, + "C_FunnelDL_LipsTowardsDL": 1.0, + "C_FunnelDL_LowerLipDepressL": 1.0, + "C_FunnelDR_CornerPullR": 1.0, + "C_FunnelDR_LipsPurseDR_CornerPullR": 1.0, + "C_FunnelDR_LipsTowardsDR": 1.0, + "C_FunnelDR_LowerLipDepressR": 1.0, + "C_FunnelUL_ChinRaiseUL": 1.0, + "C_FunnelUL_CornerPullL": 1.0, + "C_FunnelUL_LipsPurseUL_CornerPullL": 1.0, + "C_FunnelUL_LipsTowardsUL": 1.0, + "C_FunnelUL_UpperLipRaiseL": 1.0, + "C_FunnelUR_ChinRaiseUR": 1.0, + "C_FunnelUR_CornerPullR": 1.0, + "C_FunnelUR_LipsPurseUR_CornerPullR": 1.0, + "C_FunnelUR_LipsTowardsUR": 1.0, + "C_FunnelUR_UpperLipRaiseR": 1.0, + "C_LipsPurseDL_CornerPullL": 1.0, + "C_LipsPurseDL_FunnelDL": 1.0, + "C_LipsPurseDL_JawOpen": 1.0, + "C_LipsPurseDL_LipsTogetherDL": 1.0, + "C_LipsPurseDL_LipsTowardsDL": 1.0, + "C_LipsPurseDL_LipsTowardsDL_FunnelDL": 1.0, + "C_LipsPurseDR_CornerPullR": 1.0, + "C_LipsPurseDR_FunnelDR": 1.0, + "C_LipsPurseDR_JawOpen": 1.0, + "C_LipsPurseDR_LipsTogetherDR": 1.0, + "C_LipsPurseDR_LipsTowardsDR": 1.0, + "C_LipsPurseDR_LipsTowardsDR_FunnelDR": 1.0, + "C_LipsPurseUL_CornerPullL": 1.0, + "C_LipsPurseUL_FunnelUL": 1.0, + "C_LipsPurseUL_JawOpen": 1.0, + "C_LipsPurseUL_LipsTogetherUL": 1.0, + "C_LipsPurseUL_LipsTowardsUL": 1.0, + "C_LipsPurseUL_LipsTowardsUL_FunnelUL": 1.0, + "C_LipsPurseUR_CornerPullR": 1.0, + "C_LipsPurseUR_FunnelUR": 1.0, + "C_LipsPurseUR_JawOpen": 1.0, + "C_LipsPurseUR_LipsTogetherUR": 1.0, + "C_LipsPurseUR_LipsTowardsUR": 1.0, + "C_LipsPurseUR_LipsTowardsUR_FunnelUR": 1.0, + "C_LowerLipBiteL_JawOpen": 1.0, + "C_LowerLipBiteR_JawOpen": 1.0, + "C_LowerLipDepressL_LowerLipDepressR": 1.0, + "C_NoseWrinkleL_BrowDownL": 1.0, + "C_NoseWrinkleR_BrowDownR": 1.0, + "C_PressDL_JawOpen": 1.0, + "C_PressDR_JawOpen": 1.0, + "C_PressUL_JawOpen": 1.0, + "C_PressUR_JawOpen": 1.0, + "C_StretchL_CornerPullL_LowerLipDepressL": 1.0, + "C_StretchL_JawOpen": 1.0, + "C_StretchL_LowerLipBite_L": 1.0, + "C_StretchL_LowerLipDepressL": 1.0, + "C_StretchL_LowerLipDepressL_JawOpen": 1.0, + "C_StretchL_StretchR": 1.0, + "C_StretchR_CornerPullR_LowerLipDepressR": 1.0, + "C_StretchR_JawOpen": 1.0, + "C_StretchR_LowerLipBite_R": 1.0, + "C_StretchR_LowerLipDepressR": 1.0, + "C_StretchR_LowerLipDepressR_JawOpen": 1.0, + "C_UpperLipBiteL_JawOpen": 1.0, + "C_UpperLipBiteR_JawOpen": 1.0, + "C_UpperLipRaiseL_UpperLipRaiseR": 1.0, + "C_WidenL_LookDownL": 1.0, + "C_WidenR_LookDownR": 1.0, + "C_WrinkleL_MouthCornerPullL": 1.0, + "C_WrinkleL_UpperLipRaiseL": 1.0, + "C_WrinkleR_MouthCornerPullR": 1.0, + "C_WrinkleR_UpperLipRaiseR": 1.0, + "Eye_Cheek_Raise_L": 1.0, + "Eye_Cheek_Raise_R": 1.0, + "Eye_Face_Scrunch_L": 1.0, + "Eye_Face_Scrunch_R": 1.0, + "Eye_Lid_Press_L": 1.0, + "Eye_Lid_Press_R": 1.0, + "Eye_Look_Down_L": 1.0, + "Eye_Look_Down_R": 1.0, + "Eye_Look_Left_L": 1.0, + "Eye_Look_Left_R": 1.0, + "Eye_Look_Right_L": 1.0, + "Eye_Look_Right_R": 1.0, + "Eye_Look_Up_L": 1.0, + "Eye_Look_Up_R": 1.0, + "Eye_LowerLid_Down_L": 1.0, + "Eye_LowerLid_Down_R": 1.0, + "Eye_LowerLid_Up_L": 1.0, + "Eye_LowerLid_Up_R": 1.0, + "Eye_Parallel_Look_Direction": 1.0, + "Eye_Pupil_Narrow_L": 1.0, + "Eye_Pupil_Narrow_R": 1.0, + "Eye_Pupil_Wide_L": 1.0, + "Eye_Pupil_Wide_R": 1.0, + "Eye_Relax_L": 1.0, + "Eye_Relax_R": 1.0, + "Eye_Squint_Inner_L": 1.0, + "Eye_Squint_Inner_R": 1.0, + "Eye_UpperLid_Up_L": 1.0, + "Eye_UpperLid_Up_R": 1.0, + "Eye_Widen_L": 1.0, + "Eye_Widen_R": 1.0, + "Eyelashes_Down_IN_L": 1.0, + "Eyelashes_Down_IN_R": 1.0, + "Eyelashes_Down_OUT_L": 1.0, + "Eyelashes_Down_OUT_R": 1.0, + "Eyelashes_Up_IN_L": 1.0, + "Eyelashes_Up_IN_R": 1.0, + "Eyelashes_Up_OUT_L": 1.0, + "Eyelashes_Up_OUT_R": 1.0, + "Jaw_Back": 1.0, + "Jaw_Chin_Compress_L": 1.0, + "Jaw_Chin_Compress_R": 1.0, + "Jaw_Chin_Raise_DL": 1.0, + "Jaw_Chin_Raise_DR": 1.0, + "Jaw_Chin_Raise_UL": 1.0, + "Jaw_Chin_Raise_UR": 1.0, + "Jaw_Clench_L": 1.0, + "Jaw_Clench_R": 1.0, + "Jaw_Fwd": 1.0, + "Jaw_Left": 1.0, + "Jaw_Open_Extreme": 1.0, + "Jaw_Right": 1.0, + "Mouth_Cheek_Blow_L": 1.0, + "Mouth_Cheek_Blow_R": 1.0, + "Mouth_Cheek_Suck_L": 1.0, + "Mouth_Cheek_Suck_R": 1.0, + "Mouth_Corner_Depress_L": 1.0, + "Mouth_Corner_Depress_R": 1.0, + "Mouth_Corner_Down_L": 1.0, + "Mouth_Corner_Down_R": 1.0, + "Mouth_Corner_Narrow_L": 1.0, + "Mouth_Corner_Narrow_R": 1.0, + "Mouth_Corner_Pull_L": 1.0, + "Mouth_Corner_Pull_R": 1.0, + "Mouth_Corner_Rounder_DL": 1.0, + "Mouth_Corner_Rounder_DR": 1.0, + "Mouth_Corner_Rounder_UL": 1.0, + "Mouth_Corner_Rounder_UR": 1.0, + "Mouth_Corner_Sharpen_DL": 1.0, + "Mouth_Corner_Sharpen_DR": 1.0, + "Mouth_Corner_Sharpen_UL": 1.0, + "Mouth_Corner_Sharpen_UR": 1.0, + "Mouth_Corner_Up_L": 1.0, + "Mouth_Corner_Up_R": 1.0, + "Mouth_Corner_Wide_L": 1.0, + "Mouth_Corner_Wide_R": 1.0, + "Mouth_Funnel_DL": 1.0, + "Mouth_Funnel_DR": 1.0, + "Mouth_Funnel_UL": 1.0, + "Mouth_Funnel_UR": 1.0, + "Mouth_Left": 1.0, + "Mouth_Lips_Blow_L": 1.0, + "Mouth_Lips_Blow_R": 1.0, + "Mouth_Lips_Press_L": 1.0, + "Mouth_Lips_Press_R": 1.0, + "Mouth_Lips_Pull_DL": 1.0, + "Mouth_Lips_Pull_DR": 1.0, + "Mouth_Lips_Pull_UL": 1.0, + "Mouth_Lips_Pull_UR": 1.0, + "Mouth_Lips_Purse_DL": 1.0, + "Mouth_Lips_Purse_DR": 1.0, + "Mouth_Lips_Purse_UL": 1.0, + "Mouth_Lips_Purse_UR": 1.0, + "Mouth_Lips_Push_DL": 1.0, + "Mouth_Lips_Push_DR": 1.0, + "Mouth_Lips_Push_UL": 1.0, + "Mouth_Lips_Push_UR": 1.0, + "Mouth_Lips_Sticky_L_Ph1": 1.0, + "Mouth_Lips_Sticky_L_Ph2": 1.0, + "Mouth_Lips_Sticky_L_Ph3": 1.0, + "Mouth_Lips_Sticky_R_Ph1": 1.0, + "Mouth_Lips_Sticky_R_Ph2": 1.0, + "Mouth_Lips_Sticky_R_Ph3": 1.0, + "Mouth_Lips_Thick_DL": 1.0, + "Mouth_Lips_Thick_DR": 1.0, + "Mouth_Lips_Thick_Inward_DL": 1.0, + "Mouth_Lips_Thick_Inward_DR": 1.0, + "Mouth_Lips_Thick_Inward_UL": 1.0, + "Mouth_Lips_Thick_Inward_UR": 1.0, + "Mouth_Lips_Thick_UL": 1.0, + "Mouth_Lips_Thick_UR": 1.0, + "Mouth_Lips_Thin_DL": 1.0, + "Mouth_Lips_Thin_DR": 1.0, + "Mouth_Lips_Thin_Inward_DL": 1.0, + "Mouth_Lips_Thin_Inward_DR": 1.0, + "Mouth_Lips_Thin_Inward_UL": 1.0, + "Mouth_Lips_Thin_Inward_UR": 1.0, + "Mouth_Lips_Thin_UL": 1.0, + "Mouth_Lips_Thin_UR": 1.0, + "Mouth_Lips_Tighten_DL": 1.0, + "Mouth_Lips_Tighten_DR": 1.0, + "Mouth_Lips_Tighten_UL": 1.0, + "Mouth_Lips_Tighten_UR": 1.0, + "Mouth_Lips_Together_DL": 1.0, + "Mouth_Lips_Together_DR": 1.0, + "Mouth_Lips_Together_UL": 1.0, + "Mouth_Lips_Together_UR": 1.0, + "Mouth_Lips_Towards_DL": 1.0, + "Mouth_Lips_Towards_DR": 1.0, + "Mouth_Lips_Towards_UL": 1.0, + "Mouth_Lips_Towards_UR": 1.0, + "Mouth_LowerLip_Bite_L": 1.0, + "Mouth_LowerLip_Bite_R": 1.0, + "Mouth_LowerLip_Depress_L": 1.0, + "Mouth_LowerLip_Depress_R": 1.0, + "Mouth_LowerLip_RollIn_L": 1.0, + "Mouth_LowerLip_RollIn_R": 1.0, + "Mouth_LowerLip_Roll_Out_L": 1.0, + "Mouth_LowerLip_Roll_Out_R": 1.0, + "Mouth_LowerLip_Shift_Left": 1.0, + "Mouth_LowerLip_Shift_Right": 1.0, + "Mouth_LowerLip_Towards_Teeth_L": 1.0, + "Mouth_LowerLip_Towards_Teeth_R": 1.0, + "Mouth_Mouth_Press_DL": 1.0, + "Mouth_Mouth_Press_DR": 1.0, + "Mouth_Mouth_Press_UL": 1.0, + "Mouth_Mouth_Press_UR": 1.0, + "Mouth_Right": 1.0, + "Mouth_SharpCorner_Pull_L": 1.0, + "Mouth_SharpCorner_Pull_R": 1.0, + "Mouth_Sticky_DC": 1.0, + "Mouth_Sticky_D_IN_L": 1.0, + "Mouth_Sticky_D_IN_R": 1.0, + "Mouth_Sticky_D_OUT_L": 1.0, + "Mouth_Sticky_D_OUT_R": 1.0, + "Mouth_Sticky_UC": 1.0, + "Mouth_Sticky_U_IN_L": 1.0, + "Mouth_Sticky_U_IN_R": 1.0, + "Mouth_Sticky_U_OUT_L": 1.0, + "Mouth_Sticky_U_OUT_R": 1.0, + "Mouth_StretchLips_Close_L": 1.0, + "Mouth_StretchLips_Close_R": 1.0, + "Mouth_UpperLip_Bite_L": 1.0, + "Mouth_UpperLip_Bite_R": 1.0, + "Mouth_UpperLip_Raise_L": 1.0, + "Mouth_UpperLip_Raise_R": 1.0, + "Mouth_UpperLip_RollIn_L": 1.0, + "Mouth_UpperLip_RollIn_R": 1.0, + "Mouth_UpperLip_Roll_Out_L": 1.0, + "Mouth_UpperLip_Roll_Out_R": 1.0, + "Mouth_UpperLip_Shift_Left": 1.0, + "Mouth_UpperLip_Shift_Right": 1.0, + "Mouth_UpperLip_Towards_Teeth_L": 1.0, + "Mouth_UpperLip_Towards_Teeth_R": 1.0, + "Neck_Digastric_Down": 1.0, + "Neck_Digastric_Up": 1.0, + "Neck_Mastoid_Contract_L": 1.0, + "Neck_Mastoid_Contract_R": 1.0, + "Neck_Stretch_L": 1.0, + "Neck_Stretch_R": 1.0, + "Neck_Swallow_Ph1": 1.0, + "Neck_Swallow_Ph2": 1.0, + "Neck_Swallow_Ph3": 1.0, + "Neck_Swallow_Ph4": 1.0, + "Neck_Swallow_Ph5": 1.0, + "Neck_Throat_Down": 1.0, + "Neck_Throat_Exhale": 1.0, + "Neck_Throat_Inhale": 1.0, + "Neck_Throat_Up": 1.0, + "Nose_Nasolabial_Deepen_L": 1.0, + "Nose_Nasolabial_Deepen_R": 1.0, + "Nose_Nostril_Compress_L": 1.0, + "Nose_Nostril_Compress_R": 1.0, + "Nose_Nostril_Depress_L": 1.0, + "Nose_Nostril_Depress_R": 1.0, + "Nose_Wrinkle_L": 1.0, + "Nose_Wrinkle_R": 1.0, + "Nose_Wrinkle_Upper_L": 1.0, + "Nose_Wrinkle_Upper_R": 1.0, + "Teeth_Back_D": 1.0, + "Teeth_Back_U": 1.0, + "Teeth_Down_D": 1.0, + "Teeth_Down_U": 1.0, + "Teeth_Fwd_D": 1.0, + "Teeth_Fwd_U": 1.0, + "Teeth_Left_D": 1.0, + "Teeth_Left_U": 1.0, + "Teeth_Right_D": 1.0, + "Teeth_Right_U": 1.0, + "Teeth_Up_D": 1.0, + "Teeth_Up_U": 1.0, + "Tongue_Bend_Down": 1.0, + "Tongue_Bend_Up": 1.0, + "Tongue_Left": 1.0, + "Tongue_Press": 1.0, + "Tongue_Right": 1.0, + "Tongue_Thick": 1.0, + "Tongue_Thin": 1.0, + "Tongue_Tip_Left": 1.0, + "Tongue_Tip_Right": 1.0, + "Tongue_Twist_Left": 1.0, + "Tongue_Twist_Right": 1.0, + "Brow_Drop_Left": 1.0, + "Brow_Drop_Right": 1.0, + "Brow_Raise_Inner_Left": 1.0, + "Brow_Raise_Inner_Right": 1.0, + "Brow_Raise_Left": 1.0, + "Brow_Raise_Outer_Left": 1.0, + "Brow_Raise_Outer_Right": 1.0, + "Brow_Raise_Right": 1.0, + "Cheek_Blow_L": 1.0, + "Cheek_Blow_R": 1.0, + "Cheeks_Suck": 1.0, + "Eyes_Blink": 1.0, + "Left_Eyeball_Look_Down": 1.0, + "Left_Eyeball_Look_L": 1.0, + "Left_Eyeball_Look_R": 1.0, + "Left_Eyeball_Look_Up": 1.0, + "Mouth_Blow": 1.0, + "Mouth_Bottom_Lip_Bite": 1.0, + "Mouth_Bottom_Lip_Down": 1.0, + "Mouth_Bottom_Lip_Trans": 1.0, + "Mouth_Bottom_Lip_Under": 1.0, + "Mouth_Frown": 1.0, + "Mouth_Lips_Jaw_Adjust": 1.0, + "Mouth_Lips_Open": 1.0, + "Mouth_Lips_Part": 1.0, + "Mouth_Lips_Tight": 1.0, + "Mouth_Lips_Tuck": 1.0, + "Mouth_Open": 1.0, + "Mouth_Plosive": 1.0, + "Mouth_Pucker": 1.0, + "Mouth_Pucker_Open": 1.0, + "Mouth_Skewer": 1.0, + "Mouth_Smile": 1.0, + "Mouth_Snarl_Lower_L": 1.0, + "Mouth_Snarl_Lower_R": 1.0, + "Mouth_Snarl_Upper_L": 1.0, + "Mouth_Snarl_Upper_R": 1.0, + "Mouth_Top_Lip_Under": 1.0, + "Mouth_Top_Lip_Up": 1.0, + "Mouth_Widen": 1.0, + "Mouth_Widen_Sides": 1.0, + "Move_Jaw_Down": 1.0, + "Move_Jaw_L": 1.0, + "Move_Jaw_R": 1.0, + "Nose_Flank_Raise_L": 1.0, + "Nose_Flank_Raise_R": 1.0, + "Nose_Flanks_Raise": 1.0, + "Nose_Nostrils_Flare": 1.0, + "Nose_Scrunch": 1.0, + "Right_Eyeball_Look_Down": 1.0, + "Right_Eyeball_Look_L": 1.0, + "Right_Eyeball_Look_R": 1.0, + "Right_Eyeball_Look_Up": 1.0, + "Turn_Jaw_Down": 1.0, + "Turn_Jaw_L": 1.0, + "Turn_Jaw_R": 1.0, + "A01_Brow_Inner_Up": 1.0, + "A02_Brow_Down_Left": 1.0, + "A03_Brow_Down_Right": 1.0, + "A04_Brow_Outer_Up_Left": 1.0, + "A05_Brow_Outer_Up_Right": 1.0, + "A06_Eye_Look_Up_Left": 1.0, + "A07_Eye_Look_Up_Right": 1.0, + "A08_Eye_Look_Down_Left": 1.0, + "A09_Eye_Look_Down_Right": 1.0, + "A10_Eye_Look_Out_Left": 1.0, + "A11_Eye_Look_In_Left": 1.0, + "A12_Eye_Look_In_Right": 1.0, + "A13_Eye_Look_Out_Right": 1.0, + "A14_Eye_Blink_Left": 1.0, + "A15_Eye_Blink_Right": 1.0, + "A16_Eye_Squint_Left": 1.0, + "A17_Eye_Squint_Right": 1.0, + "A18_Eye_Wide_Left": 1.0, + "A19_Eye_Wide_Right": 1.0, + "A20_Cheek_Puff": 1.0, + "A21_Cheek_Squint_Left": 1.0, + "A22_Cheek_Squint_Right": 1.0, + "A23_Nose_Sneer_Left": 1.0, + "A24_Nose_Sneer_Right": 1.0, + "A25_Jaw_Open": 1.0, + "A26_Jaw_Forward": 1.0, + "A27_Jaw_Left": 1.0, + "A28_Jaw_Right": 1.0, + "A29_Mouth_Funnel": 1.0, + "A30_Mouth_Pucker": 1.0, + "A31_Mouth_Left": 1.0, + "A32_Mouth_Right": 1.0, + "A33_Mouth_Roll_Upper": 1.0, + "A34_Mouth_Roll_Lower": 1.0, + "A35_Mouth_Shrug_Upper": 1.0, + "A36_Mouth_Shrug_Lower": 1.0, + "A37_Mouth_Close": 1.0, + "A38_Mouth_Smile_Left": 1.0, + "A39_Mouth_Smile_Right": 1.0, + "A40_Mouth_Frown_Left": 1.0, + "A41_Mouth_Frown_Right": 1.0, + "A42_Mouth_Dimple_Left": 1.0, + "A43_Mouth_Dimple_Right": 1.0, + "A44_Mouth_Upper_Up_Left": 1.0, + "A45_Mouth_Upper_Up_Right": 1.0, + "A46_Mouth_Lower_Down_Left": 1.0, + "A47_Mouth_Lower_Down_Right": 1.0, + "A48_Mouth_Press_Left": 1.0, + "A49_Mouth_Press_Right": 1.0, + "A50_Mouth_Stretch_Left": 1.0, + "A51_Mouth_Stretch_Right": 1.0, + "A52_Tongue_Out": 1.0, + "T01_Tongue_Up": 1.0, + "T02_Tongue_Down": 1.0, + "T03_Tongue_Left": 1.0, + "T04_Tongue_Right": 1.0, + "T05_Tongue_Roll": 1.0, + "T06_Tongue_Tip_Up": 1.0, + "T07_Tongue_Tip_Down": 1.0, + "T08_Tongue_Width": 1.0, + "T09_Tongue_Thickness": 1.0, + "T10_Tongue_Bulge_Left": 1.0, + "T11_Tongue_Bulge_Right": 1.0, + "OverBite": 1.0, + "Teeth Lower Depth": 1.0, + "Teeth Lower Height": 1.0, + "Teeth Lower Length": 1.0, + "Teeth Lower Scale Width": 1.0, + "Teeth Lower Sharp": 1.0, + "Teeth Upper Depth": 1.0, + "Teeth Upper Height": 1.0, + "Teeth Upper Length": 1.0, + "Teeth Upper Scale Width": 1.0, + "Teeth Upper Sharp": 1.0 + } +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Hands.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Hands.json new file mode 100644 index 0000000..e8c6349 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Hands.json @@ -0,0 +1,129 @@ +{ + "Bones": { + "CC_Base_L_Hand": 1.0, + "CC_Base_L_Pinky1": 1.0, + "CC_Base_L_Pinky2": 1.0, + "CC_Base_L_Pinky3": 1.0, + "CC_Base_L_Ring1": 1.0, + "CC_Base_L_Ring2": 1.0, + "CC_Base_L_Ring3": 1.0, + "CC_Base_L_Mid1": 1.0, + "CC_Base_L_Mid2": 1.0, + "CC_Base_L_Mid3": 1.0, + "CC_Base_L_Index1": 1.0, + "CC_Base_L_Index2": 1.0, + "CC_Base_L_Index3": 1.0, + "CC_Base_L_Thumb1": 1.0, + "CC_Base_L_Thumb2": 1.0, + "CC_Base_L_Thumb3": 1.0, + "CC_Base_R_Hand": 1.0, + "CC_Base_R_Ring1": 1.0, + "CC_Base_R_Ring2": 1.0, + "CC_Base_R_Ring3": 1.0, + "CC_Base_R_Mid1": 1.0, + "CC_Base_R_Mid2": 1.0, + "CC_Base_R_Mid3": 1.0, + "CC_Base_R_Thumb1": 1.0, + "CC_Base_R_Thumb2": 1.0, + "CC_Base_R_Thumb3": 1.0, + "CC_Base_R_Index1": 1.0, + "CC_Base_R_Index2": 1.0, + "CC_Base_R_Index3": 1.0, + "CC_Base_R_Pinky1": 1.0, + "CC_Base_R_Pinky2": 1.0, + "CC_Base_R_Pinky3": 1.0, + "hand_tweak.L": 1.0, + "hand_fk.L": 1.0, + "f_index.01_master.L": 1.0, + "thumb.01_master.L": 1.0, + "f_index.01.L": 1.0, + "f_index.02.L": 1.0, + "f_index.03.L": 1.0, + "f_index.01.L.001": 1.0, + "thumb.01.L": 1.0, + "thumb.02.L": 1.0, + "thumb.03.L": 1.0, + "thumb.01.L.001": 1.0, + "f_middle.01_master.L": 1.0, + "f_middle.01.L": 1.0, + "f_middle.02.L": 1.0, + "f_middle.03.L": 1.0, + "f_middle.01.L.001": 1.0, + "f_ring.01_master.L": 1.0, + "f_ring.01.L": 1.0, + "f_ring.02.L": 1.0, + "f_ring.03.L": 1.0, + "f_ring.01.L.001": 1.0, + "f_pinky.01_master.L": 1.0, + "f_pinky.01.L": 1.0, + "f_pinky.02.L": 1.0, + "f_pinky.03.L": 1.0, + "f_pinky.01.L.001": 1.0, + "palm.L": 1.0, + "hand_tweak.R": 1.0, + "hand_fk.R": 1.0, + "f_index.01_master.R": 1.0, + "thumb.01_master.R": 1.0, + "f_index.01.R": 1.0, + "f_index.02.R": 1.0, + "f_index.03.R": 1.0, + "f_index.01.R.001": 1.0, + "thumb.01.R": 1.0, + "thumb.02.R": 1.0, + "thumb.03.R": 1.0, + "thumb.01.R.001": 1.0, + "f_middle.01_master.R": 1.0, + "f_middle.01.R": 1.0, + "f_middle.02.R": 1.0, + "f_middle.03.R": 1.0, + "f_middle.01.R.001": 1.0, + "f_ring.01_master.R": 1.0, + "f_ring.01.R": 1.0, + "f_ring.02.R": 1.0, + "f_ring.03.R": 1.0, + "f_ring.01.R.001": 1.0, + "f_pinky.01_master.R": 1.0, + "f_pinky.01.R": 1.0, + "f_pinky.02.R": 1.0, + "f_pinky.03.R": 1.0, + "f_pinky.01.R.001": 1.0, + "palm.R": 1.0, + "hand_ik.L": 1.0, + "upper_arm_ik_target.L": 1.0, + "hand_ik.R": 1.0, + "upper_arm_ik_target.R": 1.0, + "hand_l": 1.0, + "middle_01_l": 1.0, + "middle_02_l": 1.0, + "middle_03_l": 1.0, + "index_01_l": 1.0, + "index_02_l": 1.0, + "index_03_l": 1.0, + "ring_01_l": 1.0, + "ring_02_l": 1.0, + "ring_03_l": 1.0, + "pinky_01_l": 1.0, + "pinky_02_l": 1.0, + "pinky_03_l": 1.0, + "thumb_01_l": 1.0, + "thumb_02_l": 1.0, + "thumb_03_l": 1.0, + "hand_r": 1.0, + "middle_01_r": 1.0, + "middle_02_r": 1.0, + "middle_03_r": 1.0, + "ring_01_r": 1.0, + "ring_02_r": 1.0, + "ring_03_r": 1.0, + "thumb_01_r": 1.0, + "thumb_02_r": 1.0, + "thumb_03_r": 1.0, + "index_01_r": 1.0, + "index_02_r": 1.0, + "index_03_r": 1.0, + "pinky_01_r": 1.0, + "pinky_02_r": 1.0, + "pinky_03_r": 1.0 + }, + "Keys": {} +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Head & Expressions.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Head & Expressions.json new file mode 100644 index 0000000..16d322f --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Head & Expressions.json @@ -0,0 +1,872 @@ +{ + "Bones": { + "CTRL_L_brow_down": 1.0, + "CTRL_R_brow_down": 1.0, + "CTRL_L_brow_lateral": 1.0, + "CTRL_R_brow_lateral": 1.0, + "CTRL_L_brow_raiseIn": 1.0, + "CTRL_R_brow_raiseIn": 1.0, + "CTRL_L_brow_raiseOut": 1.0, + "CTRL_R_brow_raiseOut": 1.0, + "CTRL_L_eye_blink": 1.0, + "CTRL_R_eye_blink": 1.0, + "CTRL_L_eye_cheekRaise": 1.0, + "CTRL_R_eye_cheekRaise": 1.0, + "CTRL_L_eye_lidPress": 1.0, + "CTRL_R_eye_lidPress": 1.0, + "CTRL_L_eye_squintInner": 1.0, + "CTRL_R_eye_squintInner": 1.0, + "CTRL_C_eye": 1.0, + "CTRL_C_eye_parallelLook": 1.0, + "CTRL_L_eye": 1.0, + "CTRL_R_eye": 1.0, + "CTRL_L_eye_pupil": 1.0, + "CTRL_R_eye_pupil": 1.0, + "CTRL_L_ear_up": 1.0, + "CTRL_R_ear_up": 1.0, + "CTRL_L_nose": 1.0, + "CTRL_R_nose": 1.0, + "CTRL_L_nose_wrinkleUpper": 1.0, + "CTRL_R_nose_wrinkleUpper": 1.0, + "CTRL_L_mouth_suckBlow": 1.0, + "CTRL_R_mouth_suckBlow": 1.0, + "CTRL_C_mouth": 1.0, + "CTRL_L_mouth_cornerDepress": 1.0, + "CTRL_R_mouth_cornerDepress": 1.0, + "CTRL_L_mouth_cornerPull": 1.0, + "CTRL_R_mouth_cornerPull": 1.0, + "CTRL_L_mouth_dimple": 1.0, + "CTRL_R_mouth_dimple": 1.0, + "CTRL_L_mouth_lowerLipDepress": 1.0, + "CTRL_R_mouth_lowerLipDepress": 1.0, + "CTRL_L_mouth_sharpCornerPull": 1.0, + "CTRL_R_mouth_sharpCornerPull": 1.0, + "CTRL_L_mouth_stretch": 1.0, + "CTRL_R_mouth_stretch": 1.0, + "CTRL_L_mouth_stretchLipsClose": 1.0, + "CTRL_R_mouth_stretchLipsClose": 1.0, + "CTRL_L_mouth_upperLipRaise": 1.0, + "CTRL_R_mouth_upperLipRaise": 1.0, + "CTRL_L_mouth_funnelD": 1.0, + "CTRL_R_mouth_funnelD": 1.0, + "CTRL_L_mouth_funnelU": 1.0, + "CTRL_R_mouth_funnelU": 1.0, + "CTRL_L_mouth_lipBiteD": 1.0, + "CTRL_R_mouth_lipBiteD": 1.0, + "CTRL_L_mouth_lipBiteU": 1.0, + "CTRL_R_mouth_lipBiteU": 1.0, + "CTRL_L_mouth_lipsBlow": 1.0, + "CTRL_R_mouth_lipsBlow": 1.0, + "CTRL_L_mouth_lipsPressU": 1.0, + "CTRL_R_mouth_lipsPressU": 1.0, + "CTRL_L_mouth_pressD": 1.0, + "CTRL_R_mouth_pressD": 1.0, + "CTRL_L_mouth_pressU": 1.0, + "CTRL_R_mouth_pressU": 1.0, + "CTRL_L_mouth_purseD": 1.0, + "CTRL_R_mouth_purseD": 1.0, + "CTRL_L_mouth_purseU": 1.0, + "CTRL_R_mouth_purseU": 1.0, + "CTRL_L_mouth_tightenD": 1.0, + "CTRL_R_mouth_tightenD": 1.0, + "CTRL_L_mouth_tightenU": 1.0, + "CTRL_R_mouth_tightenU": 1.0, + "CTRL_L_mouth_towardsD": 1.0, + "CTRL_R_mouth_towardsD": 1.0, + "CTRL_L_mouth_towardsU": 1.0, + "CTRL_R_mouth_towardsU": 1.0, + "CTRL_L_mouth_lipsTogetherD": 1.0, + "CTRL_R_mouth_lipsTogetherD": 1.0, + "CTRL_L_mouth_lipsTogetherU": 1.0, + "CTRL_R_mouth_lipsTogetherU": 1.0, + "CTRL_C_mouth_stickyD": 1.0, + "CTRL_C_mouth_stickyU": 1.0, + "CTRL_L_mouth_lipSticky": 1.0, + "CTRL_R_mouth_lipSticky": 1.0, + "CTRL_L_mouth_stickyInnerD": 1.0, + "CTRL_R_mouth_stickyInnerD": 1.0, + "CTRL_L_mouth_stickyInnerU": 1.0, + "CTRL_R_mouth_stickyInnerU": 1.0, + "CTRL_L_mouth_stickyOuterD": 1.0, + "CTRL_R_mouth_stickyOuterD": 1.0, + "CTRL_L_mouth_stickyOuterU": 1.0, + "CTRL_R_mouth_stickyOuterU": 1.0, + "CTRL_C_jaw": 1.0, + "CTRL_C_jaw_fwdBack": 1.0, + "CTRL_C_jaw_openExtreme": 1.0, + "CTRL_L_jaw_ChinRaiseD": 1.0, + "CTRL_R_jaw_ChinRaiseD": 1.0, + "CTRL_L_jaw_ChinRaiseU": 1.0, + "CTRL_R_jaw_ChinRaiseU": 1.0, + "CTRL_L_jaw_chinCompress": 1.0, + "CTRL_R_jaw_chinCompress": 1.0, + "CTRL_L_jaw_clench": 1.0, + "CTRL_R_jaw_clench": 1.0, + "CTRL_C_neck_swallow": 1.0, + "CTRL_L_neck_mastoidContract": 1.0, + "CTRL_R_neck_mastoidContract": 1.0, + "CTRL_L_neck_stretch": 1.0, + "CTRL_R_neck_stretch": 1.0, + "CTRL_neck_digastricUpDown": 1.0, + "CTRL_neck_throatExhaleInhale": 1.0, + "CTRL_neck_throatUpDown": 1.0, + "CTRL_C_head_turn": 1.0, + "CTRL_C_head_move": 1.0, + "CTRL_C_head_tilt": 1.0, + "CTRL_C_head_fwdBack": 1.0, + "CTRL_L_eye_eyelidD": 1.0, + "CTRL_R_eye_eyelidD": 1.0, + "CTRL_L_eye_eyelidU": 1.0, + "CTRL_R_eye_eyelidU": 1.0, + "CTRL_L_eye_faceScrunch": 1.0, + "CTRL_R_eye_faceScrunch": 1.0, + "CTRL_L_eyelashes_tweakerIn": 1.0, + "CTRL_R_eyelashes_tweakerIn": 1.0, + "CTRL_L_eyelashes_tweakerOut": 1.0, + "CTRL_R_eyelashes_tweakerOut": 1.0, + "CTRL_L_nose_nasolabialDeepen": 1.0, + "CTRL_R_nose_nasolabialDeepen": 1.0, + "CTRL_L_mouth_pushPullD": 1.0, + "CTRL_R_mouth_pushPullD": 1.0, + "CTRL_L_mouth_pushPullU": 1.0, + "CTRL_R_mouth_pushPullU": 1.0, + "CTRL_C_mouth_lipShiftD": 1.0, + "CTRL_C_mouth_lipShiftU": 1.0, + "CTRL_L_mouth_lipsRollD": 1.0, + "CTRL_R_mouth_lipsRollD": 1.0, + "CTRL_L_mouth_lipsRollU": 1.0, + "CTRL_R_mouth_lipsRollU": 1.0, + "CTRL_L_mouth_corner": 1.0, + "CTRL_R_mouth_corner": 1.0, + "CTRL_L_mouth_cornerSharpnessD": 1.0, + "CTRL_R_mouth_cornerSharpnessD": 1.0, + "CTRL_L_mouth_cornerSharpnessU": 1.0, + "CTRL_R_mouth_cornerSharpnessU": 1.0, + "CTRL_L_mouth_lipsTowardsTeethD": 1.0, + "CTRL_R_mouth_lipsTowardsTeethD": 1.0, + "CTRL_L_mouth_lipsTowardsTeethU": 1.0, + "CTRL_R_mouth_lipsTowardsTeethU": 1.0, + "CTRL_L_mouth_thicknessD": 1.0, + "CTRL_R_mouth_thicknessD": 1.0, + "CTRL_L_mouth_thicknessInwardD": 1.0, + "CTRL_R_mouth_thicknessInwardD": 1.0, + "CTRL_L_mouth_thicknessInwardU": 1.0, + "CTRL_R_mouth_thicknessInwardU": 1.0, + "CTRL_L_mouth_thicknessU": 1.0, + "CTRL_R_mouth_thicknessU": 1.0, + "CTRL_C_tongue_bendTwist": 1.0, + "CTRL_C_tongue_inOut": 1.0, + "CTRL_C_tongue_move": 1.0, + "CTRL_C_tongue_press": 1.0, + "CTRL_C_tongue_roll": 1.0, + "CTRL_C_tongue_thickThin": 1.0, + "CTRL_C_tongue_tipMove": 1.0, + "CTRL_C_tongue_wideNarrow": 1.0, + "CTRL_C_teethD": 1.0, + "CTRL_C_teethU": 1.0, + "CTRL_C_teeth_fwdBackD": 1.0, + "CTRL_C_teeth_fwdBackU": 1.0, + "CTRL_eye_pupil": 1.0, + "CTRL_R_mouth_tighten": 1.0, + "CTRL_L_mouth_tighten": 1.0, + "CTRL_C_jaw_chinRaise": 1.0, + "CTRL_C_mouth_lipsTogether": 1.0, + "CTRL_R_mouth_press": 1.0, + "CTRL_L_mouth_press": 1.0, + "CTRL_C_tongue": 1.0, + "CTRL_C_tongue_tip": 1.0, + "CTRL_mouth_shrugDropU": 1.0, + "CTRL_mouth_shrugDropD": 1.0, + "CTRL_C_tongue_narrowWide": 1.0, + "CTRL_R_eyelashU": 1.0, + "CTRL_R_eyelashD": 1.0, + "CTRL_L_eyelashU": 1.0, + "CTRL_L_eyelashD": 1.0, + "CTRL_C_mouth_thickness": 1.0, + "CTRL_brow_raiseIn": 1.0, + "CTRL_mouth_purse_Std": 1.0, + "CTRL_mouth_funnel_Std": 1.0, + "CTRL_mouth_lipBiteU_Std": 1.0, + "CTRL_mouth_lipBiteD_Std": 1.0, + "CTRL_C_jaw_fwdBack_Std": 1.0, + "CTRL_mouth_puff_Std": 1.0, + "CTRL_C_tongue_Out_Std": 1.0, + "CTRL_C_tongue_tip_upDown_Std": 1.0, + "CTRL_C_tongue_roll_Std": 1.0, + "CTRL_mouth_shrugU_Std": 1.0, + "CTRL_mouth_shrugD_Std": 1.0, + "CTRL_mouth_lipsRollU_Std": 1.0, + "CTRL_mouth_lipsRollD_Std": 1.0, + "CTRL_R_mouth_corner_Std": 1.0, + "CTRL_L_mouth_corner_Std": 1.0, + "CC_Base_NeckTwist01": 1.0, + "CC_Base_NeckTwist02": 1.0, + "CC_Base_Head": 1.0, + "CC_Base_FacialBone": 1.0, + "CC_Base_JawRoot": 1.0, + "CC_Base_Tongue01": 1.0, + "CC_Base_Tongue02": 1.0, + "CC_Base_Tongue03": 1.0, + "CC_Base_Teeth02": 1.0, + "CC_Base_R_Eye": 1.0, + "CC_Base_L_Eye": 1.0, + "CC_Base_UpperJaw": 1.0, + "CC_Base_Teeth01": 1.0, + "master_eye.L": 1.0, + "master_eye.R": 1.0, + "teeth.T": 1.0, + "jaw_master": 1.0, + "chin": 1.0, + "jaw": 1.0, + "teeth.B": 1.0, + "tongue.003": 1.0, + "tongue_master": 1.0, + "tongue": 1.0, + "tongue.001": 1.0, + "tongue.002": 1.0, + "neck": 1.0, + "head": 1.0, + "tweak_spine.005": 1.0, + "eyes": 1.0, + "eye.L": 1.0, + "eye.R": 1.0, + "neck_01": 1.0 + }, + "Keys": { + "Brow_Compress_L": 1.0, + "Brow_Compress_R": 1.0, + "Brow_Drop_L": 1.0, + "Brow_Drop_R": 1.0, + "Brow_Raise_Inner_L": 1.0, + "Brow_Raise_Inner_R": 1.0, + "Brow_Raise_Outer_L": 1.0, + "Brow_Raise_Outer_R": 1.0, + "Cheek_Puff_L": 1.0, + "Cheek_Puff_R": 1.0, + "Cheek_Raise_L": 1.0, + "Cheek_Raise_R": 1.0, + "Cheek_Suck_L": 1.0, + "Cheek_Suck_R": 1.0, + "Ear_Down_L": 1.0, + "Ear_Down_R": 1.0, + "Ear_Out_L": 1.0, + "Ear_Out_R": 1.0, + "Ear_Up_L": 1.0, + "Ear_Up_R": 1.0, + "Eye_Blink_L": 1.0, + "Eye_Blink_R": 1.0, + "Eye_L_Look_Down": 1.0, + "Eye_L_Look_L": 1.0, + "Eye_L_Look_R": 1.0, + "Eye_L_Look_Up": 1.0, + "Eye_Pupil_Contract": 1.0, + "Eye_Pupil_Dilate": 1.0, + "Eye_R_Look_Down": 1.0, + "Eye_R_Look_L": 1.0, + "Eye_R_Look_R": 1.0, + "Eye_R_Look_Up": 1.0, + "Eye_Squint_L": 1.0, + "Eye_Squint_R": 1.0, + "Eye_Wide_L": 1.0, + "Eye_Wide_R": 1.0, + "Eyelash_Lower_Down_L": 1.0, + "Eyelash_Lower_Down_R": 1.0, + "Eyelash_Lower_Up_L": 1.0, + "Eyelash_Lower_Up_R": 1.0, + "Eyelash_Upper_Down_L": 1.0, + "Eyelash_Upper_Down_R": 1.0, + "Eyelash_Upper_Up_L": 1.0, + "Eyelash_Upper_Up_R": 1.0, + "Head_Backward": 1.0, + "Head_Forward": 1.0, + "Head_L": 1.0, + "Head_R": 1.0, + "Head_Tilt_L": 1.0, + "Head_Tilt_R": 1.0, + "Head_Turn_Down": 1.0, + "Head_Turn_L": 1.0, + "Head_Turn_R": 1.0, + "Head_Turn_Up": 1.0, + "Jaw_Backward": 1.0, + "Jaw_Down": 1.0, + "Jaw_Forward": 1.0, + "Jaw_L": 1.0, + "Jaw_Open": 1.0, + "Jaw_R": 1.0, + "Jaw_Up": 1.0, + "Mouth_Blow_L": 1.0, + "Mouth_Blow_R": 1.0, + "Mouth_Chin_Up": 1.0, + "Mouth_Close": 1.0, + "Mouth_Contract": 1.0, + "Mouth_Dimple_L": 1.0, + "Mouth_Dimple_R": 1.0, + "Mouth_Down": 1.0, + "Mouth_Down_Lower_L": 1.0, + "Mouth_Down_Lower_R": 1.0, + "Mouth_Drop_Lower": 1.0, + "Mouth_Drop_Upper": 1.0, + "Mouth_Frown_L": 1.0, + "Mouth_Frown_R": 1.0, + "Mouth_Funnel_Down_L": 1.0, + "Mouth_Funnel_Down_R": 1.0, + "Mouth_Funnel_Up_L": 1.0, + "Mouth_Funnel_Up_R": 1.0, + "Mouth_L": 1.0, + "Mouth_Lower_L": 1.0, + "Mouth_Lower_R": 1.0, + "Mouth_Press_L": 1.0, + "Mouth_Press_R": 1.0, + "Mouth_Pucker_Down_L": 1.0, + "Mouth_Pucker_Down_R": 1.0, + "Mouth_Pucker_Up_L": 1.0, + "Mouth_Pucker_Up_R": 1.0, + "Mouth_Pull_Lower_L": 1.0, + "Mouth_Pull_Lower_R": 1.0, + "Mouth_Pull_Upper_L": 1.0, + "Mouth_Pull_Upper_R": 1.0, + "Mouth_Push_Lower_L": 1.0, + "Mouth_Push_Lower_R": 1.0, + "Mouth_Push_Upper_L": 1.0, + "Mouth_Push_Upper_R": 1.0, + "Mouth_R": 1.0, + "Mouth_Roll_In_Lower_L": 1.0, + "Mouth_Roll_In_Lower_R": 1.0, + "Mouth_Roll_In_Upper_L": 1.0, + "Mouth_Roll_In_Upper_R": 1.0, + "Mouth_Roll_Out_Lower_L": 1.0, + "Mouth_Roll_Out_Lower_R": 1.0, + "Mouth_Roll_Out_Upper_L": 1.0, + "Mouth_Roll_Out_Upper_R": 1.0, + "Mouth_Shrug_Lower": 1.0, + "Mouth_Shrug_Upper": 1.0, + "Mouth_Smile_L": 1.0, + "Mouth_Smile_R": 1.0, + "Mouth_Smile_Sharp_L": 1.0, + "Mouth_Smile_Sharp_R": 1.0, + "Mouth_Stretch_L": 1.0, + "Mouth_Stretch_R": 1.0, + "Mouth_Tighten_L": 1.0, + "Mouth_Tighten_R": 1.0, + "Mouth_Up": 1.0, + "Mouth_Up_Upper_L": 1.0, + "Mouth_Up_Upper_R": 1.0, + "Mouth_Upper_L": 1.0, + "Mouth_Upper_R": 1.0, + "Neck_Swallow_Down": 1.0, + "Neck_Swallow_Up": 1.0, + "Neck_Tighten_L": 1.0, + "Neck_Tighten_R": 1.0, + "Nose_Crease_L": 1.0, + "Nose_Crease_R": 1.0, + "Nose_Nostril_Dilate_L": 1.0, + "Nose_Nostril_Dilate_R": 1.0, + "Nose_Nostril_Down_L": 1.0, + "Nose_Nostril_Down_R": 1.0, + "Nose_Nostril_In_L": 1.0, + "Nose_Nostril_In_R": 1.0, + "Nose_Nostril_Raise_L": 1.0, + "Nose_Nostril_Raise_R": 1.0, + "Nose_Sneer_L": 1.0, + "Nose_Sneer_R": 1.0, + "Nose_Tip_Down": 1.0, + "Nose_Tip_L": 1.0, + "Nose_Tip_R": 1.0, + "Nose_Tip_Up": 1.0, + "Tongue_Bulge_L": 1.0, + "Tongue_Bulge_R": 1.0, + "Tongue_Down": 1.0, + "Tongue_Enlarge": 1.0, + "Tongue_Extend": 1.0, + "Tongue_In": 1.0, + "Tongue_L": 1.0, + "Tongue_Mid_Up": 1.0, + "Tongue_Narrow": 1.0, + "Tongue_Out": 1.0, + "Tongue_R": 1.0, + "Tongue_Roll": 1.0, + "Tongue_Tip_Down": 1.0, + "Tongue_Tip_L": 1.0, + "Tongue_Tip_R": 1.0, + "Tongue_Tip_Up": 1.0, + "Tongue_Twist_L": 1.0, + "Tongue_Twist_R": 1.0, + "Tongue_Up": 1.0, + "Tongue_Wide": 1.0, + "Brow_Down_L": 1.0, + "Brow_Down_R": 1.0, + "Brow_Lateral_L": 1.0, + "Brow_Lateral_R": 1.0, + "Brow_Raise_In_L": 1.0, + "Brow_Raise_In_R": 1.0, + "C_BlinkL": 1.0, + "C_BlinkL_CheekRaiseL": 1.0, + "C_BlinkL_LookDownL": 1.0, + "C_BlinkL_LookLeftL": 1.0, + "C_BlinkL_LookRightL": 1.0, + "C_BlinkL_LookUpL": 1.0, + "C_BlinkL_SquintInnerL": 1.0, + "C_BlinkL_SquintInnerL_CheekRaiseL": 1.0, + "C_BlinkR": 1.0, + "C_BlinkR_CheekRaiseR": 1.0, + "C_BlinkR_LookDownR": 1.0, + "C_BlinkR_LookLeftR": 1.0, + "C_BlinkR_LookRightR": 1.0, + "C_BlinkR_LookUpR": 1.0, + "C_BlinkR_SquintInnerR": 1.0, + "C_BlinkR_SquintInnerR_CheekRaiseR": 1.0, + "C_CheekBlowL_CheekBlowR": 1.0, + "C_CheekRaiseL_CornerPullL": 1.0, + "C_CheekRaiseL_SquintInnerL": 1.0, + "C_CheekRaiseR_CornerPullR": 1.0, + "C_CheekRaiseR_SquintInnerR": 1.0, + "C_CornerPullL_CornerPullR": 1.0, + "C_CornerPullL_DimpleL": 1.0, + "C_CornerPullL_JawOpen": 1.0, + "C_CornerPullL_JawOpen_LowerLipDepressL": 1.0, + "C_CornerPullL_LowerLipDepressL": 1.0, + "C_CornerPullL_SharpCornerPullL": 1.0, + "C_CornerPullL_StretchL": 1.0, + "C_CornerPullL_StretchL_JawOpen": 1.0, + "C_CornerPullL_UpperLipRaiseL": 1.0, + "C_CornerPullRL_StretchRL_JawOpen": 1.0, + "C_CornerPullRL_StretchRL_JawOpen_LowerLipDepressRL": 1.0, + "C_CornerPullR_DimpleR": 1.0, + "C_CornerPullR_JawOpen": 1.0, + "C_CornerPullR_JawOpen_LowerLipDepressR": 1.0, + "C_CornerPullR_LowerLipDepressR": 1.0, + "C_CornerPullR_SharpCornerPullR": 1.0, + "C_CornerPullR_StretchR": 1.0, + "C_CornerPullR_StretchR_JawOpen": 1.0, + "C_CornerPullR_UpperLipRaiseR": 1.0, + "C_DimpleL_JawOpen": 1.0, + "C_DimpleL_LowerLipDepressL": 1.0, + "C_DimpleL_UpperLipRaiseL": 1.0, + "C_DimpleR_JawOpen": 1.0, + "C_DimpleR_LowerLipDepressR": 1.0, + "C_DimpleR_UpperLipRaiseR": 1.0, + "C_FunnelDL_CornerPullL": 1.0, + "C_FunnelDL_LipsPurseDL_CornerPullL": 1.0, + "C_FunnelDL_LipsTowardsDL": 1.0, + "C_FunnelDL_LowerLipDepressL": 1.0, + "C_FunnelDR_CornerPullR": 1.0, + "C_FunnelDR_LipsPurseDR_CornerPullR": 1.0, + "C_FunnelDR_LipsTowardsDR": 1.0, + "C_FunnelDR_LowerLipDepressR": 1.0, + "C_FunnelUL_ChinRaiseUL": 1.0, + "C_FunnelUL_CornerPullL": 1.0, + "C_FunnelUL_LipsPurseUL_CornerPullL": 1.0, + "C_FunnelUL_LipsTowardsUL": 1.0, + "C_FunnelUL_UpperLipRaiseL": 1.0, + "C_FunnelUR_ChinRaiseUR": 1.0, + "C_FunnelUR_CornerPullR": 1.0, + "C_FunnelUR_LipsPurseUR_CornerPullR": 1.0, + "C_FunnelUR_LipsTowardsUR": 1.0, + "C_FunnelUR_UpperLipRaiseR": 1.0, + "C_LipsPurseDL_CornerPullL": 1.0, + "C_LipsPurseDL_FunnelDL": 1.0, + "C_LipsPurseDL_JawOpen": 1.0, + "C_LipsPurseDL_LipsTogetherDL": 1.0, + "C_LipsPurseDL_LipsTowardsDL": 1.0, + "C_LipsPurseDL_LipsTowardsDL_FunnelDL": 1.0, + "C_LipsPurseDR_CornerPullR": 1.0, + "C_LipsPurseDR_FunnelDR": 1.0, + "C_LipsPurseDR_JawOpen": 1.0, + "C_LipsPurseDR_LipsTogetherDR": 1.0, + "C_LipsPurseDR_LipsTowardsDR": 1.0, + "C_LipsPurseDR_LipsTowardsDR_FunnelDR": 1.0, + "C_LipsPurseUL_CornerPullL": 1.0, + "C_LipsPurseUL_FunnelUL": 1.0, + "C_LipsPurseUL_JawOpen": 1.0, + "C_LipsPurseUL_LipsTogetherUL": 1.0, + "C_LipsPurseUL_LipsTowardsUL": 1.0, + "C_LipsPurseUL_LipsTowardsUL_FunnelUL": 1.0, + "C_LipsPurseUR_CornerPullR": 1.0, + "C_LipsPurseUR_FunnelUR": 1.0, + "C_LipsPurseUR_JawOpen": 1.0, + "C_LipsPurseUR_LipsTogetherUR": 1.0, + "C_LipsPurseUR_LipsTowardsUR": 1.0, + "C_LipsPurseUR_LipsTowardsUR_FunnelUR": 1.0, + "C_LowerLipBiteL_JawOpen": 1.0, + "C_LowerLipBiteR_JawOpen": 1.0, + "C_LowerLipDepressL_LowerLipDepressR": 1.0, + "C_NoseWrinkleL_BrowDownL": 1.0, + "C_NoseWrinkleR_BrowDownR": 1.0, + "C_PressDL_JawOpen": 1.0, + "C_PressDR_JawOpen": 1.0, + "C_PressUL_JawOpen": 1.0, + "C_PressUR_JawOpen": 1.0, + "C_StretchL_CornerPullL_LowerLipDepressL": 1.0, + "C_StretchL_JawOpen": 1.0, + "C_StretchL_LowerLipBite_L": 1.0, + "C_StretchL_LowerLipDepressL": 1.0, + "C_StretchL_LowerLipDepressL_JawOpen": 1.0, + "C_StretchL_StretchR": 1.0, + "C_StretchR_CornerPullR_LowerLipDepressR": 1.0, + "C_StretchR_JawOpen": 1.0, + "C_StretchR_LowerLipBite_R": 1.0, + "C_StretchR_LowerLipDepressR": 1.0, + "C_StretchR_LowerLipDepressR_JawOpen": 1.0, + "C_UpperLipBiteL_JawOpen": 1.0, + "C_UpperLipBiteR_JawOpen": 1.0, + "C_UpperLipRaiseL_UpperLipRaiseR": 1.0, + "C_WidenL_LookDownL": 1.0, + "C_WidenR_LookDownR": 1.0, + "C_WrinkleL_MouthCornerPullL": 1.0, + "C_WrinkleL_UpperLipRaiseL": 1.0, + "C_WrinkleR_MouthCornerPullR": 1.0, + "C_WrinkleR_UpperLipRaiseR": 1.0, + "Eye_Cheek_Raise_L": 1.0, + "Eye_Cheek_Raise_R": 1.0, + "Eye_Face_Scrunch_L": 1.0, + "Eye_Face_Scrunch_R": 1.0, + "Eye_Lid_Press_L": 1.0, + "Eye_Lid_Press_R": 1.0, + "Eye_Look_Down_L": 1.0, + "Eye_Look_Down_R": 1.0, + "Eye_Look_Left_L": 1.0, + "Eye_Look_Left_R": 1.0, + "Eye_Look_Right_L": 1.0, + "Eye_Look_Right_R": 1.0, + "Eye_Look_Up_L": 1.0, + "Eye_Look_Up_R": 1.0, + "Eye_LowerLid_Down_L": 1.0, + "Eye_LowerLid_Down_R": 1.0, + "Eye_LowerLid_Up_L": 1.0, + "Eye_LowerLid_Up_R": 1.0, + "Eye_Parallel_Look_Direction": 1.0, + "Eye_Pupil_Narrow_L": 1.0, + "Eye_Pupil_Narrow_R": 1.0, + "Eye_Pupil_Wide_L": 1.0, + "Eye_Pupil_Wide_R": 1.0, + "Eye_Relax_L": 1.0, + "Eye_Relax_R": 1.0, + "Eye_Squint_Inner_L": 1.0, + "Eye_Squint_Inner_R": 1.0, + "Eye_UpperLid_Up_L": 1.0, + "Eye_UpperLid_Up_R": 1.0, + "Eye_Widen_L": 1.0, + "Eye_Widen_R": 1.0, + "Eyelashes_Down_IN_L": 1.0, + "Eyelashes_Down_IN_R": 1.0, + "Eyelashes_Down_OUT_L": 1.0, + "Eyelashes_Down_OUT_R": 1.0, + "Eyelashes_Up_IN_L": 1.0, + "Eyelashes_Up_IN_R": 1.0, + "Eyelashes_Up_OUT_L": 1.0, + "Eyelashes_Up_OUT_R": 1.0, + "Jaw_Back": 1.0, + "Jaw_Chin_Compress_L": 1.0, + "Jaw_Chin_Compress_R": 1.0, + "Jaw_Chin_Raise_DL": 1.0, + "Jaw_Chin_Raise_DR": 1.0, + "Jaw_Chin_Raise_UL": 1.0, + "Jaw_Chin_Raise_UR": 1.0, + "Jaw_Clench_L": 1.0, + "Jaw_Clench_R": 1.0, + "Jaw_Fwd": 1.0, + "Jaw_Left": 1.0, + "Jaw_Open_Extreme": 1.0, + "Jaw_Right": 1.0, + "Mouth_Cheek_Blow_L": 1.0, + "Mouth_Cheek_Blow_R": 1.0, + "Mouth_Cheek_Suck_L": 1.0, + "Mouth_Cheek_Suck_R": 1.0, + "Mouth_Corner_Depress_L": 1.0, + "Mouth_Corner_Depress_R": 1.0, + "Mouth_Corner_Down_L": 1.0, + "Mouth_Corner_Down_R": 1.0, + "Mouth_Corner_Narrow_L": 1.0, + "Mouth_Corner_Narrow_R": 1.0, + "Mouth_Corner_Pull_L": 1.0, + "Mouth_Corner_Pull_R": 1.0, + "Mouth_Corner_Rounder_DL": 1.0, + "Mouth_Corner_Rounder_DR": 1.0, + "Mouth_Corner_Rounder_UL": 1.0, + "Mouth_Corner_Rounder_UR": 1.0, + "Mouth_Corner_Sharpen_DL": 1.0, + "Mouth_Corner_Sharpen_DR": 1.0, + "Mouth_Corner_Sharpen_UL": 1.0, + "Mouth_Corner_Sharpen_UR": 1.0, + "Mouth_Corner_Up_L": 1.0, + "Mouth_Corner_Up_R": 1.0, + "Mouth_Corner_Wide_L": 1.0, + "Mouth_Corner_Wide_R": 1.0, + "Mouth_Funnel_DL": 1.0, + "Mouth_Funnel_DR": 1.0, + "Mouth_Funnel_UL": 1.0, + "Mouth_Funnel_UR": 1.0, + "Mouth_Left": 1.0, + "Mouth_Lips_Blow_L": 1.0, + "Mouth_Lips_Blow_R": 1.0, + "Mouth_Lips_Press_L": 1.0, + "Mouth_Lips_Press_R": 1.0, + "Mouth_Lips_Pull_DL": 1.0, + "Mouth_Lips_Pull_DR": 1.0, + "Mouth_Lips_Pull_UL": 1.0, + "Mouth_Lips_Pull_UR": 1.0, + "Mouth_Lips_Purse_DL": 1.0, + "Mouth_Lips_Purse_DR": 1.0, + "Mouth_Lips_Purse_UL": 1.0, + "Mouth_Lips_Purse_UR": 1.0, + "Mouth_Lips_Push_DL": 1.0, + "Mouth_Lips_Push_DR": 1.0, + "Mouth_Lips_Push_UL": 1.0, + "Mouth_Lips_Push_UR": 1.0, + "Mouth_Lips_Sticky_L_Ph1": 1.0, + "Mouth_Lips_Sticky_L_Ph2": 1.0, + "Mouth_Lips_Sticky_L_Ph3": 1.0, + "Mouth_Lips_Sticky_R_Ph1": 1.0, + "Mouth_Lips_Sticky_R_Ph2": 1.0, + "Mouth_Lips_Sticky_R_Ph3": 1.0, + "Mouth_Lips_Thick_DL": 1.0, + "Mouth_Lips_Thick_DR": 1.0, + "Mouth_Lips_Thick_Inward_DL": 1.0, + "Mouth_Lips_Thick_Inward_DR": 1.0, + "Mouth_Lips_Thick_Inward_UL": 1.0, + "Mouth_Lips_Thick_Inward_UR": 1.0, + "Mouth_Lips_Thick_UL": 1.0, + "Mouth_Lips_Thick_UR": 1.0, + "Mouth_Lips_Thin_DL": 1.0, + "Mouth_Lips_Thin_DR": 1.0, + "Mouth_Lips_Thin_Inward_DL": 1.0, + "Mouth_Lips_Thin_Inward_DR": 1.0, + "Mouth_Lips_Thin_Inward_UL": 1.0, + "Mouth_Lips_Thin_Inward_UR": 1.0, + "Mouth_Lips_Thin_UL": 1.0, + "Mouth_Lips_Thin_UR": 1.0, + "Mouth_Lips_Tighten_DL": 1.0, + "Mouth_Lips_Tighten_DR": 1.0, + "Mouth_Lips_Tighten_UL": 1.0, + "Mouth_Lips_Tighten_UR": 1.0, + "Mouth_Lips_Together_DL": 1.0, + "Mouth_Lips_Together_DR": 1.0, + "Mouth_Lips_Together_UL": 1.0, + "Mouth_Lips_Together_UR": 1.0, + "Mouth_Lips_Towards_DL": 1.0, + "Mouth_Lips_Towards_DR": 1.0, + "Mouth_Lips_Towards_UL": 1.0, + "Mouth_Lips_Towards_UR": 1.0, + "Mouth_LowerLip_Bite_L": 1.0, + "Mouth_LowerLip_Bite_R": 1.0, + "Mouth_LowerLip_Depress_L": 1.0, + "Mouth_LowerLip_Depress_R": 1.0, + "Mouth_LowerLip_RollIn_L": 1.0, + "Mouth_LowerLip_RollIn_R": 1.0, + "Mouth_LowerLip_Roll_Out_L": 1.0, + "Mouth_LowerLip_Roll_Out_R": 1.0, + "Mouth_LowerLip_Shift_Left": 1.0, + "Mouth_LowerLip_Shift_Right": 1.0, + "Mouth_LowerLip_Towards_Teeth_L": 1.0, + "Mouth_LowerLip_Towards_Teeth_R": 1.0, + "Mouth_Mouth_Press_DL": 1.0, + "Mouth_Mouth_Press_DR": 1.0, + "Mouth_Mouth_Press_UL": 1.0, + "Mouth_Mouth_Press_UR": 1.0, + "Mouth_Right": 1.0, + "Mouth_SharpCorner_Pull_L": 1.0, + "Mouth_SharpCorner_Pull_R": 1.0, + "Mouth_Sticky_DC": 1.0, + "Mouth_Sticky_D_IN_L": 1.0, + "Mouth_Sticky_D_IN_R": 1.0, + "Mouth_Sticky_D_OUT_L": 1.0, + "Mouth_Sticky_D_OUT_R": 1.0, + "Mouth_Sticky_UC": 1.0, + "Mouth_Sticky_U_IN_L": 1.0, + "Mouth_Sticky_U_IN_R": 1.0, + "Mouth_Sticky_U_OUT_L": 1.0, + "Mouth_Sticky_U_OUT_R": 1.0, + "Mouth_StretchLips_Close_L": 1.0, + "Mouth_StretchLips_Close_R": 1.0, + "Mouth_UpperLip_Bite_L": 1.0, + "Mouth_UpperLip_Bite_R": 1.0, + "Mouth_UpperLip_Raise_L": 1.0, + "Mouth_UpperLip_Raise_R": 1.0, + "Mouth_UpperLip_RollIn_L": 1.0, + "Mouth_UpperLip_RollIn_R": 1.0, + "Mouth_UpperLip_Roll_Out_L": 1.0, + "Mouth_UpperLip_Roll_Out_R": 1.0, + "Mouth_UpperLip_Shift_Left": 1.0, + "Mouth_UpperLip_Shift_Right": 1.0, + "Mouth_UpperLip_Towards_Teeth_L": 1.0, + "Mouth_UpperLip_Towards_Teeth_R": 1.0, + "Neck_Digastric_Down": 1.0, + "Neck_Digastric_Up": 1.0, + "Neck_Mastoid_Contract_L": 1.0, + "Neck_Mastoid_Contract_R": 1.0, + "Neck_Stretch_L": 1.0, + "Neck_Stretch_R": 1.0, + "Neck_Swallow_Ph1": 1.0, + "Neck_Swallow_Ph2": 1.0, + "Neck_Swallow_Ph3": 1.0, + "Neck_Swallow_Ph4": 1.0, + "Neck_Swallow_Ph5": 1.0, + "Neck_Throat_Down": 1.0, + "Neck_Throat_Exhale": 1.0, + "Neck_Throat_Inhale": 1.0, + "Neck_Throat_Up": 1.0, + "Nose_Nasolabial_Deepen_L": 1.0, + "Nose_Nasolabial_Deepen_R": 1.0, + "Nose_Nostril_Compress_L": 1.0, + "Nose_Nostril_Compress_R": 1.0, + "Nose_Nostril_Depress_L": 1.0, + "Nose_Nostril_Depress_R": 1.0, + "Nose_Wrinkle_L": 1.0, + "Nose_Wrinkle_R": 1.0, + "Nose_Wrinkle_Upper_L": 1.0, + "Nose_Wrinkle_Upper_R": 1.0, + "Teeth_Back_D": 1.0, + "Teeth_Back_U": 1.0, + "Teeth_Down_D": 1.0, + "Teeth_Down_U": 1.0, + "Teeth_Fwd_D": 1.0, + "Teeth_Fwd_U": 1.0, + "Teeth_Left_D": 1.0, + "Teeth_Left_U": 1.0, + "Teeth_Right_D": 1.0, + "Teeth_Right_U": 1.0, + "Teeth_Up_D": 1.0, + "Teeth_Up_U": 1.0, + "Tongue_Bend_Down": 1.0, + "Tongue_Bend_Up": 1.0, + "Tongue_Left": 1.0, + "Tongue_Press": 1.0, + "Tongue_Right": 1.0, + "Tongue_Thick": 1.0, + "Tongue_Thin": 1.0, + "Tongue_Tip_Left": 1.0, + "Tongue_Tip_Right": 1.0, + "Tongue_Twist_Left": 1.0, + "Tongue_Twist_Right": 1.0, + "Brow_Drop_Left": 1.0, + "Brow_Drop_Right": 1.0, + "Brow_Raise_Inner_Left": 1.0, + "Brow_Raise_Inner_Right": 1.0, + "Brow_Raise_Left": 1.0, + "Brow_Raise_Outer_Left": 1.0, + "Brow_Raise_Outer_Right": 1.0, + "Brow_Raise_Right": 1.0, + "Cheek_Blow_L": 1.0, + "Cheek_Blow_R": 1.0, + "Cheeks_Suck": 1.0, + "Eyes_Blink": 1.0, + "Left_Eyeball_Look_Down": 1.0, + "Left_Eyeball_Look_L": 1.0, + "Left_Eyeball_Look_R": 1.0, + "Left_Eyeball_Look_Up": 1.0, + "Mouth_Blow": 1.0, + "Mouth_Bottom_Lip_Bite": 1.0, + "Mouth_Bottom_Lip_Down": 1.0, + "Mouth_Bottom_Lip_Trans": 1.0, + "Mouth_Bottom_Lip_Under": 1.0, + "Mouth_Frown": 1.0, + "Mouth_Lips_Jaw_Adjust": 1.0, + "Mouth_Lips_Open": 1.0, + "Mouth_Lips_Part": 1.0, + "Mouth_Lips_Tight": 1.0, + "Mouth_Lips_Tuck": 1.0, + "Mouth_Open": 1.0, + "Mouth_Plosive": 1.0, + "Mouth_Pucker": 1.0, + "Mouth_Pucker_Open": 1.0, + "Mouth_Skewer": 1.0, + "Mouth_Smile": 1.0, + "Mouth_Snarl_Lower_L": 1.0, + "Mouth_Snarl_Lower_R": 1.0, + "Mouth_Snarl_Upper_L": 1.0, + "Mouth_Snarl_Upper_R": 1.0, + "Mouth_Top_Lip_Under": 1.0, + "Mouth_Top_Lip_Up": 1.0, + "Mouth_Widen": 1.0, + "Mouth_Widen_Sides": 1.0, + "Move_Jaw_Down": 1.0, + "Move_Jaw_L": 1.0, + "Move_Jaw_R": 1.0, + "Nose_Flank_Raise_L": 1.0, + "Nose_Flank_Raise_R": 1.0, + "Nose_Flanks_Raise": 1.0, + "Nose_Nostrils_Flare": 1.0, + "Nose_Scrunch": 1.0, + "Right_Eyeball_Look_Down": 1.0, + "Right_Eyeball_Look_L": 1.0, + "Right_Eyeball_Look_R": 1.0, + "Right_Eyeball_Look_Up": 1.0, + "Turn_Jaw_Down": 1.0, + "Turn_Jaw_L": 1.0, + "Turn_Jaw_R": 1.0, + "A01_Brow_Inner_Up": 1.0, + "A02_Brow_Down_Left": 1.0, + "A03_Brow_Down_Right": 1.0, + "A04_Brow_Outer_Up_Left": 1.0, + "A05_Brow_Outer_Up_Right": 1.0, + "A06_Eye_Look_Up_Left": 1.0, + "A07_Eye_Look_Up_Right": 1.0, + "A08_Eye_Look_Down_Left": 1.0, + "A09_Eye_Look_Down_Right": 1.0, + "A10_Eye_Look_Out_Left": 1.0, + "A11_Eye_Look_In_Left": 1.0, + "A12_Eye_Look_In_Right": 1.0, + "A13_Eye_Look_Out_Right": 1.0, + "A14_Eye_Blink_Left": 1.0, + "A15_Eye_Blink_Right": 1.0, + "A16_Eye_Squint_Left": 1.0, + "A17_Eye_Squint_Right": 1.0, + "A18_Eye_Wide_Left": 1.0, + "A19_Eye_Wide_Right": 1.0, + "A20_Cheek_Puff": 1.0, + "A21_Cheek_Squint_Left": 1.0, + "A22_Cheek_Squint_Right": 1.0, + "A23_Nose_Sneer_Left": 1.0, + "A24_Nose_Sneer_Right": 1.0, + "A25_Jaw_Open": 1.0, + "A26_Jaw_Forward": 1.0, + "A27_Jaw_Left": 1.0, + "A28_Jaw_Right": 1.0, + "A29_Mouth_Funnel": 1.0, + "A30_Mouth_Pucker": 1.0, + "A31_Mouth_Left": 1.0, + "A32_Mouth_Right": 1.0, + "A33_Mouth_Roll_Upper": 1.0, + "A34_Mouth_Roll_Lower": 1.0, + "A35_Mouth_Shrug_Upper": 1.0, + "A36_Mouth_Shrug_Lower": 1.0, + "A37_Mouth_Close": 1.0, + "A38_Mouth_Smile_Left": 1.0, + "A39_Mouth_Smile_Right": 1.0, + "A40_Mouth_Frown_Left": 1.0, + "A41_Mouth_Frown_Right": 1.0, + "A42_Mouth_Dimple_Left": 1.0, + "A43_Mouth_Dimple_Right": 1.0, + "A44_Mouth_Upper_Up_Left": 1.0, + "A45_Mouth_Upper_Up_Right": 1.0, + "A46_Mouth_Lower_Down_Left": 1.0, + "A47_Mouth_Lower_Down_Right": 1.0, + "A48_Mouth_Press_Left": 1.0, + "A49_Mouth_Press_Right": 1.0, + "A50_Mouth_Stretch_Left": 1.0, + "A51_Mouth_Stretch_Right": 1.0, + "A52_Tongue_Out": 1.0, + "T01_Tongue_Up": 1.0, + "T02_Tongue_Down": 1.0, + "T03_Tongue_Left": 1.0, + "T04_Tongue_Right": 1.0, + "T05_Tongue_Roll": 1.0, + "T06_Tongue_Tip_Up": 1.0, + "T07_Tongue_Tip_Down": 1.0, + "T08_Tongue_Width": 1.0, + "T09_Tongue_Thickness": 1.0, + "T10_Tongue_Bulge_Left": 1.0, + "T11_Tongue_Bulge_Right": 1.0, + "OverBite": 1.0, + "Teeth Lower Depth": 1.0, + "Teeth Lower Height": 1.0, + "Teeth Lower Length": 1.0, + "Teeth Lower Scale Width": 1.0, + "Teeth Lower Sharp": 1.0, + "Teeth Upper Depth": 1.0, + "Teeth Upper Height": 1.0, + "Teeth Upper Length": 1.0, + "Teeth Upper Scale Width": 1.0, + "Teeth Upper Sharp": 1.0 + } +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Head.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Head.json new file mode 100644 index 0000000..40ced36 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Head.json @@ -0,0 +1,88 @@ +{ + "Bones": { + "CC_Base_NeckTwist01": 1.0, + "CC_Base_NeckTwist02": 1.0, + "CC_Base_Head": 1.0, + "CC_Base_FacialBone": 1.0, + "CC_Base_JawRoot": 1.0, + "CC_Base_Tongue01": 1.0, + "CC_Base_Tongue02": 1.0, + "CC_Base_Tongue03": 1.0, + "CC_Base_Teeth02": 1.0, + "CC_Base_R_Eye": 1.0, + "CC_Base_L_Eye": 1.0, + "CC_Base_UpperJaw": 1.0, + "CC_Base_Teeth01": 1.0, + "master_eye.L": 1.0, + "master_eye.R": 1.0, + "teeth.T": 1.0, + "jaw_master": 1.0, + "chin": 1.0, + "jaw": 1.0, + "teeth.B": 1.0, + "tongue.003": 1.0, + "tongue_master": 1.0, + "tongue": 1.0, + "tongue.001": 1.0, + "tongue.002": 1.0, + "neck": 1.0, + "head": 1.0, + "tweak_spine.005": 1.0, + "eyes": 1.0, + "eye.L": 1.0, + "eye.R": 1.0, + "CTRL_C_head_turn": 1.0, + "CTRL_C_head_move": 1.0, + "CTRL_C_head_tilt": 1.0, + "CTRL_C_head_fwdBack": 1.0, + "CTRL_C_eye": 1.0, + "CTRL_L_eye": 1.0, + "CTRL_R_eye": 1.0, + "CTRL_C_eye_parallelLook": 1.0, + "neck_01": 1.0 + }, + "Keys": { + "Head_Backward": 1.0, + "Head_Forward": 1.0, + "Head_L": 1.0, + "Head_R": 1.0, + "Head_Tilt_L": 1.0, + "Head_Tilt_R": 1.0, + "Head_Turn_Down": 1.0, + "Head_Turn_L": 1.0, + "Head_Turn_R": 1.0, + "Head_Turn_Up": 1.0, + "Eye_Look_Down_L": 1.0, + "Eye_Look_Down_R": 1.0, + "Eye_Look_Left_L": 1.0, + "Eye_Look_Left_R": 1.0, + "Eye_Look_Right_L": 1.0, + "Eye_Look_Right_R": 1.0, + "Eye_Look_Up_L": 1.0, + "Eye_Look_Up_R": 1.0, + "Eye_L_Look_Down": 1.0, + "Eye_L_Look_L": 1.0, + "Eye_L_Look_R": 1.0, + "Eye_L_Look_Up": 1.0, + "Eye_R_Look_Down": 1.0, + "Eye_R_Look_L": 1.0, + "Eye_R_Look_R": 1.0, + "Eye_R_Look_Up": 1.0, + "A06_Eye_Look_Up_Left": 1.0, + "A07_Eye_Look_Up_Right": 1.0, + "A08_Eye_Look_Down_Left": 1.0, + "A09_Eye_Look_Down_Right": 1.0, + "A10_Eye_Look_Out_Left": 1.0, + "A11_Eye_Look_In_Left": 1.0, + "A12_Eye_Look_In_Right": 1.0, + "A13_Eye_Look_Out_Right": 1.0, + "Left_Eyeball_Look_Down": 1.0, + "Left_Eyeball_Look_L": 1.0, + "Left_Eyeball_Look_R": 1.0, + "Left_Eyeball_Look_Up": 1.0, + "Right_Eyeball_Look_Down": 1.0, + "Right_Eyeball_Look_L": 1.0, + "Right_Eyeball_Look_R": 1.0, + "Right_Eyeball_Look_Up": 1.0 + } +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Left Arm.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Left Arm.json new file mode 100644 index 0000000..f212e7d --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Left Arm.json @@ -0,0 +1,88 @@ +{ + "Bones": { + "CC_Base_L_Clavicle": 1.0, + "CC_Base_L_Upperarm": 1.0, + "CC_Base_L_Forearm": 1.0, + "CC_Base_L_ForearmTwist01": 1.0, + "CC_Base_L_ForearmTwist02": 1.0, + "CC_Base_L_ElbowShareBone": 1.0, + "CC_Base_L_Hand": 1.0, + "CC_Base_L_Pinky1": 1.0, + "CC_Base_L_Pinky2": 1.0, + "CC_Base_L_Pinky3": 1.0, + "CC_Base_L_Ring1": 1.0, + "CC_Base_L_Ring2": 1.0, + "CC_Base_L_Ring3": 1.0, + "CC_Base_L_Mid1": 1.0, + "CC_Base_L_Mid2": 1.0, + "CC_Base_L_Mid3": 1.0, + "CC_Base_L_Index1": 1.0, + "CC_Base_L_Index2": 1.0, + "CC_Base_L_Index3": 1.0, + "CC_Base_L_Thumb1": 1.0, + "CC_Base_L_Thumb2": 1.0, + "CC_Base_L_Thumb3": 1.0, + "CC_Base_L_UpperarmTwist01": 1.0, + "CC_Base_L_UpperarmTwist02": 1.0, + "hand_tweak.L": 1.0, + "forearm_tweak.L": 1.0, + "forearm_tweak.L.001": 1.0, + "upper_arm_tweak.L.001": 1.0, + "upper_arm_parent.L": 1.0, + "upper_arm_fk.L": 1.0, + "forearm_fk.L": 1.0, + "hand_fk.L": 1.0, + "upper_arm_ik.L": 1.0, + "upper_arm_tweak.L": 1.0, + "f_index.01_master.L": 1.0, + "thumb.01_master.L": 1.0, + "f_index.01.L": 1.0, + "f_index.02.L": 1.0, + "f_index.03.L": 1.0, + "f_index.01.L.001": 1.0, + "thumb.01.L": 1.0, + "thumb.02.L": 1.0, + "thumb.03.L": 1.0, + "thumb.01.L.001": 1.0, + "f_middle.01_master.L": 1.0, + "f_middle.01.L": 1.0, + "f_middle.02.L": 1.0, + "f_middle.03.L": 1.0, + "f_middle.01.L.001": 1.0, + "f_ring.01_master.L": 1.0, + "f_ring.01.L": 1.0, + "f_ring.02.L": 1.0, + "f_ring.03.L": 1.0, + "f_ring.01.L.001": 1.0, + "f_pinky.01_master.L": 1.0, + "f_pinky.01.L": 1.0, + "f_pinky.02.L": 1.0, + "f_pinky.03.L": 1.0, + "f_pinky.01.L.001": 1.0, + "palm.L": 1.0, + "hand_ik.L": 1.0, + "upper_arm_ik_target.L": 1.0, + "clavicle_l": 1.0, + "upperarm_l": 1.0, + "upperarm_twist_01_l": 1.0, + "lowerarm_l": 1.0, + "lowerarm_twist_01_l": 1.0, + "hand_l": 1.0, + "middle_01_l": 1.0, + "middle_02_l": 1.0, + "middle_03_l": 1.0, + "index_01_l": 1.0, + "index_02_l": 1.0, + "index_03_l": 1.0, + "ring_01_l": 1.0, + "ring_02_l": 1.0, + "ring_03_l": 1.0, + "pinky_01_l": 1.0, + "pinky_02_l": 1.0, + "pinky_03_l": 1.0, + "thumb_01_l": 1.0, + "thumb_02_l": 1.0, + "thumb_03_l": 1.0 + }, + "Keys": {} +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Left Hand.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Left Hand.json new file mode 100644 index 0000000..4f63fd8 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Left Hand.json @@ -0,0 +1,67 @@ +{ + "Bones": { + "CC_Base_L_Hand": 1.0, + "CC_Base_L_Pinky1": 1.0, + "CC_Base_L_Pinky2": 1.0, + "CC_Base_L_Pinky3": 1.0, + "CC_Base_L_Ring1": 1.0, + "CC_Base_L_Ring2": 1.0, + "CC_Base_L_Ring3": 1.0, + "CC_Base_L_Mid1": 1.0, + "CC_Base_L_Mid2": 1.0, + "CC_Base_L_Mid3": 1.0, + "CC_Base_L_Index1": 1.0, + "CC_Base_L_Index2": 1.0, + "CC_Base_L_Index3": 1.0, + "CC_Base_L_Thumb1": 1.0, + "CC_Base_L_Thumb2": 1.0, + "CC_Base_L_Thumb3": 1.0, + "hand_tweak.L": 1.0, + "hand_fk.L": 1.0, + "f_index.01_master.L": 1.0, + "thumb.01_master.L": 1.0, + "f_index.01.L": 1.0, + "f_index.02.L": 1.0, + "f_index.03.L": 1.0, + "f_index.01.L.001": 1.0, + "thumb.01.L": 1.0, + "thumb.02.L": 1.0, + "thumb.03.L": 1.0, + "thumb.01.L.001": 1.0, + "f_middle.01_master.L": 1.0, + "f_middle.01.L": 1.0, + "f_middle.02.L": 1.0, + "f_middle.03.L": 1.0, + "f_middle.01.L.001": 1.0, + "f_ring.01_master.L": 1.0, + "f_ring.01.L": 1.0, + "f_ring.02.L": 1.0, + "f_ring.03.L": 1.0, + "f_ring.01.L.001": 1.0, + "f_pinky.01_master.L": 1.0, + "f_pinky.01.L": 1.0, + "f_pinky.02.L": 1.0, + "f_pinky.03.L": 1.0, + "f_pinky.01.L.001": 1.0, + "palm.L": 1.0, + "hand_ik.L": 1.0, + "upper_arm_ik_target.L": 1.0, + "hand_l": 1.0, + "middle_01_l": 1.0, + "middle_02_l": 1.0, + "middle_03_l": 1.0, + "index_01_l": 1.0, + "index_02_l": 1.0, + "index_03_l": 1.0, + "ring_01_l": 1.0, + "ring_02_l": 1.0, + "ring_03_l": 1.0, + "pinky_01_l": 1.0, + "pinky_02_l": 1.0, + "pinky_03_l": 1.0, + "thumb_01_l": 1.0, + "thumb_02_l": 1.0, + "thumb_03_l": 1.0 + }, + "Keys": {} +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Lower Body.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Lower Body.json new file mode 100644 index 0000000..59a2b4c --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Lower Body.json @@ -0,0 +1,88 @@ +{ + "Bones": { + "CC_Base_BoneRoot": 1.0, + "CC_Base_Hip": 1.0, + "CC_Base_Pelvis": 1.0, + "CC_Base_L_Thigh": 1.0, + "CC_Base_L_Calf": 1.0, + "CC_Base_L_Foot": 1.0, + "CC_Base_L_ToeBaseShareBone": 1.0, + "CC_Base_L_ToeBase": 1.0, + "CC_Base_L_PinkyToe1": 1.0, + "CC_Base_L_RingToe1": 1.0, + "CC_Base_L_MidToe1": 1.0, + "CC_Base_L_IndexToe1": 1.0, + "CC_Base_L_BigToe1": 1.0, + "CC_Base_L_CalfTwist01": 1.0, + "CC_Base_L_CalfTwist02": 1.0, + "CC_Base_L_KneeShareBone": 1.0, + "CC_Base_L_ThighTwist01": 1.0, + "CC_Base_L_ThighTwist02": 1.0, + "CC_Base_R_Thigh": 1.0, + "CC_Base_R_Calf": 1.0, + "CC_Base_R_KneeShareBone": 1.0, + "CC_Base_R_Foot": 1.0, + "CC_Base_R_ToeBase": 1.0, + "CC_Base_R_BigToe1": 1.0, + "CC_Base_R_PinkyToe1": 1.0, + "CC_Base_R_RingToe1": 1.0, + "CC_Base_R_IndexToe1": 1.0, + "CC_Base_R_MidToe1": 1.0, + "CC_Base_R_ToeBaseShareBone": 1.0, + "CC_Base_R_CalfTwist01": 1.0, + "CC_Base_R_CalfTwist02": 1.0, + "CC_Base_R_ThighTwist01": 1.0, + "CC_Base_R_ThighTwist02": 1.0, + "CC_Base_Waist": 1.0, + "root": 1.0, + "hips": 1.0, + "torso": 1.0, + "foot_tweak.L": 1.0, + "shin_tweak.L": 1.0, + "shin_tweak.L.001": 1.0, + "thigh_tweak.L.001": 1.0, + "foot_tweak.R": 1.0, + "shin_tweak.R": 1.0, + "shin_tweak.R.001": 1.0, + "thigh_tweak.R.001": 1.0, + "thigh_parent.L": 1.0, + "thigh_fk.L": 1.0, + "shin_fk.L": 1.0, + "foot_fk.L": 1.0, + "toe_fk.L": 1.0, + "thigh_ik.L": 1.0, + "thigh_tweak.L": 1.0, + "thigh_parent.R": 1.0, + "thigh_fk.R": 1.0, + "shin_fk.R": 1.0, + "foot_fk.R": 1.0, + "toe_fk.R": 1.0, + "thigh_ik.R": 1.0, + "thigh_tweak.R": 1.0, + "foot_ik.L": 1.0, + "foot_spin_ik.L": 1.0, + "foot_heel_ik.L": 1.0, + "toe_ik.L": 1.0, + "thigh_ik_target.L": 1.0, + "foot_ik.R": 1.0, + "foot_spin_ik.R": 1.0, + "foot_heel_ik.R": 1.0, + "toe_ik.R": 1.0, + "thigh_ik_target.R": 1.0, + "RL_BoneRoot": 1.0, + "pelvis": 1.0, + "thigh_l": 1.0, + "thigh_twist_01_l": 1.0, + "calf_l": 1.0, + "calf_twist_01_l": 1.0, + "foot_l": 1.0, + "ball_l": 1.0, + "thigh_r": 1.0, + "thigh_twist_01_r": 1.0, + "calf_r": 1.0, + "calf_twist_01_r": 1.0, + "foot_r": 1.0, + "ball_r": 1.0 + }, + "Keys": {} +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Right Arm.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Right Arm.json new file mode 100644 index 0000000..0fc8c62 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Right Arm.json @@ -0,0 +1,89 @@ +{ + "Bones": { + "CC_Base_R_Clavicle": 1.0, + "CC_Base_R_Upperarm": 1.0, + "CC_Base_R_Forearm": 1.0, + "CC_Base_R_ElbowShareBone": 1.0, + "CC_Base_R_ForearmTwist01": 1.0, + "CC_Base_R_ForearmTwist02": 1.0, + "CC_Base_R_Hand": 1.0, + "CC_Base_R_Ring1": 1.0, + "CC_Base_R_Ring2": 1.0, + "CC_Base_R_Ring3": 1.0, + "CC_Base_R_Mid1": 1.0, + "CC_Base_R_Mid2": 1.0, + "CC_Base_R_Mid3": 1.0, + "CC_Base_R_Thumb1": 1.0, + "CC_Base_R_Thumb2": 1.0, + "CC_Base_R_Thumb3": 1.0, + "CC_Base_R_Index1": 1.0, + "CC_Base_R_Index2": 1.0, + "CC_Base_R_Index3": 1.0, + "CC_Base_R_Pinky1": 1.0, + "CC_Base_R_Pinky2": 1.0, + "CC_Base_R_Pinky3": 1.0, + "CC_Base_R_UpperarmTwist01": 1.0, + "CC_Base_R_UpperarmTwist02": 1.0, + "hand_tweak.R": 1.0, + "forearm_tweak.R": 1.0, + "forearm_tweak.R.001": 1.0, + "upper_arm_tweak.R.001": 1.0, + "upper_arm_parent.R": 1.0, + "upper_arm_fk.R": 1.0, + "forearm_fk.R": 1.0, + "hand_fk.R": 1.0, + "upper_arm_ik.R": 1.0, + "upper_arm_tweak.R": 1.0, + "f_index.01_master.R": 1.0, + "thumb.01_master.R": 1.0, + "f_index.01.R": 1.0, + "f_index.02.R": 1.0, + "f_index.03.R": 1.0, + "f_index.01.R.001": 1.0, + "thumb.01.R": 1.0, + "thumb.02.R": 1.0, + "thumb.03.R": 1.0, + "thumb.01.R.001": 1.0, + "f_middle.01_master.R": 1.0, + "f_middle.01.R": 1.0, + "f_middle.02.R": 1.0, + "f_middle.03.R": 1.0, + "f_middle.01.R.001": 1.0, + "f_ring.01_master.R": 1.0, + "f_ring.01.R": 1.0, + "f_ring.02.R": 1.0, + "f_ring.03.R": 1.0, + "f_ring.01.R.001": 1.0, + "f_pinky.01_master.R": 1.0, + "f_pinky.01.R": 1.0, + "f_pinky.02.R": 1.0, + "f_pinky.03.R": 1.0, + "f_pinky.01.R.001": 1.0, + "palm.R": 1.0, + "shoulder.R": 1.0, + "hand_ik.R": 1.0, + "upper_arm_ik_target.R": 1.0, + "clavicle_r": 1.0, + "upperarm_r": 1.0, + "upperarm_twist_01_r": 1.0, + "lowerarm_r": 1.0, + "lowerarm_twist_01_r": 1.0, + "hand_r": 1.0, + "middle_01_r": 1.0, + "middle_02_r": 1.0, + "middle_03_r": 1.0, + "ring_01_r": 1.0, + "ring_02_r": 1.0, + "ring_03_r": 1.0, + "thumb_01_r": 1.0, + "thumb_02_r": 1.0, + "thumb_03_r": 1.0, + "index_01_r": 1.0, + "index_02_r": 1.0, + "index_03_r": 1.0, + "pinky_01_r": 1.0, + "pinky_02_r": 1.0, + "pinky_03_r": 1.0 + }, + "Keys": {} +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Right Hand.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Right Hand.json new file mode 100644 index 0000000..9662869 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Right Hand.json @@ -0,0 +1,67 @@ +{ + "Bones": { + "CC_Base_R_Hand": 1.0, + "CC_Base_R_Ring1": 1.0, + "CC_Base_R_Ring2": 1.0, + "CC_Base_R_Ring3": 1.0, + "CC_Base_R_Mid1": 1.0, + "CC_Base_R_Mid2": 1.0, + "CC_Base_R_Mid3": 1.0, + "CC_Base_R_Thumb1": 1.0, + "CC_Base_R_Thumb2": 1.0, + "CC_Base_R_Thumb3": 1.0, + "CC_Base_R_Index1": 1.0, + "CC_Base_R_Index2": 1.0, + "CC_Base_R_Index3": 1.0, + "CC_Base_R_Pinky1": 1.0, + "CC_Base_R_Pinky2": 1.0, + "CC_Base_R_Pinky3": 1.0, + "hand_tweak.R": 1.0, + "hand_fk.R": 1.0, + "f_index.01_master.R": 1.0, + "thumb.01_master.R": 1.0, + "f_index.01.R": 1.0, + "f_index.02.R": 1.0, + "f_index.03.R": 1.0, + "f_index.01.R.001": 1.0, + "thumb.01.R": 1.0, + "thumb.02.R": 1.0, + "thumb.03.R": 1.0, + "thumb.01.R.001": 1.0, + "f_middle.01_master.R": 1.0, + "f_middle.01.R": 1.0, + "f_middle.02.R": 1.0, + "f_middle.03.R": 1.0, + "f_middle.01.R.001": 1.0, + "f_ring.01_master.R": 1.0, + "f_ring.01.R": 1.0, + "f_ring.02.R": 1.0, + "f_ring.03.R": 1.0, + "f_ring.01.R.001": 1.0, + "f_pinky.01_master.R": 1.0, + "f_pinky.01.R": 1.0, + "f_pinky.02.R": 1.0, + "f_pinky.03.R": 1.0, + "f_pinky.01.R.001": 1.0, + "palm.R": 1.0, + "hand_ik.R": 1.0, + "upper_arm_ik_target.R": 1.0, + "hand_r": 1.0, + "middle_01_r": 1.0, + "middle_02_r": 1.0, + "middle_03_r": 1.0, + "ring_01_r": 1.0, + "ring_02_r": 1.0, + "ring_03_r": 1.0, + "thumb_01_r": 1.0, + "thumb_02_r": 1.0, + "thumb_03_r": 1.0, + "index_01_r": 1.0, + "index_02_r": 1.0, + "index_03_r": 1.0, + "pinky_01_r": 1.0, + "pinky_02_r": 1.0, + "pinky_03_r": 1.0 + }, + "Keys": {} +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Upper Body.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Upper Body.json new file mode 100644 index 0000000..cab0a61 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Upper Body.json @@ -0,0 +1,278 @@ +{ + "Bones": { + "CC_Base_Spine01": 1.0, + "CC_Base_Spine02": 1.0, + "CC_Base_NeckTwist01": 1.0, + "CC_Base_NeckTwist02": 1.0, + "CC_Base_Head": 1.0, + "CC_Base_FacialBone": 1.0, + "CC_Base_JawRoot": 1.0, + "CC_Base_Tongue01": 1.0, + "CC_Base_Tongue02": 1.0, + "CC_Base_Tongue03": 1.0, + "CC_Base_Teeth02": 1.0, + "CC_Base_R_Eye": 1.0, + "CC_Base_L_Eye": 1.0, + "CC_Base_UpperJaw": 1.0, + "CC_Base_Teeth01": 1.0, + "CC_Base_L_Clavicle": 1.0, + "CC_Base_L_Upperarm": 1.0, + "CC_Base_L_Forearm": 1.0, + "CC_Base_L_ForearmTwist01": 1.0, + "CC_Base_L_ForearmTwist02": 1.0, + "CC_Base_L_ElbowShareBone": 1.0, + "CC_Base_L_Hand": 1.0, + "CC_Base_L_Pinky1": 1.0, + "CC_Base_L_Pinky2": 1.0, + "CC_Base_L_Pinky3": 1.0, + "CC_Base_L_Ring1": 1.0, + "CC_Base_L_Ring2": 1.0, + "CC_Base_L_Ring3": 1.0, + "CC_Base_L_Mid1": 1.0, + "CC_Base_L_Mid2": 1.0, + "CC_Base_L_Mid3": 1.0, + "CC_Base_L_Index1": 1.0, + "CC_Base_L_Index2": 1.0, + "CC_Base_L_Index3": 1.0, + "CC_Base_L_Thumb1": 1.0, + "CC_Base_L_Thumb2": 1.0, + "CC_Base_L_Thumb3": 1.0, + "CC_Base_L_UpperarmTwist01": 1.0, + "CC_Base_L_UpperarmTwist02": 1.0, + "CC_Base_L_RibsTwist": 1.0, + "CC_Base_L_Breast": 1.0, + "CC_Base_R_RibsTwist": 1.0, + "CC_Base_R_Breast": 1.0, + "CC_Base_R_Clavicle": 1.0, + "CC_Base_R_Upperarm": 1.0, + "CC_Base_R_Forearm": 1.0, + "CC_Base_R_ElbowShareBone": 1.0, + "CC_Base_R_ForearmTwist01": 1.0, + "CC_Base_R_ForearmTwist02": 1.0, + "CC_Base_R_Hand": 1.0, + "CC_Base_R_Ring1": 1.0, + "CC_Base_R_Ring2": 1.0, + "CC_Base_R_Ring3": 1.0, + "CC_Base_R_Mid1": 1.0, + "CC_Base_R_Mid2": 1.0, + "CC_Base_R_Mid3": 1.0, + "CC_Base_R_Thumb1": 1.0, + "CC_Base_R_Thumb2": 1.0, + "CC_Base_R_Thumb3": 1.0, + "CC_Base_R_Index1": 1.0, + "CC_Base_R_Index2": 1.0, + "CC_Base_R_Index3": 1.0, + "CC_Base_R_Pinky1": 1.0, + "CC_Base_R_Pinky2": 1.0, + "CC_Base_R_Pinky3": 1.0, + "CC_Base_R_UpperarmTwist01": 1.0, + "CC_Base_R_UpperarmTwist02": 1.0, + "torso": 1.0, + "chest": 1.0, + "spine_fk.001": 1.0, + "spine_fk": 1.0, + "tweak_spine": 1.0, + "tweak_spine.001": 1.0, + "spine_fk.002": 1.0, + "tweak_spine.002": 1.0, + "spine_fk.003": 1.0, + "master_eye.L": 1.0, + "master_eye.R": 1.0, + "teeth.T": 1.0, + "jaw_master": 1.0, + "chin": 1.0, + "jaw": 1.0, + "teeth.B": 1.0, + "tongue.003": 1.0, + "tongue_master": 1.0, + "tongue": 1.0, + "tongue.001": 1.0, + "tongue.002": 1.0, + "neck": 1.0, + "head": 1.0, + "tweak_spine.004": 1.0, + "tweak_spine.005": 1.0, + "tweak_spine.003": 1.0, + "hand_tweak.L": 1.0, + "forearm_tweak.L": 1.0, + "forearm_tweak.L.001": 1.0, + "upper_arm_tweak.L.001": 1.0, + "upper_arm_parent.L": 1.0, + "upper_arm_fk.L": 1.0, + "forearm_fk.L": 1.0, + "hand_fk.L": 1.0, + "upper_arm_ik.L": 1.0, + "upper_arm_tweak.L": 1.0, + "f_index.01_master.L": 1.0, + "thumb.01_master.L": 1.0, + "f_index.01.L": 1.0, + "f_index.02.L": 1.0, + "f_index.03.L": 1.0, + "f_index.01.L.001": 1.0, + "thumb.01.L": 1.0, + "thumb.02.L": 1.0, + "thumb.03.L": 1.0, + "thumb.01.L.001": 1.0, + "f_middle.01_master.L": 1.0, + "f_middle.01.L": 1.0, + "f_middle.02.L": 1.0, + "f_middle.03.L": 1.0, + "f_middle.01.L.001": 1.0, + "f_ring.01_master.L": 1.0, + "f_ring.01.L": 1.0, + "f_ring.02.L": 1.0, + "f_ring.03.L": 1.0, + "f_ring.01.L.001": 1.0, + "f_pinky.01_master.L": 1.0, + "f_pinky.01.L": 1.0, + "f_pinky.02.L": 1.0, + "f_pinky.03.L": 1.0, + "f_pinky.01.L.001": 1.0, + "palm.L": 1.0, + "hand_tweak.R": 1.0, + "forearm_tweak.R": 1.0, + "forearm_tweak.R.001": 1.0, + "upper_arm_tweak.R.001": 1.0, + "upper_arm_parent.R": 1.0, + "upper_arm_fk.R": 1.0, + "forearm_fk.R": 1.0, + "hand_fk.R": 1.0, + "upper_arm_ik.R": 1.0, + "upper_arm_tweak.R": 1.0, + "f_index.01_master.R": 1.0, + "thumb.01_master.R": 1.0, + "f_index.01.R": 1.0, + "f_index.02.R": 1.0, + "f_index.03.R": 1.0, + "f_index.01.R.001": 1.0, + "thumb.01.R": 1.0, + "thumb.02.R": 1.0, + "thumb.03.R": 1.0, + "thumb.01.R.001": 1.0, + "f_middle.01_master.R": 1.0, + "f_middle.01.R": 1.0, + "f_middle.02.R": 1.0, + "f_middle.03.R": 1.0, + "f_middle.01.R.001": 1.0, + "f_ring.01_master.R": 1.0, + "f_ring.01.R": 1.0, + "f_ring.02.R": 1.0, + "f_ring.03.R": 1.0, + "f_ring.01.R.001": 1.0, + "f_pinky.01_master.R": 1.0, + "f_pinky.01.R": 1.0, + "f_pinky.02.R": 1.0, + "f_pinky.03.R": 1.0, + "f_pinky.01.R.001": 1.0, + "palm.R": 1.0, + "breast.L": 1.0, + "breast.R": 1.0, + "shoulder.L": 1.0, + "shoulder.R": 1.0, + "hand_ik.L": 1.0, + "upper_arm_ik_target.L": 1.0, + "hand_ik.R": 1.0, + "upper_arm_ik_target.R": 1.0, + "eyes": 1.0, + "eye.L": 1.0, + "eye.R": 1.0, + "CTRL_C_head_turn": 1.0, + "CTRL_C_head_move": 1.0, + "CTRL_C_head_tilt": 1.0, + "CTRL_C_head_fwdBack": 1.0, + "CTRL_C_eye": 1.0, + "CTRL_L_eye": 1.0, + "CTRL_R_eye": 1.0, + "CTRL_C_eye_parallelLook": 1.0, + "spine_01": 1.0, + "spine_02": 1.0, + "spine_03": 1.0, + "clavicle_l": 1.0, + "upperarm_l": 1.0, + "upperarm_twist_01_l": 1.0, + "lowerarm_l": 1.0, + "lowerarm_twist_01_l": 1.0, + "hand_l": 1.0, + "middle_01_l": 1.0, + "middle_02_l": 1.0, + "middle_03_l": 1.0, + "index_01_l": 1.0, + "index_02_l": 1.0, + "index_03_l": 1.0, + "ring_01_l": 1.0, + "ring_02_l": 1.0, + "ring_03_l": 1.0, + "pinky_01_l": 1.0, + "pinky_02_l": 1.0, + "pinky_03_l": 1.0, + "thumb_01_l": 1.0, + "thumb_02_l": 1.0, + "thumb_03_l": 1.0, + "clavicle_r": 1.0, + "upperarm_r": 1.0, + "upperarm_twist_01_r": 1.0, + "lowerarm_r": 1.0, + "lowerarm_twist_01_r": 1.0, + "hand_r": 1.0, + "middle_01_r": 1.0, + "middle_02_r": 1.0, + "middle_03_r": 1.0, + "ring_01_r": 1.0, + "ring_02_r": 1.0, + "ring_03_r": 1.0, + "thumb_01_r": 1.0, + "thumb_02_r": 1.0, + "thumb_03_r": 1.0, + "index_01_r": 1.0, + "index_02_r": 1.0, + "index_03_r": 1.0, + "pinky_01_r": 1.0, + "pinky_02_r": 1.0, + "pinky_03_r": 1.0, + "neck_01": 1.0 + }, + "Keys": { + "Head_Backward": 1.0, + "Head_Forward": 1.0, + "Head_L": 1.0, + "Head_R": 1.0, + "Head_Tilt_L": 1.0, + "Head_Tilt_R": 1.0, + "Head_Turn_Down": 1.0, + "Head_Turn_L": 1.0, + "Head_Turn_R": 1.0, + "Head_Turn_Up": 1.0, + "Eye_Look_Down_L": 1.0, + "Eye_Look_Down_R": 1.0, + "Eye_Look_Left_L": 1.0, + "Eye_Look_Left_R": 1.0, + "Eye_Look_Right_L": 1.0, + "Eye_Look_Right_R": 1.0, + "Eye_Look_Up_L": 1.0, + "Eye_Look_Up_R": 1.0, + "Eye_L_Look_Down": 1.0, + "Eye_L_Look_L": 1.0, + "Eye_L_Look_R": 1.0, + "Eye_L_Look_Up": 1.0, + "Eye_R_Look_Down": 1.0, + "Eye_R_Look_L": 1.0, + "Eye_R_Look_R": 1.0, + "Eye_R_Look_Up": 1.0, + "A06_Eye_Look_Up_Left": 1.0, + "A07_Eye_Look_Up_Right": 1.0, + "A08_Eye_Look_Down_Left": 1.0, + "A09_Eye_Look_Down_Right": 1.0, + "A10_Eye_Look_Out_Left": 1.0, + "A11_Eye_Look_In_Left": 1.0, + "A12_Eye_Look_In_Right": 1.0, + "A13_Eye_Look_Out_Right": 1.0, + "Left_Eyeball_Look_Down": 1.0, + "Left_Eyeball_Look_L": 1.0, + "Left_Eyeball_Look_R": 1.0, + "Left_Eyeball_Look_Up": 1.0, + "Right_Eyeball_Look_Down": 1.0, + "Right_Eyeball_Look_L": 1.0, + "Right_Eyeball_Look_R": 1.0, + "Right_Eyeball_Look_Up": 1.0 + } +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Visemes.json b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Visemes.json new file mode 100644 index 0000000..86980c5 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/Blend Presets/Visemes.json @@ -0,0 +1,36 @@ +{ + "Bones": {}, + "Keys": { + "AE": 1.0, + "Ah": 1.0, + "B_M_P": 1.0, + "Ch_J": 1.0, + "EE": 1.0, + "Er": 1.0, + "F_V": 1.0, + "IH": 1.0, + "K_G_H_NG": 1.0, + "Oh": 1.0, + "R": 1.0, + "S_Z": 1.0, + "TH": 1.0, + "W_OO": 1.0, + "T_L_D_N": 1.0, + "V_Wide": 1.0, + "V_Tongue_up": 1.0, + "V_Tongue_Raise": 1.0, + "V_Tongue_Out": 1.0, + "V_Tongue_Narrow": 1.0, + "V_Tongue_Lower": 1.0, + "V_Tongue_Curl_U": 1.0, + "V_Tongue_Curl_D": 1.0, + "V_Tight_O": 1.0, + "V_Tight": 1.0, + "V_Open": 1.0, + "V_None": 1.0, + "V_Lip_Open": 1.0, + "V_Explosive": 1.0, + "V_Dental_Lip": 1.0, + "V_Affricate": 1.0 + } +} \ No newline at end of file diff --git a/scripts/addons/cc_blender_tools-2_4_0/CC3_Eye_Displacement_Map.png b/scripts/addons/cc_blender_tools-2_4_2/CC3_Eye_Displacement_Map.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/CC3_Eye_Displacement_Map.png rename to scripts/addons/cc_blender_tools-2_4_2/CC3_Eye_Displacement_Map.png diff --git a/scripts/addons/cc_blender_tools-2_4_0/LICENSE b/scripts/addons/cc_blender_tools-2_4_2/LICENSE similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/LICENSE rename to scripts/addons/cc_blender_tools-2_4_2/LICENSE diff --git a/scripts/addons/cc_blender_tools-2_4_0/README.md b/scripts/addons/cc_blender_tools-2_4_2/README.md similarity index 96% rename from scripts/addons/cc_blender_tools-2_4_0/README.md rename to scripts/addons/cc_blender_tools-2_4_2/README.md index 77bfc3d..0a4f7cd 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/README.md +++ b/scripts/addons/cc_blender_tools-2_4_2/README.md @@ -33,6 +33,31 @@ Links ## Changelog +### 2.4.2 +- Updated Morph export for Blender 5.1 +- Fix to constraint driver setup with missing shape keys. +- Fix to Blender 5.1 import operator changes. +- Fix to Blender 4.5 import fbx. +- Fix to Blender 5+ grease pencil layers (Spring Rig Generation). +- Motion Sets & Blending update: + - Motion sets updated for lights and cameras. + - Motion blending for light & camera motion sets. + - Motion blending always generates a new motion (non-destructive) + - Datalink Pose and Sequence now write onto current motion. + +### 2.4.1 +- Mesh modify pipeline. + - Sends mesh only (no armature) for exact replace mesh updates. + - Used mainly for direct facial expression editing with the facial profile editor. +- Animation Import Options and Blending: + - Animations and be blended on import or existing motions can be blended together to create new motions. + - Root Relative: Source animation root bone is transformed relative to the starting frame on destination animation, so that the root bone follows on from the exiting animation. + - Motion Blending: Source animation blends in and out over the destination animation by the set number of frames and follows custom blend curves. For smooth transitions. + - Bone masking: Only the masked bones are blended into the destination animation. Each bone can be weighted individually. + - Shape Key masking: Only the masked shape keys are blended into the destination animation. Each shape key can be weighted individually. + - Blend mask presets: Can be applied, removed and saved. +- Fixes to compositor texture pack baking. + ### 2.4.0 - Displacement update for Skin and Pbr materials. - Displacement added for Teeth and Hair. diff --git a/scripts/addons/cc_blender_tools-2_4_0/_LIB293.blend b/scripts/addons/cc_blender_tools-2_4_2/_LIB293.blend similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/_LIB293.blend rename to scripts/addons/cc_blender_tools-2_4_2/_LIB293.blend diff --git a/scripts/addons/cc_blender_tools-2_4_0/_LIB341.blend b/scripts/addons/cc_blender_tools-2_4_2/_LIB341.blend similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/_LIB341.blend rename to scripts/addons/cc_blender_tools-2_4_2/_LIB341.blend diff --git a/scripts/addons/cc_blender_tools-2_4_0/__init__.py b/scripts/addons/cc_blender_tools-2_4_2/__init__.py similarity index 96% rename from scripts/addons/cc_blender_tools-2_4_0/__init__.py rename to scripts/addons/cc_blender_tools-2_4_2/__init__.py index 16cffe3..cba9a1a 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/__init__.py +++ b/scripts/addons/cc_blender_tools-2_4_2/__init__.py @@ -21,6 +21,7 @@ if "bpy" in locals(): importlib.reload(vars) importlib.reload(params) importlib.reload(utils) + importlib.reload(ui) importlib.reload(lib) importlib.reload(cc) importlib.reload(jsonutils) @@ -69,6 +70,7 @@ from . import preferences from . import vars from . import params from . import utils +from . import ui from . import lib from . import cc from . import jsonutils @@ -114,7 +116,7 @@ from . import rlx bl_info = { "name": "CC/iC Tools", "author": "Victor Soupday", - "version": (2, 4, 0), + "version": (2, 4, 2), "blender": (3, 4, 1), "category": "Characters", "location": "3D View > Properties > CC/iC Pipeline", @@ -139,8 +141,8 @@ classes = ( properties.CCICBakeProps, properties.CC3ActionList, properties.CC3ArmatureList, + properties.CCICStringList, properties.CCIC_UI_MixItem, - properties.CCIC_UI_MixList, properties.CCICActionStore, properties.CCICActionOptions, properties.CC3HeadParameters, @@ -168,6 +170,7 @@ classes = ( properties.CCICExpressionData, properties.CC3ObjectCache, properties.CC3CharacterCache, + properties.CC3StagingCache, properties.CC3ImportProps, importer.CC3Import, @@ -206,10 +209,12 @@ classes = ( rigutils.CCICMotionSetRename, rigutils.CCICMotionSetInfo, rigutils.CCICRigUtils, - rigutils.CCIC_ImportMixBones_UL_List, - rigutils.CCIC_RigMixBones_UL_List, + rigutils.CCIC_IMPORTMIX_UL_List, + rigutils.CCIC_AVAILABLEMIX_UL_List, rigutils.CCICActionImportFunctions, - rigutils.CCICActionImportOptions, + rigutils.CCICMotionBlendFunctions, + rigutils.CCICMotionPrefs, + rigutils.CCICMotionBlend, facerig.CCICImportARKitCSV, panels.ARMATURE_UL_List, diff --git a/scripts/addons/cc_blender_tools-2_4_0/addon_updater.py b/scripts/addons/cc_blender_tools-2_4_2/addon_updater.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/addon_updater.py rename to scripts/addons/cc_blender_tools-2_4_2/addon_updater.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/addon_updater_ops.py b/scripts/addons/cc_blender_tools-2_4_2/addon_updater_ops.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/addon_updater_ops.py rename to scripts/addons/cc_blender_tools-2_4_2/addon_updater_ops.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/bake.py b/scripts/addons/cc_blender_tools-2_4_2/bake.py similarity index 95% rename from scripts/addons/cc_blender_tools-2_4_0/bake.py rename to scripts/addons/cc_blender_tools-2_4_2/bake.py index 2c0cd81..6998ac3 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/bake.py +++ b/scripts/addons/cc_blender_tools-2_4_2/bake.py @@ -19,7 +19,7 @@ import os from mathutils import Vector from . import normal, colorspace, imageutils, wrinkle, nodeutils, materials, utils, params, vars from .exporter import get_export_objects -from . utils import B500 +from . utils import B500, B430 BAKE_SAMPLES = 4 BAKE_INDEX = 1001 @@ -54,39 +54,7 @@ def prep_bake(context, mat: bpy.types.Material=None, samples=BAKE_SAMPLES, image if not context: context = bpy.context - # cycles settings - bake_state["samples"] = context.scene.cycles.samples - if utils.B500(): - bake_state["use_bake_multires"] = context.scene.render.bake.use_multires - else: - bake_state["use_bake_multires"] = context.scene.render.use_bake_multires - # Blender 3.0 - if utils.B300(): - bake_state["preview_samples"] = context.scene.cycles.preview_samples - bake_state["use_adaptive_sampling"] = context.scene.cycles.use_adaptive_sampling - bake_state["use_preview_adaptive_sampling"] = context.scene.cycles.use_preview_adaptive_sampling - bake_state["use_denoising"] = context.scene.cycles.use_denoising - bake_state["use_preview_denoising"] = context.scene.cycles.use_preview_denoising - bake_state["use_auto_tile"] = context.scene.cycles.use_auto_tile - # render settings - bake_state["file_format"] = context.scene.render.image_settings.file_format - bake_state["color_depth"] = context.scene.render.image_settings.color_depth - bake_state["color_mode"] = context.scene.render.image_settings.color_mode - bake_state["use_selected_to_active"] = context.scene.render.bake.use_selected_to_active - bake_state["use_pass_direct"] = context.scene.render.bake.use_pass_direct - bake_state["use_pass_indirect"] = context.scene.render.bake.use_pass_indirect - bake_state["margin"] = context.scene.render.bake.margin - bake_state["use_clear"] = context.scene.render.bake.use_clear - bake_state["image_format"] = context.scene.render.image_settings.file_format - # Blender 2.92 - if utils.B292(): - bake_state["target"] = context.scene.render.bake.target - # color management - bake_state["view_transform"] = context.scene.view_settings.view_transform - bake_state["look"] = context.scene.view_settings.look - bake_state["gamma"] = context.scene.view_settings.gamma - bake_state["exposure"] = context.scene.view_settings.exposure - bake_state["colorspace"] = context.scene.sequencer_colorspace_settings.name + bake_state["render_settings"] = store_render_settings(context) context.scene.cycles.samples = samples context.scene.render.image_settings.file_format = image_format @@ -119,28 +87,36 @@ def prep_bake(context, mat: bpy.types.Material=None, samples=BAKE_SAMPLES, image if utils.B292(): context.scene.render.bake.target = 'IMAGE_TEXTURES' + # TODO Which blender versions for these? + context.scene.render.resolution_percentage = 100 + if B500(): + context.scene.render.image_settings.media_type = "IMAGE" + context.scene.display_settings.emulation = "AUTO" + context.scene.render.image_settings.compression = 15 + context.scene.render.use_overwrite = True + context.scene.render.use_placeholder = False + context.scene.render.use_render_cache = False + context.scene.render.use_file_extension = True + if B430(): + context.scene.view_settings.use_white_balance = False + # color management override + context.scene.render.image_settings.color_management = "FOLLOW_SCENE" + # post processing + context.scene.render.use_compositing = False + context.scene.render.use_sequencer = False + context.scene.render.dither_intensity = 0.0 + # go into wireframe mode (so Blender doesn't update or recompile the material shaders while # we manipulate them for baking, and also so Blender doesn't fire up the cycles viewport...): shading: bpy.types.View3DShading = utils.get_view_3d_shading(context) if shading: - bake_state["shading"] = shading.type - shading.type = 'WIREFRAME' - else: - bake_state["shading"] = 'MATERIAL' shading.type = 'WIREFRAME' + # set cycles rendering mode for baking - bake_state["engine"] = context.scene.render.engine context.scene.render.engine = 'CYCLES' - bake_state["cycles_bake_type"] = context.scene.cycles.bake_type - if utils.B500(): - bake_state["render_bake_type"] = context.scene.render.bake.type - else: - bake_state["render_bake_type"] = context.scene.render.bake_type context.scene.cycles.bake_type = "COMBINED" - if make_surface: - # deselect everything bpy.ops.object.select_all(action='DESELECT') # create the baking plane, a single quad baking surface for an even sampling across the entire texture @@ -160,6 +136,117 @@ def prep_bake(context, mat: bpy.types.Material=None, samples=BAKE_SAMPLES, image return bake_state +def get_context_setting(context, settings, path): + try: + settings[path] = eval(f"context.{path}", None, locals()) + except: ... + #except Exception as e: + # utils.log_warn(f"context setting: {path} not found!") + + +def set_context_setting(context, settings, path): + try: + exec(f"context.{path} = settings[path]", None, locals()) + except: ... + #except Exception as e: + # utils.log_warn(f"context setting: {path} not found!") + + +CONTEXT_SETTINGS = [ + # cycles settings + "scene.cycles.samples", + "scene.render.bake.use_multires", # B500 + "scene.render.use_bake_multires", # < B500 + "scene.cycles.preview_samples", # B300 + "scene.cycles.use_adaptive_sampling", # B300 + "scene.cycles.use_preview_adaptive_sampling", # B300 + "scene.cycles.use_denoising", # B300 + "scene.cycles.use_preview_denoising", # B300 + "scene.cycles.use_auto_tile", # B300 + # render settings + "scene.render.resolution_x", + "scene.render.resolution_y", + "scene.render.resolution_percentage", + "scene.render.image_settings.media_type", ###!!! + "scene.render.image_settings.file_format", + "scene.render.image_settings.color_depth", + "scene.render.image_settings.color_mode", + "scene.render.image_settings.compression", ###!!! + "scene.render.bake.use_selected_to_active", + "scene.render.bake.use_pass_direct", + "scene.render.bake.use_pass_indirect", + "scene.render.bake.margin", + "scene.render.bake.use_clear", + "scene.render.use_overwrite", ###!!! + "scene.render.use_placeholder", ###!!! + "scene.render.use_render_cache", ###!!! + "scene.render.use_file_extension", ###!!! + "scene.render.bake.target", #B292 + "scene.render.filepath", + # display settings + "scene.display_settings.display_device", + # color management + "scene.view_settings.use_curve_mapping", + "scene.view_settings.view_transform", + "scene.view_settings.look", + "scene.view_settings.gamma", + "scene.view_settings.exposure", + "scene.sequencer_colorspace_settings.name", + "scene.display_settings.emulation", # B??? + "scene.view_settings.use_white_balance", # B??? + "scene.view_settings.white_balance_temperature", + "scene.view_settings.white_balance_tint", + # color management override B??? + "scene.render.image_settings.color_management", + "scene.render.image_settings.display_settings.display_device", + "scene.render.image_settings.view_settings.view_transform", + "scene.render.image_settings.view_settings.look", + "scene.render.image_settings.view_settings.exposure", + "scene.render.image_settings.view_settings.gamma", + "scene.render.image_settings.view_settings.use_curve_mapping", + "scene.render.image_settings.view_settings.use_white_balance", + # renderer + "scene.render.engine", + "scene.cycles.bake_type", + "scene.render.bake.type", #B500 + "scene.render.bake_type", # < B500 + # post processing + "scene.render.use_compositing", ###!!! + "scene.render.use_sequencer", ###!!! + "scene.render.dither_intensity", ###!!! +] + + +def store_render_settings(context): + settings = {} + if not context: + context = bpy.context + + for path in CONTEXT_SETTINGS: + get_context_setting(context, settings, path) + + # current viewport shading + shading: bpy.types.View3DShading = utils.get_view_3d_shading(context) + settings["shading.type"] = shading.type if shading else 'MATERIAL' + + return settings + + +def restore_render_settings(context, settings): + if not context: + context = bpy.context + + for path in CONTEXT_SETTINGS: + set_context_setting(context, settings, path) + + # current viewport shading + shading: bpy.types.View3DShading = utils.get_view_3d_shading(context) + if shading: + shading.type = settings["shading.type"] + + return settings + + def set_bake_material(bake_state, mat): if mat and bake_state and "bake_surface" in bake_state: bake_surface = bake_state["bake_surface"] @@ -179,52 +266,7 @@ def post_bake(context, state): if not context: context = bpy.context - # cycles settings - context.scene.cycles.samples = state["samples"] - # Blender 3.0 - if utils.B300(): - context.scene.cycles.preview_samples = state["preview_samples"] - context.scene.cycles.use_adaptive_sampling = state["use_adaptive_sampling"] - context.scene.cycles.use_preview_adaptive_sampling = state["use_preview_adaptive_sampling"] - context.scene.cycles.use_denoising = state["use_denoising"] - context.scene.cycles.use_preview_denoising = state["use_preview_denoising"] - context.scene.cycles.use_auto_tile = state["use_auto_tile"] - # render settings - context.scene.render.image_settings.file_format = state["file_format"] - context.scene.render.image_settings.color_depth = state["color_depth"] - context.scene.render.image_settings.color_mode = state["color_mode"] - if utils.B500(): - context.scene.render.bake.use_multires = state["use_bake_multires"] - else: - context.scene.render.use_bake_multires = state["use_bake_multires"] - context.scene.render.bake.use_selected_to_active = state["use_selected_to_active"] - context.scene.render.bake.use_pass_direct = state["use_pass_direct"] - context.scene.render.bake.use_pass_indirect = state["use_pass_indirect"] - context.scene.render.bake.margin = state["margin"] - context.scene.render.bake.use_clear = state["use_clear"] - context.scene.render.image_settings.file_format = state["image_format"] - # Blender 2.92 - if utils.B292(): - context.scene.render.bake.target = state["target"] - # color management - context.scene.view_settings.view_transform = state["view_transform"] - context.scene.view_settings.look = state["look"] - context.scene.view_settings.gamma = state["gamma"] - context.scene.view_settings.exposure = state["exposure"] - context.scene.sequencer_colorspace_settings.name = state["colorspace"] - # render engine - context.scene.render.engine = state["engine"] - # viewport shading - shading = utils.get_view_3d_shading(context) - if shading: - shading.type = state["shading"] - - # bake type - context.scene.cycles.bake_type = state["cycles_bake_type"] - if utils.B500(): - context.scene.render.bake.type = state["render_bake_type"] - else: - context.scene.render.bake_type = state["render_bake_type"] + restore_render_settings(context, state["render_settings"]) # remove the bake surface if "bake_surface" in state: @@ -632,8 +674,12 @@ def get_compositor_tree(context) -> bpy.types.NodeGroup: return context.scene.node_tree -def compositing_setup(context): +def compositing_setup(context: bpy.types.Context): + + settings = store_render_settings(context) + if B500(): + # node tree old_tree = context.scene.compositing_node_group tree = bpy.data.node_groups.new("Compositor Bake", "CompositorNodeTree") context.scene.compositing_node_group = tree @@ -650,10 +696,29 @@ def compositing_setup(context): for node in nodes: store[node] = node.mute node.mute = True - return nodes, links, store, tree + + # override context render settings + if B500(): + context.scene.render.image_settings.media_type = "IMAGE" + context.scene.display_settings.emulation = "AUTO" + context.scene.render.image_settings.compression = 15 + context.scene.render.use_overwrite = True + context.scene.render.use_placeholder = False + context.scene.render.use_render_cache = False + context.scene.render.use_file_extension = True + if B430(): + context.scene.view_settings.use_white_balance = False + # color management override + context.scene.render.image_settings.color_management = "OVERRIDE" + # post processing + context.scene.render.use_compositing = True + context.scene.render.use_sequencer = False + context.scene.render.dither_intensity = 0.0 + + return nodes, links, store, tree, settings -def compositing_cleanup(context, store): +def compositing_cleanup(context, store, settings): if B500(): context.scene.compositing_node_group = store[1] bpy.data.node_groups.remove(store[0]) @@ -668,6 +733,7 @@ def compositing_cleanup(context, store): clean_up.append(node) for node in clean_up: nodes.remove(node) + restore_render_settings(context, settings) def compositor_pack_RGBA(mat, channel_id, pack_mode, bake_dir, @@ -706,7 +772,7 @@ def compositor_pack_RGBA(mat, channel_id, pack_mode, bake_dir, utils.log_info(f"Resuing existing texture pack {image_name}, not baking.") return imageutils.load_image(image_path, color_space) - nodes, links, store, node_tree = compositing_setup(context) + nodes, links, store, node_tree, settings = compositing_setup(context) CNCC_node = nodeutils.make_shader_node(nodes, "CompositorNodeCombineColor") CNCGO_node = None @@ -831,30 +897,32 @@ def compositor_pack_RGBA(mat, channel_id, pack_mode, bake_dir, nodeutils.link_nodes(links, CNI_A_node, "Image", CNCC_node, "Alpha") nodeutils.link_nodes(links, CNI_A_node, "Image", CNCGO_node, "Alpha") - X = context.scene.render.resolution_x - Y = context.scene.render.resolution_y - FP = context.scene.render.filepath - FF = context.scene.render.image_settings.file_format - CD = context.scene.render.image_settings.color_depth - CM = context.scene.render.image_settings.color_mode - VT = context.scene.view_settings.view_transform - LK = context.scene.view_settings.look - GA = context.scene.view_settings.gamma - EX = context.scene.view_settings.exposure context.scene.render.resolution_x = width context.scene.render.resolution_y = height + context.scene.render.resolution_percentage = 100 context.scene.render.image_settings.file_format = 'OPEN_EXR' context.scene.render.image_settings.color_depth = color_depth context.scene.render.image_settings.color_mode = 'RGBA' context.scene.render.image_settings.exr_codec = 'ZIP' - context.scene.render.image_settings.color_management = 'OVERRIDE' context.scene.render.image_settings.linear_colorspace_settings.name = color_space - context.scene.render.filepath = image_path + context.scene.render.image_settings.color_management = 'OVERRIDE' + context.scene.render.image_settings.display_settings.display_device = "sRGB" + context.scene.render.image_settings.view_settings.view_transform = "Standard" + context.scene.render.image_settings.view_settings.look = "None" + context.scene.render.image_settings.view_settings.gamma = 1 + context.scene.render.image_settings.view_settings.exposure = 0 + context.scene.render.image_settings.view_settings.use_curve_mapping = False + if B430(): + context.scene.render.image_settings.view_settings.use_white_balance = False + context.scene.display_settings.display_device = "sRGB" context.scene.view_settings.view_transform = 'Standard' context.scene.view_settings.look = 'None' context.scene.view_settings.gamma = 1 context.scene.view_settings.exposure = 0 + context.scene.view_settings.use_curve_mapping = False + if B430(): + context.scene.view_settings.use_white_balance = False for node in nodes: node.select = False @@ -863,18 +931,7 @@ def compositor_pack_RGBA(mat, channel_id, pack_mode, bake_dir, bpy.ops.render.render(write_still=True) - context.scene.render.resolution_x = X - context.scene.render.resolution_y = Y - context.scene.render.filepath = FP - context.scene.render.image_settings.file_format = FF - context.scene.render.image_settings.color_depth = CD - context.scene.render.image_settings.color_mode = CM - context.scene.view_settings.view_transform = VT - context.scene.view_settings.look = LK - context.scene.view_settings.gamma = GA - context.scene.view_settings.exposure = EX - - compositing_cleanup(context, store) + compositing_cleanup(context, store, settings) image: bpy.types.Image = imageutils.load_image(image_path, color_space) return image diff --git a/scripts/addons/cc_blender_tools-2_4_0/basic.py b/scripts/addons/cc_blender_tools-2_4_2/basic.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/basic.py rename to scripts/addons/cc_blender_tools-2_4_2/basic.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/bones.py b/scripts/addons/cc_blender_tools-2_4_2/bones.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/bones.py rename to scripts/addons/cc_blender_tools-2_4_2/bones.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/cc.py b/scripts/addons/cc_blender_tools-2_4_2/cc.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/cc.py rename to scripts/addons/cc_blender_tools-2_4_2/cc.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/cc_blender_tools-2_4_0_updater/cc_blender_tools-2_4_0_updater_status.json b/scripts/addons/cc_blender_tools-2_4_2/cc_blender_tools-2_4_2_updater/cc_blender_tools-2_4_2_updater_status.json similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/cc_blender_tools-2_4_0_updater/cc_blender_tools-2_4_0_updater_status.json rename to scripts/addons/cc_blender_tools-2_4_2/cc_blender_tools-2_4_2_updater/cc_blender_tools-2_4_2_updater_status.json diff --git a/scripts/addons/cc_blender_tools-2_4_0/channel_mixer.py b/scripts/addons/cc_blender_tools-2_4_2/channel_mixer.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/channel_mixer.py rename to scripts/addons/cc_blender_tools-2_4_2/channel_mixer.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/characters.py b/scripts/addons/cc_blender_tools-2_4_2/characters.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/characters.py rename to scripts/addons/cc_blender_tools-2_4_2/characters.py index 5c906f0..8c41850 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/characters.py +++ b/scripts/addons/cc_blender_tools-2_4_2/characters.py @@ -89,8 +89,8 @@ def duplicate_character(chr_cache): new_id = utils.generate_random_id(20) obj_cache.object_id = new_id for obj in objects: - if utils.get_rl_object_id(obj) == old_id: - utils.set_rl_object_id(obj, new_id) + if utils.get_rl_id(obj) == old_id: + utils.set_rl_id(obj, new_id) obj_cache.object = obj if obj.type == "ARMATURE": action, slot = utils.safe_get_action_slot(old_obj) @@ -112,9 +112,9 @@ def duplicate_character(chr_cache): mat_cache.material = mat chr_rig = utils.get_armature_from_objects(objects) - character_name = utils.unique_object_name(utils.un_suffix_name(old_cache.character_name)) + character_name = utils.unique_object_name(utils.un_suffix_name(old_cache.get_name())) utils.log_info(f"Using character name: {character_name}") - chr_cache.character_name = character_name + chr_cache.set_name(character_name) if chr_cache.rigified: rig_name = character_name + "_Rigify" chr_rig["rig_id"] = utils.generate_random_id(20) @@ -344,7 +344,7 @@ def convert_generic_to_non_standard(objects, file_path=None, type_override=None, chr_rig.data.name = chr_name chr_cache = props.import_cache.add() chr_cache.import_file = "" - chr_cache.character_name = chr_name + chr_cache.set_name(chr_name) chr_cache.import_embedded = False chr_cache.generation = "Unknown" chr_cache.non_standard_type = chr_type @@ -416,7 +416,7 @@ def link_or_append_rl_character(op, context, blend_file, link=False): for src_cache in src_props.import_cache: - character_name = src_cache.character_name + character_name = src_cache.get_name() import_file = src_cache.import_file chr_rig = src_cache.get_armature() chr_objects = src_cache.get_all_objects(include_armature=False, @@ -575,6 +575,7 @@ def reconnect_rl_character_to_fbx(chr_rig, fbx_path): rig_name = chr_rig.name character_name = rig_name + is_rigify = rigutils.is_rigify_armature(chr_rig) if "_Rigify" in character_name: character_name = character_name.replace("_Rigify", "") @@ -592,11 +593,11 @@ def reconnect_rl_character_to_fbx(chr_rig, fbx_path): meta_rig = bpy.data.objects[meta_rig_name] chr_cache.import_file = fbx_path - chr_cache.character_name = character_name + chr_cache.set_name(character_name) chr_cache.import_embedded = False chr_cache.generation = generation chr_cache.non_standard_type = "HUMANOID" - chr_cache.rigified = True + chr_cache.rigified = is_rigify chr_cache.rig_meta_rig = meta_rig chr_cache.rigified_full_face_rig = character_has_bones(chr_rig, ["nose", "lip.T", "lip.B"]) chr_cache.add_object_cache(chr_rig) @@ -642,14 +643,14 @@ def reconnect_rl_character_to_blend(chr_rig, blend_file): import_file = chr_rig["rl_import_file"] for chr_cache in src_props.import_cache: if chr_cache.import_file == import_file: - utils.log_info(f"Found matching source character fbx: {chr_cache.character_name}") + utils.log_info(f"Found matching source character fbx: {chr_cache.get_name()}") src_cache = chr_cache break # try to find the source cache by character name for chr_cache in src_props.import_cache: - if chr_cache.character_name == character_name: - utils.log_info(f"Found matching source character name: {chr_cache.character_name}") + if chr_cache.get_name() == character_name: + utils.log_info(f"Found matching source character name: {chr_cache.get_name()}") src_cache = chr_cache break @@ -683,7 +684,7 @@ def rebuild_character_cache(chr_cache, chr_rig, objects, src_cache=None): processed = [] defaults = [] for obj in objects: - obj_id = utils.get_rl_object_id(obj) + obj_id = utils.get_rl_id(obj) if utils.object_exists_is_mesh(obj) and obj not in processed: processed.append(obj) src_obj_cache = src_cache.get_object_cache(obj, by_id=obj_id) if src_cache else None @@ -1826,7 +1827,7 @@ def remove_all_empty_shapekeys_vertex_groups(chr_cache): key_count = 0 group_count = 0 if chr_cache: - utils.log_info(f"Cleaning empty shape keys and vertex groups in character: {chr_cache.character_name}") + utils.log_info(f"Cleaning empty shape keys and vertex groups in character: {chr_cache.get_name()}") objects = chr_cache.get_cache_objects() body_objects = chr_cache.get_objects_of_type("BODY") arm = chr_cache.get_armature() @@ -2350,12 +2351,12 @@ class CCICCharacterRename(bpy.types.Operator): meta_rig.data.name = metarig_name rig.name = rigify_name rig.data.name = rigify_name - chr_cache.character_name = source_name + chr_cache.set_name(source_name) else: rig_name = utils.unique_object_name(self.name, rig) rig.name = rig_name rig.data.name = rig_name - chr_cache.character_name = rig_name + chr_cache.set_name(rig_name) if chr_cache.is_non_standard(): chr_cache.non_standard_type = self.non_standard_type return {"FINISHED"} @@ -2364,7 +2365,7 @@ class CCICCharacterRename(bpy.types.Operator): props = vars.props() prefs = vars.prefs() chr_cache = props.get_context_character_cache(context) - self.name = chr_cache.character_name + self.name = chr_cache.get_name() self.non_standard_type = chr_cache.non_standard_type return context.window_manager.invoke_props_dialog(self) diff --git a/scripts/addons/cc_blender_tools-2_4_0/colorspace.py b/scripts/addons/cc_blender_tools-2_4_2/colorspace.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/colorspace.py rename to scripts/addons/cc_blender_tools-2_4_2/colorspace.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/drivers.py b/scripts/addons/cc_blender_tools-2_4_2/drivers.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/drivers.py rename to scripts/addons/cc_blender_tools-2_4_2/drivers.py index 76307b1..f6b439a 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/drivers.py +++ b/scripts/addons/cc_blender_tools-2_4_2/drivers.py @@ -909,12 +909,14 @@ def find_transform_var_def(var_defs, source_obj, transform_prop, space="LOCAL_SP def make_shape_key_var_def(var_name, source_obj, key_name): key = get_shape_key(source_obj, key_name) - data_path = "shape_keys." + key.path_from_id("value") - var_def = [var_name, - "SINGLE_PROP", - source_obj.data, - data_path] - return var_def + if key: + data_path = "shape_keys." + key.path_from_id("value") + var_def = [var_name, + "SINGLE_PROP", + source_obj.data, + data_path] + return var_def + return None def find_shape_key_var_def(var_defs, source_obj, key_name): diff --git a/scripts/addons/cc_blender_tools-2_4_0/exporter.py b/scripts/addons/cc_blender_tools-2_4_2/exporter.py similarity index 98% rename from scripts/addons/cc_blender_tools-2_4_0/exporter.py rename to scripts/addons/cc_blender_tools-2_4_2/exporter.py index 9e1e0db..01c6d8e 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/exporter.py +++ b/scripts/addons/cc_blender_tools-2_4_2/exporter.py @@ -58,7 +58,7 @@ def check_valid_export_fbx(chr_cache, objects): if standard and not arm: if chr_cache: - message = f"ERROR: Character {chr_cache.character_name} has no armature!" + message = f"ERROR: Character {chr_cache.get_name()} has no armature!" else: message = f"ERROR: Character has no armature!" report.append(message) @@ -1712,26 +1712,28 @@ def export_arp(file_path, arm, objects): def obj_export(file_path, use_selection=False, use_animation=False, global_scale=100, - use_vertex_colors=False, use_vertex_groups=False, apply_modifiers=True, - keep_vertex_order=False, use_materials=False): - if utils.B330(): - bpy.ops.wm.obj_export(filepath=file_path, - global_scale=global_scale, - export_selected_objects=use_selection, - export_animation=use_animation, - export_materials=use_materials, - export_colors=use_vertex_colors, - export_vertex_groups=use_vertex_groups, - apply_modifiers=apply_modifiers) - else: - bpy.ops.export_scene.obj(filepath=file_path, - global_scale=global_scale, - use_selection=use_selection, - use_materials=use_materials, - use_animation=use_animation, - use_vertex_groups=use_vertex_groups, - use_mesh_modifiers=apply_modifiers, - keep_vertex_order=keep_vertex_order) + use_vertex_colors=False, use_object_groups=True, + use_material_groups=False, use_vertex_groups=False, + apply_modifiers=True, use_materials=False): + bpy.ops.wm.obj_export(filepath=file_path, + export_selected_objects=use_selection, + global_scale=global_scale, + forward_axis="NEGATIVE_Z", + up_axis="Y", + export_uv=True, + export_normals=True, + export_colors=use_vertex_colors, + export_curves_as_nurbs=False, + export_triangulated_mesh=False, + apply_modifiers=apply_modifiers, + apply_transform=True, + export_object_groups=use_object_groups, + export_material_groups=use_material_groups, + export_vertex_groups=use_vertex_groups, + export_smooth_groups=False, + export_materials=use_materials, + export_animation=use_animation, + ) def export_standard(self, context, chr_cache, file_path, include_selected): @@ -1799,7 +1801,7 @@ def export_standard(self, context, chr_cache, file_path, include_selected): if not os.path.exists(file_path): custom_export = False - # proceed with normal export + # proceed with standard export if not custom_export: bpy.ops.export_scene.fbx(filepath=file_path, global_scale=1.0, @@ -1808,6 +1810,7 @@ def export_standard(self, context, chr_cache, file_path, include_selected): bake_anim = export_anim, bake_anim_simplify_factor=self.animation_simplify, add_leaf_bones = False, + mesh_smooth_type = ("FACE" if self.export_face_smoothing else "OFF"), use_mesh_modifiers = False) @@ -1855,13 +1858,7 @@ def export_standard(self, context, chr_cache, file_path, include_selected): utils.unhide(p.object) p.object.select_set(True) - obj_export(file_path, use_selection=True, - global_scale=100, - use_materials=False, - keep_vertex_order=True, - use_vertex_colors=True, - use_vertex_groups=True, - apply_modifiers=True) + obj_export(file_path, use_selection=True) #export_copy_obj_key(chr_cache, dir, name) export_copy_asset_file(chr_cache, dir, name, ".ObjKey", chr_cache.get_import_key_file()) @@ -2351,13 +2348,8 @@ def export_as_accessory(file_path, filename_ext): ) elif utils.is_file_ext(ext, "OBJ"): obj_export(file_path, use_selection=True, - global_scale=100, - use_animation=False, use_materials=True, - keep_vertex_order=True, - use_vertex_colors=True, - use_vertex_groups=False, - apply_modifiers=True) + use_vertex_colors=True) # restore selection bpy.ops.object.select_all(action='DESELECT') @@ -2374,13 +2366,8 @@ def export_as_replace_mesh(file_path): state = utils.store_mode_selection_state() obj_export(file_path, use_selection=True, - global_scale=100, - use_animation=False, use_materials=True, - keep_vertex_order=True, - use_vertex_colors=True, - use_vertex_groups=False, - apply_modifiers=True) + use_vertex_colors=True) # restore selection utils.restore_mode_selection_state(state) diff --git a/scripts/addons/cc_blender_tools-2_4_0/facerig.py b/scripts/addons/cc_blender_tools-2_4_2/facerig.py similarity index 96% rename from scripts/addons/cc_blender_tools-2_4_0/facerig.py rename to scripts/addons/cc_blender_tools-2_4_2/facerig.py index 3bbf89c..048a96e 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/facerig.py +++ b/scripts/addons/cc_blender_tools-2_4_2/facerig.py @@ -1556,22 +1556,24 @@ def build_expression_constraint_add_driver(chr_cache, rigify_rig, objects, head, for key in source_keys: source_obj = get_expression_shape_key_source_object(objects, head, key) - var_name = f"V{vidx}" - vidx += 1 - var_def = drivers.make_shape_key_var_def(var_name, source_obj, key) - var_defs.append(var_def) + if source_obj: + var_name = f"V{vidx}" + vidx += 1 + var_def = drivers.make_shape_key_var_def(var_name, source_obj, key) + if var_def: + var_defs.append(var_def) + var_expression = get_expression_constraint_var_expression(var_name, points) + if expression: + expression += "*" + expression += var_expression + else: + utils.log_error(f"Unable to find source object for shape key: {key} in {[o.name for o in objects]}") - var_expression = get_expression_constraint_var_expression(var_name, points) - - if expression: - expression += "*" - expression += var_expression - - driver_def = ["SCRIPTED", expression] - - for obj in objects: - if utils.object_has_shape_key(obj, target_key): - drivers.add_shape_key_driver(rigify_rig, obj, target_key, driver_def, var_defs, 1.0) + if expression: + driver_def = ["SCRIPTED", expression] + for obj in objects: + if utils.object_has_shape_key(obj, target_key): + drivers.add_shape_key_driver(rigify_rig, obj, target_key, driver_def, var_defs, 1.0) def apply_control_limit_constraint_drivers(rigify_rig, control_name, control_def, target_key, expression, var_defs, @@ -1619,45 +1621,46 @@ def build_expression_constraint_limit_driver(chr_cache, rigify_rig, objects, hea for key in source_keys: source_obj = get_expression_shape_key_source_object(objects, head, key) - var_name = f"lv{vidx}" - vidx += 1 - var_def = drivers.make_shape_key_var_def(var_name, source_obj, key) - var_defs.append(var_def) + if source_obj: + var_name = f"lv{vidx}" + vidx += 1 + var_def = drivers.make_shape_key_var_def(var_name, source_obj, key) + if var_def: + var_defs.append(var_def) + var_expression = get_expression_constraint_var_expression(var_name, points) + if limit_expression: + limit_expression += "*" + limit_expression += var_expression - var_expression = get_expression_constraint_var_expression(var_name, points) + if limit_expression: + # limit facerig control movement ranges + if prefs.rigify_limit_control_range: + FACERIG_CONFIG = get_facerig_config(chr_cache) + for control_name, control_def in FACERIG_CONFIG.items(): + if control_def["widget_type"] == "slider": + apply_control_limit_constraint_drivers(rigify_rig, control_name, control_def, + target_key, limit_expression, var_defs, + "_line", "", "y") + elif control_def["widget_type"] == "rect": + apply_control_limit_constraint_drivers(rigify_rig, control_name, control_def, + target_key, limit_expression, var_defs, + "_box", "x", "x") + apply_control_limit_constraint_drivers(rigify_rig, control_name, control_def, + target_key, limit_expression, var_defs, + "_box", "y", "y") - if limit_expression: - limit_expression += "*" - limit_expression += var_expression - - # limit facerig control movement ranges - if prefs.rigify_limit_control_range: - FACERIG_CONFIG = get_facerig_config(chr_cache) - for control_name, control_def in FACERIG_CONFIG.items(): - if control_def["widget_type"] == "slider": - apply_control_limit_constraint_drivers(rigify_rig, control_name, control_def, - target_key, limit_expression, var_defs, - "_line", "", "y") - elif control_def["widget_type"] == "rect": - apply_control_limit_constraint_drivers(rigify_rig, control_name, control_def, - target_key, limit_expression, var_defs, - "_box", "x", "x") - apply_control_limit_constraint_drivers(rigify_rig, control_name, control_def, - target_key, limit_expression, var_defs, - "_box", "y", "y") - - # apply a min(expression, limit_expression) to existing shape key value driver - # (driving the shape key max value has no effect) - for obj in objects: - if utils.object_has_shape_key(obj, target_key): - driver: bpy.types.Driver = drivers.get_shape_key_driver(obj, target_key) - if driver and driver.expression: - driver_expression = driver.expression - drivers.add_driver_var_defs(driver, var_defs) - new_expression = f"min({driver_expression}, {limit_expression})" - driver.expression = new_expression - else: - utils.log_warn(f"NO DRIVER: {obj.name} {target_key}") + # apply a min(expression, limit_expression) to existing shape key value driver + # (driving the shape key max value has no effect) + for obj in objects: + if utils.object_has_shape_key(obj, target_key): + driver: bpy.types.Driver = drivers.get_shape_key_driver(obj, target_key) + if driver and driver.expression: + driver_expression = driver.expression + drivers.add_driver_var_defs(driver, var_defs) + new_expression = f"min({driver_expression}, {limit_expression})" + driver.expression = new_expression + else: + utils.log_warn(f"NO DRIVER: {obj.name} {target_key}") def build_expression_constraint_drivers(chr_cache, rigify_rig): @@ -1883,8 +1886,8 @@ def generate_arkit_proxy(chr_cache): chr_collections = utils.get_object_scene_collections(rigify_rig) objects = lib.get_object(["RL_ARKit_Proxy", "RL_ARKit_Proxy_Head"]) - rig_name = f"{chr_cache.character_name}_ARKit_Proxy" - mesh_name = f"{chr_cache.character_name}_ARKit_Proxy_Head" + rig_name = f"{chr_cache.get_name()}_ARKit_Proxy" + mesh_name = f"{chr_cache.get_name()}_ARKit_Proxy_Head" proxy_rig = None proxy_mesh = None for obj in objects: @@ -1908,7 +1911,7 @@ def generate_arkit_proxy(chr_cache): build_arkit_proxy_drivers(chr_cache, rigify_rig, proxy_rig, proxy_mesh) wgt_collection = rigutils.get_widget_rig_collection(chr_cache) - wgt_root = bones.make_root_widget(f"WGT-{chr_cache.character_name}_rig_arkit_proxy_root", 3.25) + wgt_root = bones.make_root_widget(f"WGT-{chr_cache.get_name()}_rig_arkit_proxy_root", 3.25) if wgt_collection: utils.remove_from_scene_collections(wgt_root) bones.add_widget_to_collection(wgt_root, wgt_collection) @@ -2076,8 +2079,8 @@ def load_csv(chr_cache, file_path): facial_profile, viseme_profile = chr_cache.get_facial_profile() if facial_profile in facerig_data.ARKIT_SHAPE_KEY_TARGETS: keys = facerig_data.ARKIT_SHAPE_KEY_TARGETS[facial_profile].keys() - key_action = utils.make_action(f"{chr_cache.character_name}_ARKit_Proxy_Head", slot_type="KEY", clear=True, reuse=True) - arm_action = utils.make_action(f"{chr_cache.character_name}_ARKit_Proxy", slot_type="OBJECT", clear=True, reuse=True) + key_action = utils.make_action(f"{chr_cache.get_name()}_ARKit_Proxy_Head", slot_type="KEY", clear=True, reuse=True) + arm_action = utils.make_action(f"{chr_cache.get_name()}_ARKit_Proxy", slot_type="OBJECT", clear=True, reuse=True) key_channel = utils.get_action_channelbag(key_action, slot_type="KEY") if key_channel: for key in keys: diff --git a/scripts/addons/cc_blender_tools-2_4_0/facerig_data.py b/scripts/addons/cc_blender_tools-2_4_2/facerig_data.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/facerig_data.py rename to scripts/addons/cc_blender_tools-2_4_2/facerig_data.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/geom.py b/scripts/addons/cc_blender_tools-2_4_2/geom.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/geom.py rename to scripts/addons/cc_blender_tools-2_4_2/geom.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/hair.py b/scripts/addons/cc_blender_tools-2_4_2/hair.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/hair.py rename to scripts/addons/cc_blender_tools-2_4_2/hair.py index 3bb3175..cd55797 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/hair.py +++ b/scripts/addons/cc_blender_tools-2_4_2/hair.py @@ -1567,6 +1567,7 @@ def grease_pencil_to_length_loops(bone_length): grease_pencil_layer = get_active_grease_pencil_layer() if not grease_pencil_layer: + utils.log_error(f"Error finding grease pencil layer!") return frame = grease_pencil_layer.active_frame @@ -1590,6 +1591,7 @@ def grease_pencil_to_bones(chr_cache, arm, parent_mode, bone_length = 0.05, grease_pencil_layer = get_active_grease_pencil_layer() if not grease_pencil_layer: + utils.log_error(f"Error finding grease pencil layer!") return # turn off grease pencil on current object / mode (including object mode) @@ -1651,15 +1653,23 @@ def get_active_grease_pencil_layer(): #current_frame = bpy.context.scene.frame_current #note_layer = bpy.data.grease_pencils['Annotations'].layers.active #frame = note_layer.active_frame - try: - layer_index = bpy.context.scene.grease_pencil.layers.active_index - layer = bpy.context.scene.grease_pencil.layers[layer_index] - return layer - except: + if utils.B500(): try: - return bpy.context.scene.grease_pencil.layers.active + layer_index = bpy.context.scene.annotation.layers.active_index + layer = bpy.context.scene.annotation.layers[layer_index] + return layer except: return None + else: + try: + layer_index = bpy.context.scene.grease_pencil.layers.active_index + layer = bpy.context.scene.grease_pencil.layers[layer_index] + return layer + except: + try: + return bpy.context.scene.grease_pencil.layers.active + except: + return None def clear_grease_pencil(): @@ -1673,6 +1683,8 @@ def clear_grease_pencil(): active_layer.active_frame.clear() except: utils.log_error("Unable to remove active grease pencil frame!") + else: + utils.log_error(f"Error finding grease pencil layer!") def add_custom_bone(chr_cache, arm, parent_mode, bone_length = 0.05, skip_length = 0.0): diff --git a/scripts/addons/cc_blender_tools-2_4_0/hik.py b/scripts/addons/cc_blender_tools-2_4_2/hik.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/hik.py rename to scripts/addons/cc_blender_tools-2_4_2/hik.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/hik/RLRigify.3dxProfile b/scripts/addons/cc_blender_tools-2_4_2/hik/RLRigify.3dxProfile similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/hik/RLRigify.3dxProfile rename to scripts/addons/cc_blender_tools-2_4_2/hik/RLRigify.3dxProfile diff --git a/scripts/addons/cc_blender_tools-2_4_0/hik/vroid.3dxProfile b/scripts/addons/cc_blender_tools-2_4_2/hik/vroid.3dxProfile similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/hik/vroid.3dxProfile rename to scripts/addons/cc_blender_tools-2_4_2/hik/vroid.3dxProfile diff --git a/scripts/addons/cc_blender_tools-2_4_0/icons/wrinkle_bg.png b/scripts/addons/cc_blender_tools-2_4_2/icons/wrinkle_bg.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/icons/wrinkle_bg.png rename to scripts/addons/cc_blender_tools-2_4_2/icons/wrinkle_bg.png diff --git a/scripts/addons/cc_blender_tools-2_4_0/iconutils.py b/scripts/addons/cc_blender_tools-2_4_2/iconutils.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/iconutils.py rename to scripts/addons/cc_blender_tools-2_4_2/iconutils.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/imageutils.py b/scripts/addons/cc_blender_tools-2_4_2/imageutils.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/imageutils.py rename to scripts/addons/cc_blender_tools-2_4_2/imageutils.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/importer.py b/scripts/addons/cc_blender_tools-2_4_2/importer.py similarity index 95% rename from scripts/addons/cc_blender_tools-2_4_0/importer.py rename to scripts/addons/cc_blender_tools-2_4_2/importer.py index 6cf76a5..6f4925b 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/importer.py +++ b/scripts/addons/cc_blender_tools-2_4_2/importer.py @@ -20,7 +20,7 @@ import bpy from enum import IntEnum, IntFlag from . import (rlx, characters, hik, rigging, rigutils, bones, bake, imageutils, jsonutils, materials, - facerig, modifiers, wrinkle, drivers, nodeutils, physics, + facerig, modifiers, meshutils, wrinkle, drivers, nodeutils, physics, rigidbody, colorspace, scene, channel_mixer, shaders, basic, lib, cc, utils, vars) @@ -443,10 +443,10 @@ def remap_action_names(arm, objects, actions, source_id, motion_prefix=""): utils.log_info(f"Armature: {source_id} => {rig_id}") # don't change the armature id if it exists - rl_arm_id = utils.get_rl_object_id(arm) - if not rl_arm_id: - rl_arm_id = utils.generate_random_id(20) - utils.set_rl_object_id(arm, rl_arm_id) + rl_id = utils.get_rl_id(arm) + if not rl_id: + rl_id = utils.generate_random_id(20) + utils.set_rl_id(arm, rl_id) # find all motions for this armature armature_actions = [] @@ -457,13 +457,18 @@ def remap_action_names(arm, objects, actions, source_id, motion_prefix=""): for action in actions: split = action.name.split("|") action_arm_id = split[0] + # Blender 5.1.0+ all actions import as slotted and, + # action names are *not* prefixed with armature name anymore + if utils.B510(): + slotted_actions.append(action) + action_arm_id = source_id motion_id = split[-1] if action_arm_id == source_id: utils.log_info(f"Motion ID: {motion_id}") motion_ids.add(motion_id) armature_actions.append(action) motion_sets[motion_id] = rigutils.generate_motion_set(arm, motion_id, - motion_prefix) + motion_prefix) # determine how each shape key id relates to each object in the import for obj in objects: @@ -493,16 +498,16 @@ def remap_action_names(arm, objects, actions, source_id, motion_prefix=""): utils.log_info(f"Renaming action: {action.name} to {action_name}") action.name = action_name rigutils.add_motion_set_data(action, set_id, set_generation, - arm_id=rl_arm_id, + arm_id=rl_id, slotted=(action in slotted_actions)) - armature_actions.append(action) else: for obj_id, key_name in key_map.items(): if action_key_name == key_name: action_name = rigutils.make_key_action_name(rig_id, motion_id, obj_id, motion_prefix) utils.log_info(f"Renaming action: {action.name} to {action_name}") action.name = action_name - rigutils.add_motion_set_data(action, set_id, set_generation, obj_id=obj_id) + rigutils.add_motion_set_data(action, set_id, set_generation, + key_id=obj_id, arm_id=rl_id, slotted=False) shapekey_actions.append(action) return armature_actions, shapekey_actions @@ -588,7 +593,7 @@ def process_rl_import(file_path, import_flags, armatures, rl_armatures, cameras, chr_cache.import_file = file_path chr_cache.import_flags = import_flags # display name of character - chr_cache.character_name = character_name + chr_cache.set_name(character_name) arm["rl_import_file"] = file_path rigutils.fix_cc3_standard_rig(arm) @@ -607,7 +612,7 @@ def process_rl_import(file_path, import_flags, armatures, rl_armatures, cameras, # in case of duplicate names: character_name contains the name currently in Blender. # get_character_id() is the original name. - chr_cache.character_name = arm.name + chr_cache.set_name(arm.name) # add armature to object_cache chr_cache.add_object_cache(arm) # assign bone collections @@ -636,13 +641,6 @@ def process_rl_import(file_path, import_flags, armatures, rl_armatures, cameras, chr_cache.add_object_cache(obj) character_meshes.append(obj) - # clear custom normals option - if prefs.import_reset_custom_normals: - for obj in character_meshes: - bpy.context.view_layer.objects.active = obj - bpy.ops.mesh.customdata_custom_splitnormals_clear() - bpy.context.view_layer.objects.active = None - # remame actions utils.log_info("Renaming actions:") utils.log_indent() @@ -652,7 +650,7 @@ def process_rl_import(file_path, import_flags, armatures, rl_armatures, cameras, # determine character generation chr_cache.generation = detect_generation(chr_cache, arm, json_data, chr_cache.get_character_id()) - utils.log_info("Generation: " + chr_cache.character_name + " (" + chr_cache.generation + ")") + utils.log_info(f"Generation: {chr_cache.get_name()} ({chr_cache.generation})") arm["rl_generation"] = chr_cache.generation # cache materials @@ -661,6 +659,23 @@ def process_rl_import(file_path, import_flags, armatures, rl_armatures, cameras, obj = obj_cache.get_object() cache_object_materials(chr_cache, obj, chr_json, processed) + # clear custom normals option + for obj_cache in chr_cache.object_cache: + if obj_cache.is_mesh(): + obj = obj_cache.get_object() + fix_5_1_normals = (utils.B510() and prefs.import_fix_5_1_normals and + utils.object_has_shape_keys(obj) and + obj.data.has_custom_normals) + if prefs.import_reset_custom_normals or fix_5_1_normals: + utils.log_info(f"Cleaing custom normals on: {obj.name}") + bpy.context.view_layer.objects.active = obj + bpy.ops.mesh.customdata_custom_splitnormals_clear() + bpy.context.view_layer.objects.active = None + if fix_5_1_normals: + meshutils.set_shading(obj, True) + + + shaders.init_character_property_defaults(chr_cache, chr_json) basic.init_basic_default(chr_cache) @@ -709,7 +724,7 @@ def process_rl_import(file_path, import_flags, armatures, rl_armatures, cameras, chr_cache.import_file = file_path chr_cache.import_flags = import_flags # display name of character - chr_cache.character_name = character_name + chr_cache.set_name(character_name) chr_id = chr_cache.get_character_id() # link_id @@ -735,7 +750,7 @@ def process_rl_import(file_path, import_flags, armatures, rl_armatures, cameras, # in case of duplicate names: character_name contains the name currently in Blender. # import_name contains the original name. - chr_cache.character_name = arm.name + chr_cache.set_name(arm.name) # add armature to object_cache chr_cache.add_object_cache(arm) @@ -828,7 +843,7 @@ def process_rl_import(file_path, import_flags, armatures, rl_armatures, cameras, chr_cache.import_file = file_path chr_cache.import_flags = import_flags # display name of character - chr_cache.character_name = character_name + chr_cache.set_name(character_name) # link_id (OBJ exports don't have json) chr_cache.set_link_id(link_id) @@ -1072,22 +1087,33 @@ class CC3Import(bpy.types.Operator): # But the mesh is really all we need, so just keep going... if colorspace.is_aces(): try: - bpy.ops.import_scene.fbx(filepath=filepath, - directory=dir, - use_anim=import_anim, - use_image_search=False, - use_custom_normals=True, - anim_offset=self.start_frame) + bpy.ops.wm.fbx_import(filepath=filepath, + directory=dir, + use_anim=import_anim, + use_custom_normals=not prefs.import_reset_custom_normals, + mtl_name_collision_mode="MAKE_UNIQUE", + anim_offset=self.start_frame) + #bpy.ops.import_scene.fbx(filepath=filepath, + # directory=dir, + # use_anim=import_anim, + # use_image_search=False, + # use_custom_normals=True, + # anim_offset=self.start_frame) except: utils.log_warn("FBX Import Error: This may be due to color space differences. Continuing...") else: try: - bpy.ops.import_scene.fbx(filepath=filepath, - directory=dir, - use_anim=import_anim, - use_image_search=False, - use_custom_normals=True, - anim_offset=self.start_frame) + bpy.ops.wm.fbx_import(filepath=filepath, + directory=dir, + use_anim=import_anim, + use_custom_normals=not prefs.import_reset_custom_normals, + anim_offset=self.start_frame) + #bpy.ops.import_scene.fbx(filepath=filepath, + # directory=dir, + # use_anim=import_anim, + # use_image_search=False, + # use_custom_normals=True, + # anim_offset=self.start_frame) except: utils.log_error("FBX Import Error due to bad mesh?") @@ -1153,10 +1179,7 @@ class CC3Import(bpy.types.Operator): # invoke the obj importer old_objects = utils.get_set(bpy.data.objects) old_images = utils.get_set(bpy.data.images) - if ImportFlags.RL in import_flags and param == "IMPORT_MORPH": - obj_import(filepath, split_objects=False, split_groups=False, vgroups=True) - else: - obj_import(filepath, split_objects=True, split_groups=True, vgroups=False) + obj_import(filepath, split_objects=True, split_groups=True, vgroups=False) imported = utils.get_set_new(bpy.data.objects, old_objects) self.imported_images = utils.get_set_new(bpy.data.images, old_images) @@ -1966,11 +1989,17 @@ class CC3ImportAnimations(bpy.types.Operator): old_actions = utils.get_set(bpy.data.actions) old_materials = utils.get_set(bpy.data.materials) - bpy.ops.import_scene.fbx(filepath=path, - directory=dir, - use_anim=True, - use_image_search=False, - anim_offset=self.start_frame) + bpy.ops.wm.fbx_import(filepath=path, + directory=dir, + use_anim=True, + use_custom_normals=False, + mtl_name_collision_mode="MAKE_UNIQUE", + anim_offset=self.start_frame) + #bpy.ops.import_scene.fbx(filepath=path, + # directory=dir, + # use_anim=True, + # use_image_search=False, + # anim_offset=self.start_frame) objects = utils.get_set_new(bpy.data.objects, old_objects) actions = utils.get_set_new(bpy.data.actions, old_actions) diff --git a/scripts/addons/cc_blender_tools-2_4_0/jsonutils.py b/scripts/addons/cc_blender_tools-2_4_2/jsonutils.py similarity index 98% rename from scripts/addons/cc_blender_tools-2_4_0/jsonutils.py rename to scripts/addons/cc_blender_tools-2_4_2/jsonutils.py index e9d83fb..eb0a155 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/jsonutils.py +++ b/scripts/addons/cc_blender_tools-2_4_2/jsonutils.py @@ -213,12 +213,13 @@ def set_facial_profile_categories_json(json_data, character_id, categories_json) def has_node_type(chr_json, node_type): - meshes_json = chr_json["Meshes"] - for obj_name, obj_json in meshes_json.items(): - if obj_json and "Materials" in obj_json: - for mat_name, mat_json in obj_json["Materials"].items(): - if "Node Type" in mat_json and mat_json["Node Type"] == node_type: - return True + if chr_json: + meshes_json = chr_json["Meshes"] + for obj_name, obj_json in meshes_json.items(): + if obj_json and "Materials" in obj_json: + for mat_name, mat_json in obj_json["Materials"].items(): + if "Node Type" in mat_json and mat_json["Node Type"] == node_type: + return True return False diff --git a/scripts/addons/cc_blender_tools-2_4_0/lib.py b/scripts/addons/cc_blender_tools-2_4_2/lib.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/lib.py rename to scripts/addons/cc_blender_tools-2_4_2/lib.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/link.py b/scripts/addons/cc_blender_tools-2_4_2/link.py similarity index 91% rename from scripts/addons/cc_blender_tools-2_4_0/link.py rename to scripts/addons/cc_blender_tools-2_4_2/link.py index f6906c0..efe5c72 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/link.py +++ b/scripts/addons/cc_blender_tools-2_4_2/link.py @@ -42,6 +42,7 @@ class OpCodes(IntEnum): FPS = 80 MORPH = 90 MORPH_UPDATE = 91 + MESH = 92 REPLACE_MESH = 95 MATERIALS = 96 CHARACTER = 100 @@ -104,33 +105,25 @@ VISEME_NAME_MAP = { class LinkActor(): - name: str = "Name" - chr_cache = None - object: bpy.types.Object = None - bones: list = None - meshes: list = None - id_tree: dict = None - id_map: dict = None - skin_meshes: dict = None - rig_bones: list = None - expressions: list = None - visemes: list = None - morphs: list = None - cache: dict = None - alias: list = None - shape_keys: dict = None - ik_store: dict = None - rigify_ik_fk: float = 0.0 - action_store_id: str = "" - def __init__(self, obj_or_chr_cache): - if type(obj_or_chr_cache) is bpy.types.Object: - self.object = obj_or_chr_cache - self.name = obj_or_chr_cache.name + def __init__(self, obj_or_actor_cache): + props = vars.props() + self.name = "" + self.object = None + self.actor_cache = None + if type(obj_or_actor_cache) is bpy.types.Object: + self.object = obj_or_actor_cache + rl_name = utils.get_prop(obj_or_actor_cache, "rl_name") + self.name = rl_name if rl_name else obj_or_actor_cache.name + if self.object.type == "LIGHT" or self.object.type == "CAMERA": + # force create an RLX Cache for staging cache + self.actor_cache = self.get_rlx_cache(create=True) + else: + self.actor_cache = props.get_character_cache(self.object) else: - self.object = None - self.chr_cache = obj_or_chr_cache - self.name = obj_or_chr_cache.character_name + self.object = obj_or_actor_cache.get_primary_object() + self.actor_cache = obj_or_actor_cache + self.name = obj_or_actor_cache.get_name() self.bones = [] self.meshes = [] self.id_tree = None @@ -143,79 +136,93 @@ class LinkActor(): self.cache = None self.alias = [] self.shape_keys = {} + self.ik_store = None + self.rigify_ik_fk: float = 0.0 + self.action_store_id: str = "" return - def get_chr_cache(self): - return self.chr_cache + def get_actor_cache(self): + return self.actor_cache - def get_import_modes(self) -> Tuple[str, str]: - """return (action_mode, frame_mode)\n - action_mode = {"NEW", "REPLACE", "BLEND"}\n - frame_mode = {"START", "CURRENT", "MATCH"} - """ - chr_cache = self.get_chr_cache() - if chr_cache: - return chr_cache.action_options.get_action_mode(), chr_cache.action_options.get_frame_mode() - else: - props = vars.props() - action_mode = props.action_options.get_action_mode() - frame_mode = props.action_options.get_frame_mode() - return action_mode, frame_mode + def get_chr_cache(self): + props = vars.props() + if self.object: + if self.actor_cache.cache_type() in ["AVATAR", "PROP"]: + return self.actor_cache + if self.object.type == "ARMATURE" or self.object.type == "MESH": + self.actor_cache = props.get_character_cache(self.object) + return self.actor_cache + return None + + def get_rlx_cache(self, create=False): + props = vars.props() + if self.object: + if self.actor_cache and self.actor_cache.cache_type() in ["LIGHT", "CAMERA"]: + return self.actor_cache + if self.object.type == "LIGHT" or self.object.type == "CAMERA": + self.actor_cache = props.get_staging_cache(self.object, create=create) + return self.actor_cache + return None def get_link_id(self): if self.object: return utils.get_rl_link_id(self.object) - elif self.chr_cache: - return self.chr_cache.get_link_id() + elif self.actor_cache: + return self.actor_cache.get_link_id() return None + def get_primary_object(self): + if self.actor_cache: + return self.actor_cache.get_primary_object() + return self.object + def get_armature(self): - if self.chr_cache: - return self.chr_cache.get_armature() + if self.actor_cache: + return self.actor_cache.get_armature() return None def select(self): - if self.chr_cache: - self.chr_cache.select_all() + if self.actor_cache: + self.actor_cache.select_all() elif self.object: utils.try_select_object(self.object) def get_type(self): """AVATAR|PROP|LIGHT|CAMERA|NONE""" - if self.chr_cache: - return self.chr_cache_type(self.chr_cache) + if self.actor_cache: + return self.chr_cache_type(self.actor_cache) elif self.object: return self.object.type return "NONE" def add_alias(self, link_id): - chr_cache = self.get_chr_cache() - if chr_cache: - actor_link_id = chr_cache.link_id + actor_cache = self.get_actor_cache() + if actor_cache: + actor_link_id = actor_cache.link_id if not actor_link_id: - utils.log_info(f"Assigning actor link_id: {chr_cache.character_name}: {link_id}") - chr_cache.set_link_id(link_id) + utils.log_info(f"Assigning actor link_id: {actor_cache.get_name()}: {link_id}") + actor_cache.set_link_id(link_id) return if link_id not in self.alias and actor_link_id != link_id: - utils.log_info(f"Assigning actor alias: {chr_cache.character_name}: {link_id}") + utils.log_info(f"Assigning actor alias: {actor_cache.get_name()}: {link_id}") self.alias.append(link_id) return @staticmethod def get_start_frame(actor: 'LinkActor', ccic_frame, blender_current): props = vars.props() - chr_cache = actor.get_chr_cache() if actor else None - if chr_cache: - return chr_cache.action_options.get_start_frame(ccic_frame, blender_current) + actor_cache = actor.get_actor_cache() if actor else None + if actor_cache: + return actor_cache.action_options.get_start_frame(ccic_frame, blender_current) else: return props.action_options.get_start_frame(ccic_frame, blender_current) @staticmethod def get_sequence_frame(actor: 'LinkActor', ccic_frame, ccic_frame_start, blender_current): props = vars.props() - chr_cache = actor.get_chr_cache() if actor else None - if chr_cache: - return chr_cache.action_options.get_sequence_frame(ccic_frame, ccic_frame_start, blender_current) + actor_cache = actor.get_actor_cache() if actor else None + if actor_cache: + return actor_cache.action_options.get_sequence_frame(ccic_frame, ccic_frame_start, blender_current) else: return props.action_options.get_sequence_frame(ccic_frame, ccic_frame_start, blender_current) @@ -232,7 +239,7 @@ class LinkActor(): if obj.type == "LIGHT" or obj.type == "CAMERA": if not search_type or (obj.type == search_type): obj_link_id = utils.get_rl_link_id(obj) - if obj_link_id is not None and link_id == obj_link_id: + if obj_link_id is not None and link_id is not None and link_id == obj_link_id: actor = LinkActor(obj) utils.log_detail(f"Staging (Light/Camera) found by link_id: {actor.name} / {link_id}") return actor @@ -251,7 +258,7 @@ class LinkActor(): chr_cache = props.find_character_by_name(search_name) if chr_cache: if not search_type or LinkActor.chr_cache_type(chr_cache) == search_type: - utils.log_detail(f"Chr found by name: {chr_cache.character_name} / {chr_cache.link_id} -> {link_id}") + utils.log_detail(f"Chr found by name: {chr_cache.get_name()} / {chr_cache.link_id} -> {link_id}") actor = LinkActor(chr_cache) actor.add_alias(link_id) return actor @@ -270,7 +277,7 @@ class LinkActor(): if context_chr_cache and context_chr_cache.is_avatar(): chr_cache = context_chr_cache if chr_cache: - utils.log_detail(f"Falling back to first Chr Avatar: {chr_cache.character_name} / {chr_cache.link_id} -> {link_id}") + utils.log_detail(f"Falling back to first Chr Avatar: {chr_cache.get_name()} / {chr_cache.link_id} -> {link_id}") actor = LinkActor(chr_cache) actor.add_alias(link_id) return actor @@ -393,20 +400,20 @@ class LinkActor(): def update_name(self, new_name): self.name = new_name - chr_cache = self.get_chr_cache() - if chr_cache: - chr_cache.character_name = new_name + actor_cache = self.get_actor_cache() + if actor_cache: + actor_cache.set_name(new_name) def update_link_id(self, new_link_id): - chr_cache = self.get_chr_cache() - if chr_cache: - utils.log_info(f"Assigning new link_id: {chr_cache.character_name}: {new_link_id}") - chr_cache.set_link_id(new_link_id) + actor_cache = self.get_actor_cache() + if actor_cache: + utils.log_info(f"Assigning new link_id: {actor_cache.get_name()}: {new_link_id}") + actor_cache.set_link_id(new_link_id) def ready(self, require_cache=True): if require_cache and not self.cache: return False - return (self.chr_cache and self.get_armature()) or self.object + return (self.actor_cache and self.get_armature()) or self.object def is_rigified(self): chr_cache = self.get_chr_cache() @@ -432,6 +439,21 @@ class LinkActor(): return chr_cache.can_go_ic() return False + def is_obj(self): + chr_cache = self.get_chr_cache() + if chr_cache: + return chr_cache.is_obj() + return False + + def is_posable(self): + actor_type = self.get_type() + if actor_type == "LIGHT" or actor_type == "CAMERA": + return True + else: + chr_cache = self.get_chr_cache() + if chr_cache: + return chr_cache.get_armature() is not None + return False class LinkData(): actors: list = [] @@ -441,11 +463,9 @@ class LinkData(): sequence_end_frame: int = 0 sequence_actors: list = None sequence_type: str = None + sequence_selection: List[bpy.types.Object] = None scene_current_frame: int = 0 # - sequence_action_mode: str = "" - sequence_frame_mode: str = "" - # preview_shape_keys: bool = True preview_skip_frames: bool = False # remote props @@ -645,7 +665,7 @@ def make_datalink_import_rig(actor: LinkActor, objects: list): utils.restore_render_visibility_state(RV) return chr_cache.rig_datalink_rig - rig_name = f"{chr_cache.character_name}_Link_Rig" + rig_name = f"{chr_cache.get_name()}_Link_Rig" utils.log_info(f"Creating datalink transfer rig: {rig_name}") # create pose armature @@ -883,14 +903,16 @@ def prep_pose_actor(actor: LinkActor, start_frame, end_frame): motion_id = "Pose" if LINK_DATA.sequence_type == "POSE" else "Sequence" chr_cache = actor.get_chr_cache() - - action_mode, frame_mode = actor.get_import_modes() + rigutils.ensure_motion_set(actor.get_primary_object(), motion_id, LINK_DATA.motion_prefix) if actor and actor.get_type() == "LIGHT": # create keyframe cache for light animation sequences if LINK_DATA.set_keyframes: + # store current actions for replacing or mixing + actor.action_store_id = props.store_actions(actor.object) + rlx.prep_rlx_actions(actor, motion_id, timestamp=False, motion_prefix=LINK_DATA.motion_prefix) @@ -927,6 +949,9 @@ def prep_pose_actor(actor: LinkActor, start_frame, end_frame): # create keyframe cache for camera animation sequences if LINK_DATA.set_keyframes: + # store current actions for replacing or mixing + actor.action_store_id = props.store_actions(actor.object) + rlx.prep_rlx_actions(actor, motion_id, timestamp=False, motion_prefix=LINK_DATA.motion_prefix) @@ -950,7 +975,6 @@ def prep_pose_actor(actor: LinkActor, start_frame, end_frame): camera_cache["focus_distance"] = create_fcurves_cache(count, 1, [1]) camera_cache["f_stop"] = create_fcurves_cache(count, 1, [2.8]) actor.set_cache(actor_cache) - actor.action_store_id = props.store_actions(actor.object) else: # when not setting keyframes remove all actions from the camera @@ -972,7 +996,7 @@ def prep_pose_actor(actor: LinkActor, start_frame, end_frame): objects, none_objects = actor.get_sequence_objects() if rig: rig_id = rigutils.get_rig_id(rig) - rl_arm_id = utils.get_rl_object_id(rig) + rl_id = utils.get_rl_id(rig) use_slotted = prefs.use_action_slots() utils.log_info(f"Preparing Character Rig: {actor.name} {rig_id} / {len(actor.bones)} bones") @@ -980,12 +1004,12 @@ def prep_pose_actor(actor: LinkActor, start_frame, end_frame): # generate new action set data # always import into a new action set, then decide what to do with it after writing the action - motion_id = rigutils.get_unique_set_motion_id(rig_id, motion_id, LINK_DATA.motion_prefix, slotted=use_slotted) + motion_id = rigutils.get_unique_set_motion_id(rig_id, motion_id, LINK_DATA.motion_prefix, slotted=use_slotted, rlob=rig) set_id, set_generation = rigutils.generate_motion_set(rig, motion_id, LINK_DATA.motion_prefix) # rig action action = get_datalink_rig_action(rig, motion_id, slotted=use_slotted) - rigutils.add_motion_set_data(action, set_id, set_generation, arm_id=rl_arm_id, slotted=use_slotted) + rigutils.add_motion_set_data(action, set_id, set_generation, arm_id=rl_id, slotted=use_slotted) utils.log_info(f"Preparing rig action: {action.name}") # shape key actions @@ -1003,7 +1027,7 @@ def prep_pose_actor(actor: LinkActor, start_frame, end_frame): utils.log_info(f"Preparing shape key action: {action_name} / {num_expressions}+{num_visemes} shape keys") action = bpy.data.actions.new(action_name) slot, channel = rigutils.add_action_key_slot_channelbag(action, obj) - rigutils.add_motion_set_data(action, set_id, set_generation, obj_id=obj_id) + rigutils.add_motion_set_data(action, set_id, set_generation, key_id=obj_id, arm_id=rl_id) utils.safe_set_action(obj.data.shape_keys, action, slot=slot) action.use_fake_user = LINK_DATA.use_fake_user # remove actions from non sequence objects @@ -1386,17 +1410,17 @@ def write_sequence_actions(actor: LinkActor, num_frames, start_frame): utils.safe_set_action(obj.data.shape_keys, None) if rig_action: - action_mode, frame_mode = actor.get_import_modes() rigutils.load_motion_set(rig, rig_action) - rigutils.finalize_motion_import(rig, rig_action, actor.action_store_id, action_mode) + rigutils.finalize_motion_import(actor.get_chr_cache(), rig, rig_action, actor.action_store_id, + overwrite=True) elif actor.get_type() == "LIGHT": light = actor.object ob_action = utils.safe_get_action(light) light_action = utils.safe_get_action(light.data) - ob_slot = utils.get_action_slot(ob_action, "OBJECT") - light_slot = utils.get_action_slot(light_action, "LIGHT") + ob_slot = utils.get_action_slot(ob_action, slot_type="OBJECT") + light_slot = utils.get_action_slot(light_action, slot_type="LIGHT") write_action_cache_curve(ob_action, actor.cache["transform"], "loc", "location", num_frames, "Location", slot=ob_slot, reduce=True) write_action_rotation_cache_curve(ob_action, actor.cache["transform"], "rot", light, num_frames, slot=ob_slot, reduce=True) write_action_cache_curve(ob_action, actor.cache["transform"], "sca", "scale", num_frames, "Scale", slot=ob_slot, reduce=True) @@ -1410,16 +1434,16 @@ def write_sequence_actions(actor: LinkActor, num_frames, start_frame): utils.safe_set_action(light, ob_action, slot=ob_slot) utils.safe_set_action(light.data, light_action, slot=light_slot) - action_mode, frame_mode = actor.get_import_modes() - rigutils.finalize_rlx_import(light, [ob_action, light_action], actor.action_store_id, action_mode) + rlx_cache = actor.get_rlx_cache(create=True) + rigutils.finalize_rlx_import(rlx_cache, light, [ob_action, light_action], actor.action_store_id, overwrite=True) elif actor.get_type() == "CAMERA": camera = actor.object ob_action = utils.safe_get_action(camera) cam_action = utils.safe_get_action(camera.data) - ob_slot = utils.get_action_slot(ob_action, "OBJECT") - cam_slot = utils.get_action_slot(cam_action, "CAMERA") + ob_slot = utils.get_action_slot(ob_action, slot_type="OBJECT") + cam_slot = utils.get_action_slot(cam_action, slot_type="CAMERA") write_action_cache_curve(ob_action, actor.cache["transform"], "loc", "location", num_frames, "Location", slot=ob_slot, reduce=True) write_action_rotation_cache_curve(ob_action, actor.cache["transform"], "rot", camera, num_frames, slot=ob_slot, reduce=True) write_action_cache_curve(ob_action, actor.cache["transform"], "sca", "scale", num_frames, "Scale", slot=ob_slot, reduce=True) @@ -1431,8 +1455,8 @@ def write_sequence_actions(actor: LinkActor, num_frames, start_frame): utils.safe_set_action(camera, ob_action, slot=ob_slot) utils.safe_set_action(camera.data, cam_action, slot=cam_slot) - action_mode, frame_mode = actor.get_import_modes() - rigutils.finalize_rlx_import(camera, [ob_action, cam_action], actor.action_store_id, action_mode) + rlx_cache = actor.get_rlx_cache(create=True) + rigutils.finalize_rlx_import(rlx_cache, camera, [ob_action, cam_action], actor.action_store_id) actor.clear_cache() @@ -1885,6 +1909,9 @@ class LinkService(): elif op_code == OpCodes.MORPH_UPDATE: self.receive_morph(data, update=True) + elif op_code == OpCodes.MESH: + self.receive_mesh_modify(data) + elif op_code == OpCodes.CHARACTER: self.receive_actor_import(data) @@ -2258,6 +2285,13 @@ class LinkService(): return actor return None + def actors_contains_id(self, actors, link_id): + actor: LinkActor = None + for actor in actors: + if actor.get_link_id() == link_id: + return True + return False + def get_selected_actors(self): global LINK_DATA props = vars.props() @@ -2282,9 +2316,12 @@ class LinkService(): actors.append(actor) for obj in selected_objects: - if obj.type == "LIGHT" or obj.type == "CAMERA" and utils.get_rl_link_id(obj): - actor = LinkActor(obj) - actors.append(actor) + link_id = utils.get_rl_link_id(obj) + if link_id: + if obj.type == "LIGHT" or obj.type == "CAMERA" or utils.get_prop(obj, "rl_mesh_modify"): + if not self.actors_contains_id(actors, link_id): + actor = LinkActor(obj) + actors.append(actor) return actors @@ -2418,12 +2455,59 @@ class LinkService(): use_normals=True, keep_vertex_order=keep_vertex_order) - def send_replace_mesh(self): + def send_mesh_modify_request(self): + self.send_request("MESH_MODIFY") + + def send_mesh_modify(self): + global LINK_DATA state = utils.store_mode_selection_state() - objects = utils.get_selected_meshes() + objects = utils.get_selected_meshes(selection=LINK_DATA.sequence_selection) + count = 0 + for obj in objects: + if obj.type == "MESH": + if utils.get_prop(obj, "rl_mesh_modify"): + link_id = utils.get_prop(obj, "rl_link_id") + type = utils.get_prop(obj, "rl_type") + name = utils.get_prop(obj, "rl_name") + if link_id: + object_name = utils.strip_name(obj.name) + mesh_name = utils.strip_name(obj.data.name) + export_path = self.get_export_path("Meshes", f"{obj.name}_mesh.obj", + reuse_folder=True, reuse_file=True) + utils.set_active_object(obj, deselect_all=True) + self.obj_export(export_path, use_selection=True, use_vertex_colors=False) + export_data = encode_from_json({ + "path": export_path, + "actor_name": name, + "object_name": object_name, + "mesh_name": mesh_name, + "type": type, + "morph": False, + "link_id": link_id, + }) + self.send(OpCodes.REPLACE_MESH, export_data) + update_link_status(f"Sent Mesh: {obj.name}") + count += 1 + + utils.restore_mode_selection_state(state) + + LINK_DATA.sequence_selection = None + LINK_DATA.sequence_actors = None + LINK_DATA.sequence_type = None + + return count + + def send_replace_mesh_request(self): + self.send_request("REPLACE_MESH") + + def send_replace_mesh(self): + global LINK_DATA + state = utils.store_mode_selection_state() + objects = utils.get_selected_meshes(selection=LINK_DATA.sequence_selection) # important that character is in the exact same pose on both sides, # so make sure the character is on the same frame in the animation. - self.send_frame_sync() + # send pose updates for actors + self.send_pose() count = 0 for obj in objects: if obj.type == "MESH": @@ -2445,6 +2529,7 @@ class LinkService(): "object_name": object_name, "mesh_name": mesh_name, "type": actor.get_type(), + "morph": actor.is_obj(), "link_id": actor.get_link_id(), }) self.send(OpCodes.REPLACE_MESH, export_data) @@ -2525,6 +2610,8 @@ class LinkService(): actor: LinkActor for actor in actors: + if not actor.is_posable(): continue + if actor.get_type() in ["PROP", "AVATAR"]: chr_cache = actor.get_chr_cache() @@ -2643,19 +2730,24 @@ class LinkService(): } actor: LinkActor for actor in actors: - actors_data.append({ - "name": actor.name, - "type": actor.get_type(), - "link_id": actor.get_link_id(), - }) + if actor.is_posable(): + actors_data.append({ + "name": actor.name, + "type": actor.get_type(), + "link_id": actor.get_link_id(), + }) return encode_from_json(data) def encode_pose_frame_data(self, actors: list): pose_bone: bpy.types.PoseBone data = bytearray() - data += struct.pack("!II", len(actors), BFA(bpy.context.scene.frame_current)) + pose_actors = [ a for a in actors if a.is_posable() ] + data += struct.pack("!II", len(pose_actors), BFA(bpy.context.scene.frame_current)) actor: LinkActor - for actor in actors: + for actor in pose_actors: + + if not actor.is_posable(): continue + actor_type = actor.get_type() data += pack_string(actor.name) @@ -2789,11 +2881,12 @@ class LinkService(): } actor: LinkActor for actor in actors: - actors_data.append({ - "name": actor.name, - "type": actor.get_type(), - "link_id": actor.get_link_id(), - }) + if actor.is_posable(): + actors_data.append({ + "name": actor.name, + "type": actor.get_type(), + "link_id": actor.get_link_id(), + }) return encode_from_json(data) def restore_actor_rigs(self, actors: LinkActor): @@ -2808,20 +2901,22 @@ class LinkService(): # remove the export rigs utils.delete_armature_object(chr_cache.rig_export_rig) - def send_request(self, request_type): + def send_request(self, request_type, actors=None): global LINK_DATA # get actors - actors = self.get_selected_actors() + if not actors: + actors = self.get_selected_actors() if actors: mode_selection = utils.store_mode_selection_state() update_link_status(f"Sending Request") self.send_notify(f"Request") # send request - pose_data = self.encode_request_data(actors, request_type) - self.send(OpCodes.REQUEST, pose_data) + request_data = self.encode_request_data(actors, request_type) + self.send(OpCodes.REQUEST, request_data) # store the actors LINK_DATA.sequence_actors = actors LINK_DATA.sequence_type = request_type + LINK_DATA.sequence_selection = bpy.context.selected_objects.copy() # restore utils.restore_mode_selection_state(mode_selection) @@ -2876,6 +2971,10 @@ class LinkService(): self.send_pose() elif request_type == "SEQUENCE": self.send_sequence() + elif request_type == "REPLACE_MESH": + self.send_replace_mesh() + elif request_type == "MESH_MODIFY": + self.send_mesh_modify() return def send_pose(self): @@ -2883,7 +2982,7 @@ class LinkService(): # get actors if not LINK_DATA.sequence_actors: - LINK_DATA.sequence_actors = self.get_selected_actors() + LINK_DATA.sequence_actors = self.get_selected_actors(pose_only=True) actors = LINK_DATA.sequence_actors if actors: @@ -2908,6 +3007,7 @@ class LinkService(): self.restore_actor_rigs(LINK_DATA.sequence_actors) LINK_DATA.sequence_actors = None LINK_DATA.sequence_type = None + LINK_DATA.sequence_selection = None # restore utils.restore_mode_selection_state(mode_selection) @@ -2930,7 +3030,7 @@ class LinkService(): # get actors if not LINK_DATA.sequence_actors: - LINK_DATA.sequence_actors = self.get_selected_actors() + LINK_DATA.sequence_actors = self.get_selected_actors(pose_only=True) actors = LINK_DATA.sequence_actors if actors: @@ -2978,6 +3078,7 @@ class LinkService(): self.restore_actor_rigs(LINK_DATA.sequence_actors) LINK_DATA.sequence_actors = None LINK_DATA.sequence_type = None + LINK_DATA.sequence_selection = None def send_sequence_ack(self, frame): global LINK_DATA @@ -3002,10 +3103,7 @@ class LinkService(): offset = 0 count, frame = struct.unpack_from("!II", pose_data, offset) frame = RLFA(frame) - opt_frame = LinkActor.get_sequence_frame(None, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) - opt_start_frame = LinkActor.get_sequence_frame(None, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) - if LINK_DATA.set_keyframes: - ensure_current_frame(opt_frame) + least_frame = None LINK_DATA.sequence_current_frame = frame offset = 8 actors = [] @@ -3030,6 +3128,15 @@ class LinkService(): rig = None is_prop = False + if actor: + opt_frame = LinkActor.get_sequence_frame(actor, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + opt_start_frame = LinkActor.get_sequence_frame(actor, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + least_frame = opt_frame if least_frame is None or opt_frame < least_frame else least_frame + else: + opt_frame = LinkActor.get_sequence_frame(None, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + opt_start_frame = LinkActor.get_sequence_frame(None, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + least_frame = opt_frame + # unpack rig transform tx,ty,tz,rx,ry,rz,rw,sx,sy,sz = struct.unpack_from("!ffffffffff", pose_data, offset) loc = Vector((tx, ty, tz)) * 0.01 @@ -3124,6 +3231,12 @@ class LinkService(): if rig: rig.pose.bones.update() + if least_frame is None: + least_frame = LinkActor.get_sequence_frame(None, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + + if LINK_DATA.set_keyframes: + ensure_current_frame(least_frame) + return actors def reposition_prop_meshes(self, actors): @@ -3457,11 +3570,18 @@ class LinkService(): else: actors = None + least_start_frame = None + most_end_frame = None + least_current_frame = None + if actors: for actor in actors: opt_start_frame = LinkActor.get_sequence_frame(actor, start_frame, start_frame, current_frame) opt_end_frame = LinkActor.get_sequence_frame(actor, end_frame, start_frame, current_frame) opt_frame = LinkActor.get_sequence_frame(actor, frame, start_frame, current_frame) + least_start_frame = opt_start_frame if least_start_frame is None or opt_start_frame < least_start_frame else least_start_frame + most_end_frame = opt_end_frame if most_end_frame is None or opt_end_frame > most_end_frame else most_end_frame + least_current_frame = opt_frame if least_current_frame is None or opt_frame < least_current_frame else least_current_frame if expand_range: if opt_start_frame < bpy.context.scene.frame_start: bpy.context.scene.frame_start = opt_start_frame @@ -3473,25 +3593,24 @@ class LinkService(): if opt_frame > bpy.context.scene.frame_end: bpy.context.scene.frame_end = opt_frame - opt_start_frame = LinkActor.get_sequence_frame(actors[0], start_frame, start_frame, current_frame) - opt_end_frame = LinkActor.get_sequence_frame(actors[0], end_frame, start_frame, current_frame) - opt_frame = LinkActor.get_sequence_frame(actors[0], frame, start_frame, current_frame) - else: - opt_start_frame = LinkActor.get_sequence_frame(None, start_frame, start_frame, current_frame) - opt_end_frame = LinkActor.get_sequence_frame(None, end_frame, start_frame, current_frame) - opt_frame = LinkActor.get_sequence_frame(None, frame, start_frame, current_frame) + if least_start_frame is None: + least_start_frame = LinkActor.get_sequence_frame(None, start_frame, start_frame, current_frame) + if most_end_frame is None: + most_end_frame = LinkActor.get_sequence_frame(None, end_frame, start_frame, current_frame) + if least_current_frame is None: + least_current_frame = LinkActor.get_sequence_frame(None, frame, start_frame, current_frame) if set_preview: - set_frame_range(opt_start_frame, opt_end_frame, preview=True) + set_frame_range(least_start_frame, most_end_frame, preview=True) if set_current: - set_frame(opt_frame) + set_frame(least_current_frame) elif set_start: - set_frame(opt_start_frame) + set_frame(least_start_frame) elif set_end: - set_frame(opt_end_frame) + set_frame(most_end_frame) - return opt_frame, opt_start_frame, opt_end_frame + return least_current_frame, least_start_frame, most_end_frame def receive_pose(self, data): props = vars.props() @@ -3526,9 +3645,8 @@ class LinkService(): if actor: actors.append(actor) - opt_frame, opt_start_frame, opt_end_frame = \ - self.get_actors_frame_range(None, frame, start_frame, end_frame, current_frame, - expand_frame=True, set_current=True) + self.get_actors_frame_range(actors, frame, start_frame, end_frame, current_frame, + expand_frame=True, set_current=True) # set pose frame update_link_status(f"Receiving Pose Frame: {frame}") @@ -3547,9 +3665,6 @@ class LinkService(): actors = self.decode_pose_frame_data(data) utils.log_info(f"Receive Pose Frame: {frame}") - opt_frame = LinkActor.get_sequence_frame(None, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) - opt_start_frame = LinkActor.get_sequence_frame(None, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) - # force recalculate all transforms (lights and cameras seem to need this) bpy.context.view_layer.update() self.reposition_prop_meshes(actors) @@ -3558,9 +3673,17 @@ class LinkService(): update_link_status(f"Pose Frame: {frame}") self.select_actor_rigs(actors) + opt_frame = LinkActor.get_sequence_frame(None, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + opt_start_frame = LinkActor.get_sequence_frame(None, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + + least_frame = None + actor: LinkActor if LINK_DATA.set_keyframes: for actor in actors: + opt_frame = LinkActor.get_sequence_frame(actor, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + opt_start_frame = LinkActor.get_sequence_frame(actor, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + least_frame = opt_frame if least_frame is None or opt_frame < least_frame else least_frame if actor.ready(require_cache=LINK_DATA.set_keyframes): if actor.get_type() == "PROP" or actor.get_type() == "AVATAR": store_bone_cache_keyframes(actor, opt_frame, opt_start_frame) @@ -3569,9 +3692,13 @@ class LinkService(): elif actor.get_type() == "CAMERA": store_camera_cache_keyframes(actor, opt_frame, opt_start_frame) + if least_frame is None: + least_frame = LinkActor.get_sequence_frame(None, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + # write pose action for actor in actors: if actor.ready(require_cache=LINK_DATA.set_keyframes): + opt_start_frame = LinkActor.get_sequence_frame(actor, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) if LINK_DATA.set_keyframes: write_sequence_actions(actor, 1, opt_start_frame) if actor.get_type() == "PROP" or actor.get_type() == "AVATAR": @@ -3585,8 +3712,9 @@ class LinkService(): # finish LINK_DATA.sequence_actors = None LINK_DATA.sequence_type = None + LINK_DATA.sequence_selection = None if LINK_DATA.set_keyframes: - bpy.context.scene.frame_current = opt_frame + bpy.context.scene.frame_current = least_frame utils.restore_mode_selection_state(state, include_frames=False) # doesn't work with existing actions, the pose is reset back to action after execution. @@ -3630,9 +3758,8 @@ class LinkService(): if actor: actors.append(actor) - opt_frame, opt_start_frame, opt_end_frame = \ - self.get_actors_frame_range(None, frame, start_frame, end_frame, current_frame, - expand_range=True, set_preview=True, set_start=True) + self.get_actors_frame_range(actors, frame, start_frame, end_frame, current_frame, + expand_range=True, set_preview=True, set_start=True) LINK_DATA.sequence_actors = actors LINK_DATA.sequence_type = "SEQUENCE" @@ -3667,9 +3794,6 @@ class LinkService(): actors = self.decode_pose_frame_data(data) utils.update_timer("DECODE") - opt_frame = LinkActor.get_sequence_frame(None, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) - opt_start_frame = LinkActor.get_sequence_frame(None, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) - utils.mark_timer("REPOSITION") self.reposition_prop_meshes(actors) utils.update_timer("REPOSITION") @@ -3690,6 +3814,8 @@ class LinkService(): for actor in actors: if actor.ready(require_cache=LINK_DATA.set_keyframes): if LINK_DATA.set_keyframes: + opt_frame = LinkActor.get_sequence_frame(actor, frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + opt_start_frame = LinkActor.get_sequence_frame(actor, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) if actor.get_type() == "PROP" or actor.get_type() == "AVATAR": store_bone_cache_keyframes(actor, opt_frame, opt_start_frame) elif actor.get_type() == "LIGHT": @@ -3731,11 +3857,13 @@ class LinkService(): else: update_link_status(f"Live Sequence Aborted!") - opt_start_frame = LinkActor.get_sequence_frame(None, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + least_start_frame = None # write actions utils.mark_timer("WRITE") for actor in actors: + opt_start_frame = LinkActor.get_sequence_frame(actor, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + least_start_frame = opt_start_frame if least_start_frame is None or opt_start_frame < least_start_frame else least_start_frame if LINK_DATA.set_keyframes: write_sequence_actions(actor, num_frames, opt_start_frame) if actor.get_type() == "PROP" or actor.get_type() == "AVATAR": @@ -3744,6 +3872,10 @@ class LinkService(): rigutils.update_prop_rig(actor.get_armature()) elif actor.get_type() == "AVATAR": rigutils.update_avatar_rig(actor.get_armature()) + + if least_start_frame is None: + least_start_frame = LinkActor.get_sequence_frame(None, LINK_DATA.sequence_start_frame, LINK_DATA.sequence_start_frame, LINK_DATA.scene_current_frame) + utils.update_timer("WRITE") utils.log_timer("Frame", name="FRAME") @@ -3758,6 +3890,7 @@ class LinkService(): self.stop_sequence() LINK_DATA.sequence_actors = None LINK_DATA.sequence_type = None + LINK_DATA.sequence_selection = None #bpy.context.scene.frame_current = LINK_DATA.sequence_start_frame # play the recorded sequence @@ -3797,7 +3930,9 @@ class LinkService(): else: self.update_sequence(5, delta_frames) - def get_remote_file(self, remote_id, source_path, file_override=None): + def get_remote_file(self, remote_id: str, source_path: str, file_override=None): + if os.path.sep != "\\": + source_path = source_path.replace("\\", os.path.sep) if remote_id: remote_files_folder = get_unpacked_tar_file_folder(remote_id) if file_override: @@ -3991,6 +4126,7 @@ class LinkService(): character_type = character_types[i] file = name + ".rlx" rlx_path = self.get_remote_file(remote_id, base_path, file_override=file) + actor = LinkActor.find_actor(link_id, search_name=name, search_type=character_type) utils.log_info(f"Receive Light / Camera Import: {name} / {link_id} / {rlx_path}") @@ -3999,6 +4135,7 @@ class LinkService(): utils.log_error(f"Invalid Import Path: {rlx_path}") continue + opt_start_frame = LinkActor.get_sequence_frame(actor, start_frame, start_frame, current_frame) self.do_file_import(rlx_path, link_id, save_after_import, opt_start_frame) def receive_motion_import(self, data): @@ -4043,13 +4180,13 @@ class LinkService(): if chr_cache and LinkActor.chr_cache_type(chr_cache) == character_type: link_id = chr_cache.link_id if link_id: - utils.log_info(f"Redirecting to active character: {chr_cache.character_name}") + utils.log_info(f"Redirecting to active character: {chr_cache.get_name()}") opt_frame, opt_start_frame, opt_end_frame = \ self.get_actors_frame_range(chr_cache, frame, start_frame, end_frame, current_frame, expand_range=True, set_preview=True, set_start=True) if prefs.datalink_confirm_mismatch: bpy.ops.ccic.link_confirm_dialog("INVOKE_DEFAULT", - message=f"Character {name} not found, do you want to apply the motion to the current character: {chr_cache.character_name}?", + message=f"Character {name} not found, do you want to apply the motion to the current character: {chr_cache.get_name()}?", mode="MOTION", name=name, filepath=fbx_path, @@ -4086,7 +4223,7 @@ class LinkService(): use_fake_user=LINK_DATA.use_fake_user) except Exception as e: utils.log_error(f"Error importing {fbx_path}", e) - motion_rig = utils.get_active_object() + motion_rig = utils.first(utils.get_selected_armatures()) if motion_rig: self.replace_actor_motion(actor, motion_rig) update_link_status(f"Motion Imported: {actor.name}") @@ -4110,12 +4247,12 @@ class LinkService(): actor_rig = actor.get_armature() chr_cache = actor.get_chr_cache() actor_rig_id = rigutils.get_rig_id(actor_rig) - arm_id = utils.get_rl_object_id(actor_rig) + rl_id = utils.get_rl_id(actor_rig) # store existing motion action_store_id = props.store_actions(actor_rig) # generate new action set data - motion_id = rigutils.get_unique_set_motion_id(actor_rig_id, motion_id, LINK_DATA.motion_prefix, slotted=use_slotted) + motion_id = rigutils.get_unique_set_motion_id(actor_rig_id, motion_id, LINK_DATA.motion_prefix, slotted=use_slotted, rlob=actor_rig) set_id, set_generation = rigutils.generate_motion_set(actor_rig, motion_id, LINK_DATA.motion_prefix) remove_actions = [] @@ -4130,7 +4267,7 @@ class LinkService(): update_link_status(f"Retargeting Motion...") baked_action = rigutils.bake_rig_action_from_source(motion_rig, actor_rig) baked_action.use_fake_user = LINK_DATA.use_fake_user - rigutils.add_motion_set_data(baked_action, set_id, set_generation, arm_id=arm_id, slotted=use_slotted) + rigutils.add_motion_set_data(baked_action, set_id, set_generation, arm_id=rl_id, slotted=use_slotted) rigutils.set_armature_action_name(baked_action, actor_rig_id, motion_id, LINK_DATA.motion_prefix, slotted=use_slotted) rigutils.copy_action_shape_key_channels(actor_rig, motion_rig_action, baked_action, fake_user=LINK_DATA.use_fake_user) rigutils.delete_motion_set(motion_rig_action) @@ -4148,13 +4285,16 @@ class LinkService(): if chr_cache.rigified: update_link_status(f"Retargeting Motion...") baked_action = rigging.adv_bake_retarget_to_rigify(None, chr_cache, motion_rig, motion_rig_action) - baked_action.use_fake_user = LINK_DATA.use_fake_user - rigutils.add_motion_set_data(baked_action, set_id, set_generation, arm_id=arm_id, slotted=use_slotted) - rigutils.set_armature_action_name(baked_action, actor_rig_id, motion_id, LINK_DATA.motion_prefix, slotted=use_slotted) - rigutils.copy_action_shape_key_channels(actor_rig, motion_rig_action, baked_action, fake_user=LINK_DATA.use_fake_user) - rigutils.delete_motion_set(motion_rig_action) - rigutils.load_motion_set(actor_rig, baked_action) - incoming_motion_action = baked_action + if baked_action: + baked_action.use_fake_user = LINK_DATA.use_fake_user + rigutils.add_motion_set_data(baked_action, set_id, set_generation, arm_id=rl_id, slotted=use_slotted) + rigutils.set_armature_action_name(baked_action, actor_rig_id, motion_id, LINK_DATA.motion_prefix, slotted=use_slotted) + rigutils.copy_action_shape_key_channels(actor_rig, motion_rig_action, baked_action, fake_user=LINK_DATA.use_fake_user) + rigutils.delete_motion_set(motion_rig_action) + rigutils.load_motion_set(actor_rig, baked_action) + incoming_motion_action = baked_action + else: + utils.log_error(f"Unable to bake replacement action: {motion_rig} / {motion_rig_action}") else: actor_rig_action = utils.safe_get_action(actor_rig) motion_rig_action.use_fake_user = LINK_DATA.use_fake_user @@ -4168,8 +4308,7 @@ class LinkService(): utils.delete_armature_object(motion_rig) if actor_rig and incoming_motion_action: - action_mode, frame_mode = actor.get_import_modes() - rigutils.finalize_motion_import(actor_rig, incoming_motion_action, action_store_id, action_mode) + rigutils.finalize_motion_import(actor.get_chr_cache(), actor_rig, incoming_motion_action, action_store_id) utils.log_recess() @@ -4247,6 +4386,45 @@ class LinkService(): geom.copy_vert_positions_by_index(source, dest) utils.delete_mesh_object(source) + def receive_mesh_modify(self, data): + props = vars.props() + global LINK_DATA + + props.validate_and_clean_up() + + # decode receive morph + json_data = decode_to_json(data) + obj_path = json_data.get("path") + remote_id = json_data.get("remote_id") + obj_path = self.get_remote_file(remote_id, obj_path) + name = json_data["name"] + character_type = json_data["type"] + link_id = json_data["link_id"] + utils.log_info(f"Receive Mesh Modify: {name} / {link_id} / {obj_path}") + + update_link_status(f"Receving Mesh Modify: {name}") + if os.path.exists(obj_path): + mss = utils.store_mode_selection_state() + # remove old mesh modify + remove = [] + for obj in bpy.data.objects: + if utils.get_prop(obj, "rl_mesh_modify") and utils.get_prop(obj, "rl_link_id") == link_id: + remove.append(obj) + for obj in remove: + utils.delete_object(obj) + # import new mesh modify + old_objects = utils.get_set(bpy.data.objects) + importer.obj_import(obj_path, split_objects=True, split_groups=True, vgroups=False) + objects = utils.get_set_new(bpy.data.objects, old_objects) + # update mesh modify + for source in objects: + utils.set_rl_link_id(source, link_id) + utils.set_prop(source, "rl_mesh_modify", True) + utils.set_prop(source, "rl_name", name) + utils.set_prop(source, "rl_type", character_type) + source.scale = (0.01, 0.01, 0.01) + utils.restore_mode_selection_state(mss) + def receive_update_replace(self, data): props = vars.props() props.validate_and_clean_up() @@ -4395,7 +4573,7 @@ class LinkService(): if temp_obj_cache.source_name in original_data: utils.force_object_name(child, original_data[temp_obj_cache.source_name]["name"]) new_obj_cache.object_id = original_data[temp_obj_cache.source_name]["object_id"] - utils.set_rl_object_id(child, new_obj_cache.object_id) + utils.set_rl_id(child, new_obj_cache.object_id) for mat in child.data.materials: if utils.material_exists(mat): temp_mat_cache = temp_chr_cache.get_material_cache(mat) @@ -4502,17 +4680,17 @@ class LinkService(): rigutils.delete_motion_set(temp_rig_action) link_id = chr_cache.link_id - character_name = chr_cache.character_name + character_name = chr_cache.get_name() rig_name = rig.name rig_data_name = rig.data.name - rl_armature_id = utils.get_rl_object_id(rig) + rl_id = utils.get_rl_id(rig) temp_chr_cache.set_link_id(link_id) - temp_chr_cache.character_name = character_name + temp_chr_cache.set_name(character_name) - utils.set_rl_object_id(temp_rig, rl_armature_id) + utils.set_rl_id(temp_rig, rl_id) rig_obj_cache = temp_chr_cache.get_object_cache(temp_rig) if rig_obj_cache: - rig_obj_cache.object_id = rl_armature_id + rig_obj_cache.object_id = rl_id utils.force_object_name(temp_rig, rig_name) utils.force_armature_name(temp_rig.data, rig_data_name) @@ -4703,18 +4881,15 @@ class CCICDataLink(bpy.types.Operator): self.report({'ERROR'}, f"Morph not sent!") return {'FINISHED'} + elif self.param == "SEND_MESH_MODIFY": + LINK_SERVICE.send_mesh_modify_request() + elif self.param == "SYNC_CAMERA": LINK_SERVICE.send_camera_sync() return {'FINISHED'} elif self.param == "SEND_REPLACE_MESH": - count = LINK_SERVICE.send_replace_mesh() - if count == 1: - self.report({'INFO'}, f"Replace Mesh sent ...") - elif count > 1: - self.report({'INFO'}, f"{count} Replace Meshes sent ...") - else: - self.report({'ERROR'}, f"No Replace Meshes sent!") + LINK_SERVICE.send_replace_mesh_request() return {'FINISHED'} elif self.param == "SEND_MATERIAL_UPDATE": diff --git a/scripts/addons/cc_blender_tools-2_4_0/materials.py b/scripts/addons/cc_blender_tools-2_4_2/materials.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/materials.py rename to scripts/addons/cc_blender_tools-2_4_2/materials.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/meshutils.py b/scripts/addons/cc_blender_tools-2_4_2/meshutils.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/meshutils.py rename to scripts/addons/cc_blender_tools-2_4_2/meshutils.py index c177557..a49eb0d 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/meshutils.py +++ b/scripts/addons/cc_blender_tools-2_4_2/meshutils.py @@ -597,7 +597,7 @@ def get_head_body_object(chr_cache): if body_cache: body_id = body_cache.object_id for child in arm.children: - if utils.get_rl_object_id(child) == body_id and child not in body_objects: + if utils.get_rl_id(child) == body_id and child not in body_objects: body_objects[child] = total_vertex_group_weight(child, head_bones) else: for child in arm.children: diff --git a/scripts/addons/cc_blender_tools-2_4_0/modifiers.py b/scripts/addons/cc_blender_tools-2_4_2/modifiers.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/modifiers.py rename to scripts/addons/cc_blender_tools-2_4_2/modifiers.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/nodeutils.py b/scripts/addons/cc_blender_tools-2_4_2/nodeutils.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/nodeutils.py rename to scripts/addons/cc_blender_tools-2_4_2/nodeutils.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/normal.py b/scripts/addons/cc_blender_tools-2_4_2/normal.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/normal.py rename to scripts/addons/cc_blender_tools-2_4_2/normal.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/panels.py b/scripts/addons/cc_blender_tools-2_4_2/panels.py similarity index 97% rename from scripts/addons/cc_blender_tools-2_4_0/panels.py rename to scripts/addons/cc_blender_tools-2_4_2/panels.py index b824b1c..7531b09 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/panels.py +++ b/scripts/addons/cc_blender_tools-2_4_2/panels.py @@ -18,7 +18,7 @@ import bpy import textwrap import os -from . import addon_updater_ops, iconutils, rigging, rigutils +from . import addon_updater_ops, iconutils, rigging, rigutils, rlx from . import (link, rigify_mapping_data, bones, characters, sculpting, springbones, bake, rigidbody, physics, colorspace, modifiers, channel_mixer, nodeutils, utils, params, vars) @@ -91,7 +91,7 @@ def character_info_box(chr_cache, chr_rig, layout, show_name = True, show_type = is_non_standard = True is_morph = False if chr_cache: - character_name = chr_cache.character_name + character_name = chr_cache.get_name() is_non_standard = chr_cache.is_non_standard() if chr_cache.is_standard(): type_text = f"Standard ({chr_cache.generation})" @@ -527,7 +527,7 @@ def character_tools_ui(context, layout: bpy.types.UILayout): rig = generic_rig if chr_cache: - chr_name = chr_cache.character_name + chr_name = chr_cache.get_name() elif generic_rig: chr_name = generic_rig.name elif non_chr_objects: @@ -829,7 +829,7 @@ class ACTION_UL_List(bpy.types.UIList): if arm_object.type == "ARMATURE": arm_name = arm_object.name arm_set_generation = rigutils.get_set_generation(arm_object) - rl_arm_id = utils.get_rl_object_id(arm_object) + rl_arm_id = utils.get_rl_id(arm_object) items = getattr(data, propname) filtered = [self.bitflag_filter_item] * len(items) item : bpy.types.Action @@ -837,13 +837,13 @@ class ACTION_UL_List(bpy.types.UIList): allowed = False action_set_generation = rigutils.get_set_generation(item) action_type = utils.get_prop(item, "rl_action_type") - action_armature_id = utils.get_prop(item, "rl_armature_id") + action_rl_id = utils.get_rl_id(item) channel = utils.get_action_channelbag(item, slot_type="OBJECT") if props.armature_action_filter and arm_object: - if arm_set_generation and action_set_generation and action_type and rl_arm_id and action_armature_id: + if arm_set_generation and action_set_generation and action_type and rl_arm_id and action_rl_id: if (arm_set_generation == action_set_generation and (action_type == "ARM" or action_type == "SLOTTED") and - action_armature_id == rl_arm_id): + action_rl_id == rl_arm_id): allowed = True elif channel and len(channel.fcurves) > 0: if channel.fcurves[0].data_path.startswith("key_blocks"): @@ -877,23 +877,30 @@ class ACTION_SET_UL_List(bpy.types.UIList): items = getattr(data, propname) filtered = [self.bitflag_filter_item] * len(items) item : bpy.types.Action + rlx_cache = props.get_context_staging_cache(context) chr_cache = props.get_context_character_cache(context) - arm_set_generation = None - arm_rig_id = None - if chr_cache: + set_generation = None + rig_id = None + arm = None + rob = None + if rlx_cache: + rob = rlx_cache.get_rlx_object() + set_generation = rlx.get_rlx_generation(rob) + rig_id = utils.get_rl_id(rob) + elif chr_cache: arm = chr_cache.get_armature() - arm_set_generation = rigutils.get_set_generation(arm) - arm_rig_id = utils.get_prop(arm, "rl_armature_id") + set_generation = rigutils.get_set_generation(arm) + rig_id = utils.get_rl_id(arm) for i, item in enumerate(items): allowed = False action_set_generation = rigutils.get_set_generation(item) action_type = utils.get_prop(item, "rl_action_type") - action_rig_id = utils.get_prop(item, "rl_armature_id") - if action_type == "ARM" or action_type == "SLOTTED": + action_rig_id = utils.get_rl_id(item) + if action_type == "ARM" or action_type == "SLOTTED" or action_type == "RLX": if props.filter_motion_set_type == "RIG": - allowed = arm_rig_id and action_rig_id and arm_rig_id == action_rig_id + allowed = rig_id and action_rig_id and rig_id == action_rig_id elif props.filter_motion_set_type == "GEN": - allowed = arm_set_generation and action_set_generation and arm_set_generation == action_set_generation + allowed = set_generation and action_set_generation and set_generation == action_set_generation else: allowed = True # filter by name @@ -961,7 +968,7 @@ class CC3CharacterSettingsPanel(bpy.types.Panel): # import details if chr_cache: if fake_drop_down(box.row(), "Import Details", "stage1_details", props.stage1_details): - box.label(text="Name: " + chr_cache.character_name) + box.label(text="Name: " + chr_cache.get_name()) box.label(text="Type: " + chr_cache.get_import_type().upper()) split = box.split(factor=0.4) col_1 = split.column() @@ -979,7 +986,7 @@ class CC3CharacterSettingsPanel(bpy.types.Panel): #if o: box.prop(obj_cache, "object", text="") else: - box.label(text="Name: " + chr_cache.character_name) + box.label(text="Name: " + chr_cache.get_name()) else: box.label(text="No Character") disable_on_linked(box, chr_cache) @@ -994,6 +1001,7 @@ class CC3CharacterSettingsPanel(bpy.types.Panel): column.prop(PREFS, "import_deduplicate") column.prop(PREFS, "import_auto_convert") column.prop(PREFS, "import_reset_custom_normals") + column.prop(PREFS, "import_fix_5_1_normals") column.prop(PREFS, "clean_empty_mesh_data") column.prop(PREFS, "action_use_action_slots") column.prop(PREFS, "action_add_key_slots_per_obj") @@ -2185,7 +2193,8 @@ class CC3RigifyPanel(bpy.types.Panel): props = vars.props() prefs = vars.prefs() - chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context) + chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context, strict=True) + rlx_cache = props.get_context_staging_cache(context) missing_materials = characters.has_missing_materials(chr_cache) layout = self.layout @@ -2211,7 +2220,7 @@ class CC3RigifyPanel(bpy.types.Panel): col_1 = split.column() col_2 = split.column() col_1.label(text = "Character:") - col_2.label(text = chr_cache.character_name) + col_2.label(text = chr_cache.get_name()) col_1.label(text = "Generation:") col_2.label(text = chr_cache.generation) col_1.label(text = "Face Profile:") @@ -2313,21 +2322,19 @@ class CC3RigifyPanel(bpy.types.Panel): icon = "FAKE_USER_OFF" if not props.rigify_retarget_use_fake_user else "FAKE_USER_ON" row.prop(props, "rigify_retarget_use_fake_user", text="", icon=icon, toggle=True) - if chr_cache.can_expression_rig() or chr_cache.can_rigify_face(): - col = layout.column(align=True) - col.label(text="Expression Rig:") - col.row(align=True).prop(prefs, "rigify_expression_rig", expand=True) - col = layout.column() - if prefs.rigify_expression_rig == "META": - if allow_rigify: - col.row().prop(prefs, "rigify_face_control_color") - else: - wrapped_text_box(layout, "Invalid Facial Profile!", width, alert=True) - elif prefs.rigify_expression_rig == "RIGIFY": - if not chr_cache.can_rigify_face(): - wrapped_text_box(layout, "Note: Full face rig cannot be auto-detected for this character.", width) - else: - grid = layout.grid_flow(columns=1, row_major=True, align=True) + # Always show rigify expression rig type (so AccuRig without facial profile can switch to None) + col = layout.column(align=True) + col.label(text="Expression Rig:") + col.row(align=True).prop(prefs, "rigify_expression_rig", expand=True) + col = layout.column() + if prefs.rigify_expression_rig == "META": + if allow_rigify: + col.row().prop(prefs, "rigify_face_control_color") + else: + wrapped_text_box(layout, "Invalid Facial Profile!", width, alert=True, icon="ERROR", ) + elif prefs.rigify_expression_rig == "RIGIFY": + if not chr_cache.can_rigify_face(): + wrapped_text_box(layout, "Note: Full face rig cannot be auto-detected for this character.", width) col = layout.column(align=True) col.label(text="Bone Alignment:") @@ -2599,12 +2606,15 @@ class CC3RigifyPanel(bpy.types.Panel): props.section_rigify_action_sets, icon="ANIM_DATA", icon_closed="ANIM_DATA"): - layout.label(text="Import Options:") + row = layout.row() + row.label(text="Import Options:") + row.operator("ccic.motion_prefs", icon="MODIFIER_ON", text="") row = layout.row(align=True) - label = rigutils.CCICActionImportOptions.label(context) - row.operator("ccic.action_import_options", icon="COLLAPSEMENU", text=label) + label = rigutils.CCICMotionBlend.label(context) + row.operator("ccic.motion_blend", icon="COLLAPSEMENU", text=label) + row.operator("ccic.motion_blend_funcs", icon="LOOP_BACK", text="").param = "RESET_OPTS" column = layout.column() - motion_set_ui(column, chr_cache) + motion_set_ui(column, chr_cache, rlx_cache) box_row = layout.box().row() if fake_drop_down(box_row, @@ -2690,17 +2700,19 @@ class CC3RigifyPanel(bpy.types.Panel): wrapped_text_box(layout, "Rigify add-on is not enabled.", width, True) -def motion_set_ui(layout: bpy.types.UILayout, chr_cache, show_nla=False): +def motion_set_ui(layout: bpy.types.UILayout, chr_cache, rlx_cache, show_nla=False): props = vars.props() # current selected motion set action action_set_list_action = utils.collection_at_index(props.action_set_list_index, bpy.data.actions) action_set_generation = rigutils.get_set_generation(action_set_list_action) rig = None - rig_set_generation = None - if chr_cache: + set_generation = None + if rlx_cache: + set_generation = rlx_cache.get_generation() + elif chr_cache: rig = chr_cache.get_armature() - rig_set_generation = rigutils.get_set_generation(rig) + set_generation = rigutils.get_set_generation(rig) col = layout.column(align=True) split = col.split(factor=0.4) @@ -2721,26 +2733,26 @@ def motion_set_ui(layout: bpy.types.UILayout, chr_cache, show_nla=False): param = "SET_FAKE_USER_OFF" if depress else "SET_FAKE_USER_ON" row.operator("ccic.rigutils", icon=icon, text="", depress=depress).param = param - split = col.split(factor=0.5, align=True) - split.scale_y = 1.5 + split.scale_y = 1.25 col_1 = split.column(align=True) col_2 = split.column(align=True) row = col_1.row(align=True) row.operator("ccic.rigutils", icon="ACTION_TWEAK", text="Load").param = "LOAD_MOTION_SET" - if rig_set_generation != action_set_generation: + if set_generation != action_set_generation: row.enabled = False col_2.operator("ccic.rigutils", icon="REMOVE", text="Clear").param = "CLEAR_ACTION_SET" - col_1.separator() - col_2.separator() col_1.operator("ccic.rigutils", icon="PLUS", text="New").param = "NEW_ACTION_SET" col_2.operator("ccic.rigutils", icon="MODIFIER_ON", text="Clean").param = "CLEAN_ACTIONS" - + row = col.row(align=True) + row.scale_y = 2.0 + row.operator("ccic.motion_blend", icon="MOD_ENVELOPE", text="Blend Motions").param="BLEND" + col.enabled = chr_cache is not None or rlx_cache is not None if show_nla: row = col_1.row(align=True) row.operator("ccic.rigutils", icon="NLA_PUSHDOWN", text="Push").param = "PUSH_ACTION_SET" - if rig_set_generation != action_set_generation: + if set_generation != action_set_generation: row.enabled = False col_2.operator("ccic.rigutils", icon="RESTRICT_SELECT_OFF", text="Select").param = "SELECT_SET_STRIPS" @@ -2772,7 +2784,7 @@ def motion_set_ui(layout: bpy.types.UILayout, chr_cache, show_nla=False): if not bpy.context.selected_nla_strips: split.enabled = False - if not chr_cache: + if not chr_cache and not rlx_cache: split.enabled = False @@ -2787,20 +2799,24 @@ class CCICAnimationToolsPanel(bpy.types.Panel): props = vars.props() prefs = vars.prefs() - chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context) + chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context, strict=True) + rlx_cache = props.get_context_staging_cache(context) layout = self.layout layout.use_property_split = False layout.use_property_decorate = False - layout.label(text="Import Options:") + row = layout.row() + row.label(text="Import Options:") + row.operator("ccic.motion_prefs", icon="MODIFIER_ON", text="") row = layout.row(align=True) - label = rigutils.CCICActionImportOptions.label(context) - row.operator("ccic.action_import_options", icon="COLLAPSEMENU", text=label) + label = rigutils.CCICMotionBlend.label(context) + row.operator("ccic.motion_blend", icon="COLLAPSEMENU", text=label).param="NONE" + row.operator("ccic.motion_blend_funcs", icon="LOOP_BACK", text="").param = "RESET_OPTS" column = layout.column() - motion_set_ui(column, chr_cache) + motion_set_ui(column, chr_cache, rlx_cache) class CCICNLASetsPanel(bpy.types.Panel): @@ -2814,13 +2830,14 @@ class CCICNLASetsPanel(bpy.types.Panel): props = vars.props() prefs = vars.prefs() - chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context) + chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context, strict=True) + rlx_cache = props.get_context_staging_cache(context) layout = self.layout layout.use_property_split = False layout.use_property_decorate = False - motion_set_ui(layout, chr_cache, show_nla=True) + motion_set_ui(layout, chr_cache, rlx_cache, show_nla=True) class CCICNLABakePanel(bpy.types.Panel): @@ -3816,6 +3833,12 @@ class CCICDataLinkPanel(bpy.types.Panel): chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context, strict=True) selected_meshes = [ obj for obj in bpy.context.selected_objects if obj.type == "MESH"] + mesh_modify_id = None + for obj in bpy.context.selected_objects: + if utils.get_prop(obj, "rl_mesh_modify"): + mesh_modify_id = utils.get_prop(obj, "rl_link_id") + break + #active_chr_cache = props.get_character_cache(obj, mat) all_valid_topography = True if chr_cache and selected_meshes: @@ -3966,6 +3989,11 @@ class CCICDataLinkPanel(bpy.types.Panel): text = "Go CC" param = "SEND_ACTOR" icon = "COMMUNITY" + if mesh_modify_id: + param = "SEND_MESH_MODIFY" + icon = "MESH_ICOSPHERE" + text = "Update Mesh" + grid.enabled = True if chr_cache and chr_cache.is_morph(): param = "SEND_MORPH" icon="MESH_ICOSPHERE" @@ -3977,7 +4005,7 @@ class CCICDataLinkPanel(bpy.types.Panel): text = "Invalid Character!" param += "_INVALID" grid.operator("ccic.datalink", icon=icon, text=text).param = param - if not (chr_cache and chr_cache.can_go_cc()): + if not mesh_modify_id and not (chr_cache and chr_cache.can_go_cc()): grid.enabled = False elif is_iclone: @@ -4091,7 +4119,7 @@ class CC3ToolsPipelineImportPanel(bpy.types.Panel): chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context) if chr_cache: - character_name = chr_cache.character_name + character_name = chr_cache.get_name() else: character_name = "No Character" @@ -4128,6 +4156,7 @@ class CC3ToolsPipelineImportPanel(bpy.types.Panel): column.prop(PREFS, "import_deduplicate") column.prop(PREFS, "import_auto_convert") column.prop(PREFS, "import_reset_custom_normals") + column.prop(PREFS, "import_fix_5_1_normals") column.prop(PREFS, "clean_empty_mesh_data") column.prop(PREFS, "action_use_action_slots") column.prop(PREFS, "action_add_key_slots_per_obj") @@ -4204,7 +4233,7 @@ class CC3ToolsPipelineExportPanel(bpy.types.Panel): chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context) if chr_cache: - character_name = chr_cache.character_name + character_name = chr_cache.get_name() else: character_name = "No Character" diff --git a/scripts/addons/cc_blender_tools-2_4_0/params.py b/scripts/addons/cc_blender_tools-2_4_2/params.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/params.py rename to scripts/addons/cc_blender_tools-2_4_2/params.py index 9ba8d20..8af455d 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/params.py +++ b/scripts/addons/cc_blender_tools-2_4_2/params.py @@ -1449,7 +1449,7 @@ SHADER_MATRIX = [ ["eye_iris_brightness", 1, "", "Custom/Iris Color Brightness"], ["eye_pupil_scale", 1, "", "Custom/Pupil Scale"], ["eye_ior", 1.4, "", "Custom/_IoR"], - ["eye_iris_radius", 0.24, "", "Custom/Iris UV Radius"], + ["eye_iris_radius", 0.16, "", "Custom/Iris UV Radius"], ["eye_iris_color", (1.0, 1.0, 1.0, 1.0), "func_color_bytes", "Custom/Iris Color"], ["eye_iris_inner_color", (1.0, 1.0, 1.0, 1.0), "func_color_bytes", "Custom/Iris Inner Color"], ["eye_iris_inner_scale", 0, "", "Custom/Iris Inner Scale"], diff --git a/scripts/addons/cc_blender_tools-2_4_0/physics.py b/scripts/addons/cc_blender_tools-2_4_2/physics.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/physics.py rename to scripts/addons/cc_blender_tools-2_4_2/physics.py index 711948b..0122438 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/physics.py +++ b/scripts/addons/cc_blender_tools-2_4_2/physics.py @@ -1309,7 +1309,7 @@ def apply_all_physics(chr_cache): props = vars.props() if chr_cache: - utils.log_info(f"Adding all Physics modifiers to: {chr_cache.character_name}") + utils.log_info(f"Adding all Physics modifiers to: {chr_cache.get_name()}") utils.log_indent() arm = chr_cache.get_armature() objects = chr_cache.get_all_objects(include_armature=False, @@ -1393,7 +1393,7 @@ def arrange_physics_modifiers(obj): def remove_all_physics(chr_cache): if chr_cache: - utils.log_info(f"Removing all Physics modifiers from: {chr_cache.character_name}") + utils.log_info(f"Removing all Physics modifiers from: {chr_cache.get_name()}") utils.log_indent() objects_processed = [] for obj in chr_cache.get_cache_objects(): @@ -1600,27 +1600,27 @@ def set_physics_settings(op, context, param): if chr_cache: restore_collision_proxy_view(context, chr_cache) disable_physics(chr_cache) - op.report({'INFO'}, f"Physics disabled for {chr_cache.character_name}") + op.report({'INFO'}, f"Physics disabled for {chr_cache.get_name()}") elif param == "ENABLE_PHYSICS": if chr_cache: restore_collision_proxy_view(context, chr_cache) enable_physics(chr_cache) reset_physics(context) - op.report({'INFO'}, f"Physics enabled for {chr_cache.character_name}") + op.report({'INFO'}, f"Physics enabled for {chr_cache.get_name()}") elif param == "REMOVE_PHYSICS": if chr_cache: restore_collision_proxy_view(context, chr_cache) remove_all_physics(chr_cache) - op.report({'INFO'}, f"Physics removed for {chr_cache.character_name}") + op.report({'INFO'}, f"Physics removed for {chr_cache.get_name()}") elif param == "APPLY_PHYSICS": if chr_cache: restore_collision_proxy_view(context, chr_cache) apply_all_physics(chr_cache) reset_physics(context) - op.report({'INFO'}, f"Physics applied to {chr_cache.character_name}") + op.report({'INFO'}, f"Physics applied to {chr_cache.get_name()}") elif param == "PHYSICS_INC_STRENGTH": strength = float(round(props.physics_paint_strength * 10)) / 10.0 diff --git a/scripts/addons/cc_blender_tools-2_4_0/preferences.py b/scripts/addons/cc_blender_tools-2_4_2/preferences.py similarity index 98% rename from scripts/addons/cc_blender_tools-2_4_0/preferences.py rename to scripts/addons/cc_blender_tools-2_4_2/preferences.py index 82bb1f3..7afcbdc 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/preferences.py +++ b/scripts/addons/cc_blender_tools-2_4_2/preferences.py @@ -100,7 +100,7 @@ def reset_rigify(): def reset_datalink(): prefs: CC3ToolsAddonPreferences = vars.prefs() prefs.datalink_auto_start = False - prefs.datalink_frame_sync = True + prefs.datalink_frame_sync = False prefs.datalink_preview_shape_keys = True prefs.datalink_match_client_rate = True prefs.datalink_retarget_prop_actions = True @@ -140,6 +140,7 @@ def reset_preferences(): prefs.auto_convert_materials = True prefs.import_deduplicate = True prefs.import_reset_custom_normals = False + prefs.import_fix_5_1_normals = True prefs.build_pack_texture_channels = False prefs.build_pack_wrinkle_diffuse_roughness = False prefs.build_reuse_baked_channel_packs = True @@ -274,8 +275,8 @@ class CC3ToolsAddonPreferences(bpy.types.AddonPreferences): description="When importing and retargeting animations, use action slots to combine armature and shape key actions into a single action", name="Use Action Slots") action_add_key_slots_per_obj: bpy.props.BoolProperty(default=True, - description="When using action slots, generate an slot channel specifically for each object's shape keys. " \ - "Otherwise, use only a single shapekey channel for all mesh obejcts", + description="When using action slots, generate a shape key slot for each object. " \ + "Otherwise, use only a single shape key slot for all mesh objects", name="Action Key Slots Per Obj") action_clean_actions: bpy.props.BoolProperty(default=False, description="When loading motions onto character, clean up (remove redundant) action keyframes", @@ -376,6 +377,8 @@ class CC3ToolsAddonPreferences(bpy.types.AddonPreferences): import_reset_custom_normals: bpy.props.BoolProperty(default=False, name="Reset Custom Normals", description="Reset the custom normals on all imported meshes (can help resolve lighting artifacts)") + import_fix_5_1_normals: bpy.props.BoolProperty(default=True, name="Fix Blender 5.1 Normals", + description="Blender 5.1 introduced an export bug that fails to export custom normals on meshes with shape-keys, this option will remove custom normals from all meshes with shape keys on import") import_deduplicate: bpy.props.BoolProperty(default=True, name="De-duplicate Materials", description="Detects and re-uses duplicate textures and consolidates materials with same name, textures and parameters into a single material") import_auto_convert: bpy.props.BoolProperty(default=True, name="Auto Convert Generic", @@ -629,6 +632,7 @@ class CC3ToolsAddonPreferences(bpy.types.AddonPreferences): layout.label(text="Import:") grid = layout.grid_flow(row_major=True, columns=2) grid.prop(self, "import_reset_custom_normals") + grid.prop(self, "import_fix_5_1_normals") grid.prop(self, "import_deduplicate") grid.prop(self, "import_auto_convert") grid.prop(self, "auto_convert_materials") diff --git a/scripts/addons/cc_blender_tools-2_4_0/presets/kiara_1_dawn_4k.hdr b/scripts/addons/cc_blender_tools-2_4_2/presets/kiara_1_dawn_4k.hdr similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/presets/kiara_1_dawn_4k.hdr rename to scripts/addons/cc_blender_tools-2_4_2/presets/kiara_1_dawn_4k.hdr diff --git a/scripts/addons/cc_blender_tools-2_4_0/presets/veranda_4k.hdr b/scripts/addons/cc_blender_tools-2_4_2/presets/veranda_4k.hdr similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/presets/veranda_4k.hdr rename to scripts/addons/cc_blender_tools-2_4_2/presets/veranda_4k.hdr diff --git a/scripts/addons/cc_blender_tools-2_4_0/properties.py b/scripts/addons/cc_blender_tools-2_4_2/properties.py similarity index 91% rename from scripts/addons/cc_blender_tools-2_4_0/properties.py rename to scripts/addons/cc_blender_tools-2_4_2/properties.py index f178ebc..5b34167 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/properties.py +++ b/scripts/addons/cc_blender_tools-2_4_2/properties.py @@ -20,6 +20,7 @@ from mathutils import Vector from . import (channel_mixer, imageutils, meshutils, sculpting, materials, rigidbody, facerig, facerig_data, springbones, rigify_mapping_data, modifiers, nodeutils, shaders, params, physics, basic, jsonutils, utils, vars) +from . rlx import get_rlx_generation from .meshutils import get_head_body_object_quick @@ -599,6 +600,38 @@ def clean_collection_property(collection_prop): break +def blend_mask_preset_list(self, context): + preset_list = [("NONE", "Blend Mask Presets ...", "Blend Presets ...", 0)] + folder = utils.get_resource_folder("Blend Presets") + json_files = [f for f in os.listdir(folder) if os.path.isfile(os.path.join(folder, f)) and f.endswith(".json")] + i = 1 + for file in json_files: + preset_name = file[:-5] + preset_list.append((preset_name, preset_name, preset_name, i)) + i += 1 + return preset_list + + +def update_blend_root_relative(self, context): + props = vars.props() + prefs = vars.prefs() + chr_cache = props.get_context_character_cache(context, strict=True) + if chr_cache: + opts = chr_cache.action_options + if opts.relative_root: + opts.current_root = False + + +def update_blend_root_current(self, context): + props = vars.props() + prefs = vars.prefs() + chr_cache = props.get_context_character_cache(context, strict=True) + if chr_cache: + opts = chr_cache.action_options + if opts.current_root: + opts.relative_root = False + + class CC3OperatorProperties(bpy.types.Operator): """CC3 Property Functions""" bl_idname = "cc3.setproperties" @@ -684,46 +717,70 @@ class CC3ArmatureList(bpy.types.PropertyGroup): actions: bpy.props.CollectionProperty(type=CC3ActionList) +class CCICStringList(bpy.types.PropertyGroup): + name: bpy.props.StringProperty(default="") + index: bpy.props.IntProperty(default=0) + visible: bpy.props.BoolProperty(default=True) + + class CCIC_UI_MixItem(bpy.types.PropertyGroup): name: bpy.props.StringProperty(default="") - weight: bpy.props.FloatProperty(default=1.0) - - -class CCIC_UI_MixList(bpy.types.PropertyGroup): - bones: bpy.props.CollectionProperty(type=CCIC_UI_MixItem) + weight: bpy.props.FloatProperty(default=1.0, min=0.0, max=1.0) class CCICActionOptions(bpy.types.PropertyGroup): - action_mode: bpy.props.EnumProperty(items=[ - ("NEW","New","Import actions as a new set of actions and keep the existing actions"), - ("REPLACE","Replace","Import new actions to replace the existing actions"), - ("BLEND","Overwrite","Import the new actions into the existing actions keeping the keyframes not overwritten by the import"), - ], default="NEW", name = "Import Action Mode") frame_mode: bpy.props.EnumProperty(items=[ - ("START","Start","Import keyframes into Blender starting at the start frame"), - ("CURRENT","Current","Import keyframes into Blender starting at the current frame"), - ("MATCH","Match","Import keyframes into Blender matching keyframes with CC/iClone \n*Note: +1 as Blender starts at frame 1*"), + ("MATCH","Match","Import / Blend keyframes matching keyframes with source motion\n*Note: +1 as Blender starts at frame 1*"), + ("CURRENT","Current","Import / Blend keyframes starting at the current frame"), + ("START","Start","Import / Blend keyframes starting at the start frame (frame 1)"), ], default="MATCH", name = "Import Frame Mode") - use_masking: bpy.props.BoolProperty(default=False, name="Use Bone / Shape-Key Masking", - description="Only import the keyframes from the masked bones and shape-keys") - override_global: bpy.props.BoolProperty(default=False, name="Character Motion Override:", - description="Override the global action options for this character or prop") - import_mix_bones: bpy.props.CollectionProperty(type=CCIC_UI_MixItem) - rig_mix_bones_list_index: bpy.props.IntProperty(default=-1) - import_mix_bones_list_index: bpy.props.IntProperty(default=-1) + use_bone_masking: bpy.props.BoolProperty(default=False, name="Use Bone Masking", + description="Only import the keyframes from the masked bones") + use_key_masking: bpy.props.BoolProperty(default=False, name="Use Shape-Key Masking", + description="Only import the keyframes from the masked shape-keys") + import_bones: bpy.props.CollectionProperty(type=CCIC_UI_MixItem) + import_keys: bpy.props.CollectionProperty(type=CCIC_UI_MixItem) + available_bones: bpy.props.CollectionProperty(type=CCICStringList) + available_keys: bpy.props.CollectionProperty(type=CCICStringList) + import_bones_index: bpy.props.IntProperty(default=-1) + import_keys_index: bpy.props.IntProperty(default=-1) + available_bones_index: bpy.props.IntProperty(default=-1) + available_keys_index: bpy.props.IntProperty(default=-1) + relative_root: bpy.props.BoolProperty(default=False, name="Relative Root", + description="Source motion root bone transform will match the destination motion at the starting frame", + update=update_blend_root_relative) + current_root: bpy.props.BoolProperty(default=False, name="Current Root", + description="Source motion root bone transform will match the current root bone transform in the scene", + update=update_blend_root_current) + use_blend: bpy.props.BoolProperty(default=False, name="Blend Frames", + description="Blend Frames from the source motion into the destination motion") + blend_strength: bpy.props.FloatProperty(default=1.0, name="Blend Strength", + min=0.0, soft_min=0.0, soft_max=1.0, + description="Blend Strength") + blend_in_frames: bpy.props.IntProperty(default=0, name="Blend In Frames", + min=0, soft_max=1000, + description="The number of frames at the start of the source or imported motion to blend in from the target motion") + blend_out_frames: bpy.props.IntProperty(default=0, name="Blend Out Frames", + min=0, soft_max=1000, + description="The number of frames at the end of the source or imported motion to blend out to the target motion") action_store: bpy.props.CollectionProperty(type=CCICActionStore) + source_motion: bpy.props.PointerProperty(type=bpy.types.Action) + target_motion: bpy.props.PointerProperty(type=bpy.types.Action) + motion_prefix: bpy.props.StringProperty(default="Blend") + motion_id: bpy.props.StringProperty(default="New Motion") + show_motion_options: bpy.props.BoolProperty(default=True) + show_blend_options: bpy.props.BoolProperty(default=True) + show_bone_mask_options: bpy.props.BoolProperty(default=True) + show_key_mask_options: bpy.props.BoolProperty(default=True) + mode_save_mask_preset: bpy.props.BoolProperty(default=False) + preset_mask_name: bpy.props.StringProperty(default="New Blend Mask Preset") # some masking settings ... # some masking presets ... # export / import presets ... - def get_action_mode(self): - props = vars.props() - action_mode = self.action_mode if self.override_global else props.action_options.action_mode - return action_mode - def get_frame_mode(self): props = vars.props() - frame_mode = self.frame_mode if self.override_global else props.action_options.frame_mode + frame_mode = self.frame_mode return frame_mode def get_start_frame(self, ccic_start_frame, blender_current_frame): @@ -1601,6 +1658,12 @@ class CC3ObjectCache(bpy.types.PropertyGroup): def is_armature(self): return utils.object_exists_is_armature(self.object) + def is_light(self): + return utils.object_exists_is_light(self.object) + + def is_camera(self): + return utils.object_exists_is_camera(self.object) + def get_object(self, return_invalid=False): if utils.object_exists(self.object): return self.object @@ -1633,7 +1696,7 @@ class CC3ObjectCache(bpy.types.PropertyGroup): if self.object_id == "": self.object_id = utils.generate_random_id(20) if self.object: - utils.set_rl_object_id(self.object, self.object_id) + utils.set_rl_id(self.object, self.object_id) self.object["rl_object_type"] = self.object_type def validate(self, report=None, split_objects=None): @@ -1688,6 +1751,174 @@ class CCICExpressionData(bpy.types.PropertyGroup): offset_rotation: bpy.props.FloatVectorProperty() # endregion +# region Staging Cache +class CC3StagingCache(bpy.types.PropertyGroup): + object_cache: bpy.props.CollectionProperty(type=CC3ObjectCache) + link_id: bpy.props.StringProperty(default="") + staging_name: bpy.props.StringProperty(default="") + action_options: bpy.props.PointerProperty(type=CCICActionOptions) + disabled: bpy.props.BoolProperty(default=False) + + def set_link_id(self, link_id): + self.link_id = link_id + arm = self.get_armature() + utils.set_rl_link_id(arm, link_id) + + def get_generation(self): + rob = self.get_rlx_object() + return get_rlx_generation(rob) + + def get_link_id(self): + if not self.link_id: + self.link_id = utils.generate_random_id(20) + return self.link_id + + def get_name(self): + return self.staging_name + + def set_name(self, name): + self.staging_name = name + + def get_rlx_object(self, include_disabled=False): + for obj_cache in self.object_cache: + obj = obj_cache.get_object() + if obj and (include_disabled or not obj_cache.disabled): + return obj + return None + + def get_primary_object(self, include_disabled=False): + return self.get_rlx_object(include_disabled=include_disabled) + + def get_armature(self, include_disabled=False): + return None + + def is_avatar(self): + return False + + def is_prop(self): + return False + + def is_light(self): + for obj_cache in self.object_cache: + if obj_cache.is_light(): + return True + return False + + def is_camera(self): + for obj_cache in self.object_cache: + if obj_cache.is_camera(): + return True + return False + + def cache_type(self): + if self.is_light(): + return "LIGHT" + else: + return "CAMERA" + + def get_object_cache(self, obj, include_disabled=False, by_id=None, strict=False) -> CC3ObjectCache: + """Returns the object cache for this object. + """ + if utils.object_exists(obj): + # by object + if not strict and not by_id: + by_id = utils.get_rl_id(obj) + for obj_cache in self.object_cache: + if include_disabled or not obj_cache.disabled: + cache_object = obj_cache.get_object() + if cache_object and cache_object == obj: + return obj_cache + # by id + if by_id: + for obj_cache in self.object_cache: + if include_disabled or not obj_cache.disabled: + if obj_cache.object_id == by_id: + return obj_cache + return None + + def update_object(self, obj): + obj_cache: CC3ObjectCache = None + for obj_cache in self.object_cache: + if ((obj.type == "CAMERA" and obj_cache.is_camera()) or + (obj.type == "LIGHT" and obj_cache.is_light())): + obj_cache.object = obj + self.set_name(obj.name) + obj_cache.check_id() + return + obj_cache = self.add_object_cache(obj) + self.set_name(obj.name) + + def remove_object_cache(self, obj): + """Removes the object from the object cache. + + Note this will invalidate all current object cache references! + """ + if obj: + for obj_cache in self.object_cache: + cache_object = obj_cache.get_object() + if cache_object and cache_object == obj: + utils.remove_from_collection(self.object_cache, obj_cache) + return + + def add_object_cache(self, obj, copy_from=None, user=False): + """Returns the object cache for this object. + + Fetches or creates an object cache for the object. Always returns an object cache collection. + """ + + obj_cache: CC3ObjectCache = self.get_object_cache(obj) + if obj_cache is None: + utils.log_info(f"Creating Object Cache for: {obj.name}") + obj_cache = self.object_cache.add() + obj_cache.object_id = utils.generate_random_id(20) + if copy_from: + utils.log_info(f"Copying object cache from: {copy_from}") + utils.copy_property_group(copy_from, obj_cache) + if user: + obj_cache.user_added = True + obj_cache.object_id = utils.generate_random_id(20) + obj_cache.set_object(obj) + obj_cache.set_object_type("LIGHT" if obj.type == "LIGHT" else "CAMERA") + obj_cache.source_name = utils.strip_name(obj.name) + obj_cache.check_id() + return obj_cache + + def validate(self, report=None): + """Checks character object is still valid. + Returns True if any objects in the character are still valid""" + obj_cache: CC3ObjectCache = None + any_valid = False + for obj_cache in self.object_cache: + obj_valid = obj_cache.validate(report=report) + any_valid = any_valid or obj_valid + if not any_valid: + rep = f"Staging Cache: {self.get_name()} is no longer valid!" + utils.log_info(rep) + if report is not None: + report.append(rep) + self.invalidate() + return not self.disabled + + def invalidate(self): + utils.log_info(f"Invalidating Staging cache: {self.get_name()}") + self.disabled = True + obj_cache: CC3ObjectCache = None + for obj_cache in self.object_cache: + obj_cache.invalidate() + + def delete(self): + obj_cache: CC3ObjectCache = None + for obj_cache in self.object_cache: + obj_cache.delete() + + def clean_up(self): + obj_cache: CC3ObjectCache = None + for obj_cache in self.object_cache: + obj_cache.clean_up() + utils.log_detail(f"Cleaning up object cache.") + clean_collection_property(self.object_cache) +# endregion + # region CharacterCache class CC3CharacterCache(bpy.types.PropertyGroup): open_mouth: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=open_mouth_update) @@ -1863,6 +2094,17 @@ class CC3CharacterCache(bpy.types.PropertyGroup): arm = self.get_armature() utils.set_rl_link_id(arm, link_id) + def get_link_id(self): + if not self.link_id: + self.link_id = utils.generate_random_id(20) + return self.link_id + + def get_name(self): + return self.character_name + + def set_name(self, name): + self.character_name = name + def get_auto_index(self): self.auto_index += 1 return self.auto_index @@ -1919,6 +2161,10 @@ class CC3CharacterCache(bpy.types.PropertyGroup): import_type = import_type[1:] return self.get_import_type().lower() == import_type.lower() + def is_obj(self): + res = (self.import_flags & 2 > 0) + return res + def get_character_id(self): dir, file = os.path.split(self.import_file) name, ext = os.path.splitext(file) @@ -1985,6 +2231,12 @@ class CC3CharacterCache(bpy.types.PropertyGroup): return True return False + def is_light(self): + return False + + def is_camera(self): + return False + def cache_type(self): if self.is_avatar(): return "AVATAR" @@ -2057,7 +2309,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): can_expresion_rig = self.can_expression_rig() can_rigify_face = self.can_rigify_face() return ((prefs.rigify_expression_rig == "META" and can_expresion_rig) or - (prefs.rigify_expression_rig == "RIGIFY" and can_rigify_face) or + (prefs.rigify_expression_rig == "RIGIFY") or (prefs.rigify_expression_rig == "NONE")) def get_facial_profile(self, update=True): @@ -2254,7 +2506,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): obj_cache = self.get_object_cache(obj) arm = self.get_armature() for child in arm.children: - if obj_cache.object_id == utils.get_rl_object_id(child): + if obj_cache.object_id == utils.get_rl_id(child): if "rl_collision_proxy" in child and child["rl_collision_proxy"] == obj.name: return child proxy_name = obj.name + ".Collision_Proxy" @@ -2322,7 +2574,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): if utils.object_exists(obj): # by object if not strict and not by_id: - by_id = utils.get_rl_object_id(obj) + by_id = utils.get_rl_id(obj) for obj_cache in self.object_cache: if include_disabled or not obj_cache.disabled: cache_object = obj_cache.get_object() @@ -2351,7 +2603,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): def has_cache_objects(self, objects, include_disabled=False): """Returns True if *any* of the objects are actively the object cache. """ - object_ids = [ utils.get_rl_object_id(o) for o in objects ] + object_ids = [ utils.get_rl_id(o) for o in objects ] for obj_cache in self.object_cache: if include_disabled or not obj_cache.disabled: cache_object = obj_cache.get_object() @@ -2373,11 +2625,11 @@ class CC3CharacterCache(bpy.types.PropertyGroup): """Returns True if the object is in the object cache. """ if obj: - object_id = utils.get_rl_object_id(obj) + object_id = utils.get_rl_id(obj) for obj_cache in self.object_cache: if include_disabled or not obj_cache.disabled: cache_object = obj_cache.get_object() - cache_object_id = utils.get_rl_object_id(cache_object) + cache_object_id = utils.get_rl_id(cache_object) if cache_object == obj or cache_object_id == object_id: return True return False @@ -2388,12 +2640,12 @@ class CC3CharacterCache(bpy.types.PropertyGroup): obj = obj.get_object() if obj: split_objects.append(obj) - obj_id = utils.get_rl_object_id(obj) + obj_id = utils.get_rl_id(obj) arm = self.get_armature() if arm: for child in arm.children: if child not in split_objects: - child_id = utils.get_rl_object_id(child) + child_id = utils.get_rl_id(child) if child_id == obj_id: split_objects.append(obj) return split_objects @@ -2409,6 +2661,12 @@ class CC3CharacterCache(bpy.types.PropertyGroup): pass return None + def get_rlx_object(self, include_disabled=False): + return None + + def get_primary_object(self, include_disabled=False): + return self.get_armature(include_disabled=include_disabled) + def get_body(self): return self.get_object_of_type("BODY") @@ -2422,7 +2680,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): body_id = obj_cache.object_id chr_objects = self.get_cache_objects() for obj in chr_objects: - if utils.get_rl_object_id(obj) == body_id: + if utils.get_rl_id(obj) == body_id: objects.append(obj) return objects @@ -2453,12 +2711,12 @@ class CC3CharacterCache(bpy.types.PropertyGroup): for obj_cache in self.object_cache: if not obj_cache.disabled: cache_object = obj_cache.get_object() - if cache_object.type == "ARMATURE": + if cache_object and cache_object.type == "ARMATURE": self.rig_original_rig = cache_object obj_cache.set_object(new_arm) # update the object id obj_cache.object_id = utils.generate_random_id(20) - utils.set_rl_object_id(new_arm, obj_cache.object_id) + utils.set_rl_id(new_arm, obj_cache.object_id) utils.set_rl_link_id(new_arm, self.link_id) except: pass @@ -2688,7 +2946,10 @@ class CC3CharacterCache(bpy.types.PropertyGroup): if chr_json and "Expression" in chr_json: expression_json = chr_json["Expression"] return copy.deepcopy(expression_json) + else: + return self.get_default_expression_json() + def get_default_expression_json(self): facial_profile, viseme_profile = self.get_facial_profile() if facial_profile == "MH": # MH profile has complete expression json @@ -2718,7 +2979,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): self.link_id = utils.generate_random_id(20) utils.set_rl_link_id(rig, self.link_id) # if rigified, ensure the face rig type is on the rig - if self.rigified: + if rig and self.rigified: if not utils.get_prop(rig, "rl_face_rig"): bone_collection = rig.data.edit_bones if utils.get_mode() == "EDIT" else rig.pose.bones if "facerig" in bone_collection: @@ -2857,11 +3118,11 @@ class CC3CharacterCache(bpy.types.PropertyGroup): def find_object_from_proxy(self, proxy, include_disabled=False): if "rl_collision_proxy" in proxy: - proxy_object_id = utils.get_rl_object_id(proxy) + proxy_object_id = utils.get_rl_id(proxy) for obj in self.get_cache_objects(): obj_cache = self.get_object_cache(obj) if include_disabled or not obj_cache.disabled: - if utils.get_rl_object_id(obj) == proxy_object_id and obj.name == proxy["rl_collision_proxy"]: + if utils.get_rl_id(obj) == proxy_object_id and obj.name == proxy["rl_collision_proxy"]: return obj if proxy.name.endswith(".Collision_Proxy"): obj_name = proxy.name[:-16] @@ -2911,11 +3172,6 @@ class CC3CharacterCache(bpy.types.PropertyGroup): else: return True - def get_link_id(self): - if not self.link_id: - self.link_id = utils.generate_random_id(20) - return self.link_id - def validate(self, report=None): """Checks character objects and materials are still valid. Returns True if any objects in the character are still valid""" @@ -2930,7 +3186,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): for mat_cache in all_materials_cache: mat_valid = mat_cache.validate(report) if not any_valid: - rep = f"Character Cache: {self.character_name} is no longer valid!" + rep = f"Character Cache: {self.get_name()} is no longer valid!" utils.log_info(rep) if report is not None: report.append(rep) @@ -2938,7 +3194,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): return not self.disabled def invalidate(self): - utils.log_info(f"Invalidating Character cache: {self.character_name}") + utils.log_info(f"Invalidating Character cache: {self.get_name()}") self.disabled = True obj_cache: CC3ObjectCache mat_cache: CC3MaterialCache @@ -2961,7 +3217,7 @@ class CC3CharacterCache(bpy.types.PropertyGroup): for mat_cache in all_materials_cache: mat_cache.delete() if self.disabled: - utils.log_info(f"Deleting Character Meta Objects: {self.character_name}") + utils.log_info(f"Deleting Character Meta Objects: {self.get_name()}") utils.delete_object_tree(self.rig_meta_rig) utils.delete_object_tree(self.arkit_proxy) utils.delete_object_tree(self.rig_export_rig) @@ -3039,6 +3295,7 @@ class CC3ImportProps(bpy.types.PropertyGroup): import_file: bpy.props.StringProperty(default="", subtype="FILE_PATH") import_cache: bpy.props.CollectionProperty(type=CC3CharacterCache) + staging_cache: bpy.props.CollectionProperty(type=CC3StagingCache) dummy_slider: bpy.props.FloatProperty(default=0.5, min=0, max=1) @@ -3258,6 +3515,8 @@ class CC3ImportProps(bpy.types.PropertyGroup): rigified_action_list_index: bpy.props.IntProperty(default=-1) rigified_action_list_action: bpy.props.PointerProperty(type=bpy.types.Action) + blend_mask_presets: bpy.props.EnumProperty(items=blend_mask_preset_list, default=0, description="Presets for motion blend masking", name="Blend Mask Presets") + wrinkle_regions: bpy.props.EnumProperty(items=[ ("ALL", "All", "All Regions"), ("01", " 1 - Brow Raise", "Brow Raise"), @@ -3300,15 +3559,22 @@ class CC3ImportProps(bpy.types.PropertyGroup): action_options: bpy.props.PointerProperty(type=CCICActionOptions) - def add_character_cache(self, copy_from=None): + def add_character_cache(self, copy_from=None) -> 'CC3CharacterCache': chr_cache = self.import_cache.add() if copy_from: exclude_list = ["*_material_cache", "object_cache"] utils.copy_property_group(copy_from, chr_cache, exclude=exclude_list) return chr_cache - def get_character_cache_from_objects(self, objects, search_materials=False): - chr_cache : CC3CharacterCache + def add_staging_cache(self, copy_from=None) -> 'CC3StagingCache': + rlx_cache = self.staging_cache.add() + if copy_from: + exclude_list = ["object_cache"] + utils.copy_property_group(copy_from, rlx_cache, exclude=exclude_list) + return rlx_cache + + def get_character_cache_from_objects(self, objects, search_materials=False) -> 'CC3CharacterCache': + chr_cache : CC3CharacterCache = None if objects: for chr_cache in self.import_cache: @@ -3333,10 +3599,10 @@ class CC3ImportProps(bpy.types.PropertyGroup): return chr_cache return None - def get_character_cache(self, obj, mat, by_id=None): + def get_character_cache(self, obj, mat, by_id=None) -> 'CC3CharacterCache': if obj: if not by_id: - by_id = utils.get_rl_object_id(obj) + by_id = utils.get_rl_id(obj) for chr_cache in self.import_cache: if not chr_cache.disabled: obj_cache = chr_cache.get_object_cache(obj, by_id=by_id) @@ -3357,6 +3623,21 @@ class CC3ImportProps(bpy.types.PropertyGroup): characters.append(chr_cache) return characters + def get_staging_cache(self, obj, by_id=None, create=False) -> 'CC3StagingCache': + if obj: + if not by_id: + by_id = utils.get_rl_id(obj) + for rlx_cache in self.staging_cache: + if not rlx_cache.disabled: + obj_cache = rlx_cache.get_object_cache(obj, by_id=by_id) + if obj_cache and not obj_cache.disabled: + return rlx_cache + if create: + rlx_cache = self.add_staging_cache() + rlx_cache.update_object(obj) + return rlx_cache + return None + def get_avatars(self): avatars = [] chr_cache: CC3CharacterCache @@ -3375,7 +3656,7 @@ class CC3ImportProps(bpy.types.PropertyGroup): def find_character_by_name(self, name): if name: for chr_cache in self.import_cache: - if not chr_cache.disabled and chr_cache.character_name == name: + if not chr_cache.disabled and chr_cache.get_name() == name: return chr_cache return None @@ -3406,6 +3687,31 @@ class CC3ImportProps(bpy.types.PropertyGroup): return chr_cache + def ensure_context_object(self, context=None): + """Ensure lights and cameras have staging_cache data""" + context = vars.get_context(context) + obj = context.object + if utils.object_exists_is_camera(obj) or utils.object_exists_is_light(obj): + if not self.get_staging_cache(obj) and utils.get_rl_link_id(obj): + rlx_cache = self.add_staging_cache() + rlx_cache.update_object(obj) + + def get_context_staging_cache(self, context=None) -> CC3StagingCache: + context = vars.get_context(context) + obj = context.object + rlx_cache = self.get_staging_cache(obj) + return rlx_cache + + def get_context_actor_cache(self, context=None) -> CC3CharacterCache: + rlx_cache = self.get_context_staging_cache(context=context) + if rlx_cache: + return rlx_cache + chr_cache = self.get_context_character_cache(context=context) + if chr_cache: + return chr_cache + else: + return None + def get_object_cache(self, obj, include_disabled=False): if obj: for chr_cache in self.import_cache: @@ -3452,16 +3758,81 @@ class CC3ImportProps(bpy.types.PropertyGroup): restored = True return restored - def fetch_stored_rig_action(self, store_id): + def fetch_stored_motion_set_id(self, store_id): action_store: CCICActionStore for action_store in self.action_options.action_store: if action_store.store_id == store_id: if (utils.object_exists_is_armature(action_store.object) or utils.object_exists_is_light(action_store.object) or utils.object_exists_is_camera(action_store.object)): - return action_store.object_action + obj_action = action_store.object_action + data_action = action_store.data_action + if obj_action and "rl_set_id" in obj_action: + return obj_action["rl_set_id"] + elif data_action and "rl_set_id" in data_action: + return data_action["rl_set_id"] return None + def fetch_stored_rig_action(self, store_id): + action_store: CCICActionStore + first_action = None + for action_store in self.action_options.action_store: + if action_store.store_id == store_id: + action = action_store.object_action + if utils.action_exists(action): + if (utils.object_exists_is_armature(action_store.object) or + utils.object_exists_is_light(action_store.object) or + utils.object_exists_is_camera(action_store.object)): + return action + if not first_action: + first_action = action + return first_action + + def fetch_stored_rig_channel(self, store_id): + action_store: CCICActionStore + for action_store in self.action_options.action_store: + if action_store.store_id == store_id: + if (utils.object_exists_is_armature(action_store.object) or + utils.object_exists_is_light(action_store.object) or + utils.object_exists_is_camera(action_store.object)): + action = action_store.object_action + if utils.action_exists(action): + slot_id = action_store.object_slot_id + slot = utils.get_action_slot(action, slot_id=slot_id) + channelbag = utils.get_action_channelbag(action, slot) + return action, slot, channelbag + return None, None, None + + def fetch_stored_key_channels(self, store_id): + action_store: CCICActionStore + key_actions = {} + for action_store in self.action_options.action_store: + if action_store.store_id == store_id: + if (utils.object_exists_has_shape_keys(action_store.object)): + obj = action_store.object + action = action_store.object_action + if utils.action_exists(action): + slot_id = action_store.object_slot_id + slot = utils.get_action_slot(action, slot_id=slot_id) + channelbag = utils.get_action_channelbag(action, slot) + key_actions[obj.name] = (action, slot, channelbag) + return key_actions + + def fetch_stored_data_channel(self, store_id): + action_store: CCICActionStore + for action_store in self.action_options.action_store: + if action_store.store_id == store_id: + if (utils.object_exists_is_light(action_store.object) or + utils.object_exists_is_camera(action_store.object)): + action = action_store.data_action + if utils.action_exists(action): + slot_id = action_store.data_slot_id + slot = utils.get_action_slot(action, slot_id=slot_id) + channelbag = utils.get_action_channelbag(action, slot) + return action, slot, channelbag + return None, None, None + + def fetch_action_stores(self, store_id: str): """This returns a list of the action store Property Collection items directly""" action_stores = [] diff --git a/scripts/addons/cc_blender_tools-2_4_0/proportion.py b/scripts/addons/cc_blender_tools-2_4_2/proportion.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/proportion.py rename to scripts/addons/cc_blender_tools-2_4_2/proportion.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/rigging.py b/scripts/addons/cc_blender_tools-2_4_2/rigging.py similarity index 97% rename from scripts/addons/cc_blender_tools-2_4_0/rigging.py rename to scripts/addons/cc_blender_tools-2_4_2/rigging.py index fe14e15..c21b819 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/rigging.py +++ b/scripts/addons/cc_blender_tools-2_4_2/rigging.py @@ -1338,6 +1338,44 @@ def store_source_bone_data(chr_cache, cc3_rig, rigify_rig, rigify_data): store_expression_set(chr_cache, cc3_rig, rigify_rig, rigify_data) +def is_valid_expression_def(expression, expression_def): + if "Bones" in expression_def: + for bone_name, bone_def in expression_def["Bones"].items(): + if "Translate" in bone_def and "Rotation" in bone_def: + tra = utils.array_to_vector(bone_def["Translate"]) + rot = utils.array_to_quaternion(bone_def["Rotation"]) + if tra.length > 0.001 or rot.angle > 0.001: + return True + else: + utils.log_detail(f"{expression}/{bone_name} has missing translate/rotate data") + return False + utils.log_detail(f"{expression} has bad translate/rotate data") + return False + utils.log_detail(f"{expression} has missing bone data") + return False + + +def merge_missing_expression_set(expression_json, default_expression_json): + # merge missing values from default expression set + if default_expression_json: + for expression, default_expression_def in default_expression_json.items(): + if expression not in expression_json: + expression_json[expression] = copy.deepcopy(default_expression_def) + expression_def = expression_json[expression] + if (not is_valid_expression_def(expression, expression_def) and + "Bones" in default_expression_def): + expression_def["Bones"] = copy.deepcopy(default_expression_def["Bones"]) + if False: + for bone_name, default_bone_def in default_expression_def["Bones"].items(): + if bone_name not in expression_def["Bones"]: + expression_def["Bones"][bone_name] = copy.deepcopy(default_expression_def["Bones"][bone_name]) + else: + bone_def = expression_def["Bones"][bone_name] + if "Translate" in default_bone_def and "Translate" not in bone_def: + bone_def["Translate"] = default_bone_def["Translate"].copy() + if "Rotation" in default_bone_def and "Rotation" not in bone_def: + bone_def["Rotation"] = default_bone_def["Rotation"].copy() + def store_expression_set(chr_cache, cc3_rig, rigify_rig=None, rigify_data=None): """Store source bone data from the cc3 rig in the org and def bones of rigify rig. @@ -1364,6 +1402,9 @@ def store_expression_set(chr_cache, cc3_rig, rigify_rig=None, rigify_data=None): json_data = chr_cache.get_json_data() expression_json = chr_cache.get_expression_json(json_data) + default_expression_json = chr_cache.get_default_expression_json() + if default_expression_json: + merge_missing_expression_set(expression_json, default_expression_json) utils.clear_prop_collection(chr_cache.expression_set) if expression_json: @@ -1406,8 +1447,14 @@ def store_expression_set(chr_cache, cc3_rig, rigify_rig=None, rigify_data=None): expression_cache.offset_translation = OR_tra expression_cache.offset_rotation = OR_euler else: - tra = utils.array_to_vector(expression_def["Bones"][bone_name]["Translate"]) - rot = utils.array_to_quaternion(expression_def["Bones"][bone_name]["Rotation"]) + try: + tra = utils.array_to_vector(expression_def["Bones"][bone_name]["Translate"]) + except: + tra = Vector((0,0,0)) + try: + rot = utils.array_to_quaternion(expression_def["Bones"][bone_name]["Rotation"]) + except: + rot = Quaternion((1,0,0,0)) pba: bpy.types.PoseBone = cc3_rig.pose.bones[bone_name] tra_local = pba.bone.matrix.inverted() @ tra rot_euler = rot.to_euler("XYZ") @@ -2054,7 +2101,7 @@ def generate_retargeting_rig(chr_cache, source_rig, rigify_rig, retarget_data, t utils.unhide(rigify_rig) rigify_rig.data.pose_position = "POSE" - retarget_rig = bpy.data.objects.new(chr_cache.character_name + "_Retarget", bpy.data.armatures.new(chr_cache.character_name + "_Retarget")) + retarget_rig = bpy.data.objects.new(chr_cache.get_name() + "_Retarget", bpy.data.armatures.new(chr_cache.get_name() + "_Retarget")) bpy.context.collection.objects.link(retarget_rig) if retarget_rig: @@ -2715,7 +2762,7 @@ def adv_retarget_pair_rigs(op, chr_cache, source_rig=None, source_action=None, t # if op: op.report({'ERROR'}, "No Source Action!") # utils.log_error("No Source Action!") # return None - if source_action and not check_armature_action(source_rig, source_action, fix_rotation_mode=True): + if source_rig and source_action and not check_armature_action(source_rig, source_action, fix_rotation_mode=True): if op: op.report({'ERROR'}, "Source Action does not match Source Armature!") utils.log_error("Source Action does not match Source Armature!") return None @@ -2800,25 +2847,28 @@ def full_retarget_source_rig_action(op, chr_cache, src_rig=None, src_action=None if rigify_rig and src_rig and src_action: armature_action = adv_bake_retarget_to_rigify(op, chr_cache, src_rig, src_action) - utils.log_info(f"Armature action retargetted to: {armature_action.name}") - # assign names and set data - rig_id = rigutils.get_rig_id(rigify_rig) - rl_arm_id = utils.get_rl_object_id(rigify_rig) - motion_prefix = rigutils.get_motion_prefix(src_action) - custom_prefix = props.rigify_retarget_motion_prefix.strip() - if use_ui_options and custom_prefix: - motion_prefix = custom_prefix - motion_id = rigutils.get_motion_id(src_action, "Retarget") - motion_id = rigutils.get_unique_set_motion_id(rig_id, motion_id, motion_prefix, slotted=use_slotted) - set_id, set_generation = rigutils.generate_motion_set(rigify_rig, motion_id, motion_prefix) - rigutils.set_armature_action_name(armature_action, rig_id, motion_id, motion_prefix, slotted=use_slotted) - rigutils.add_motion_set_data(armature_action, set_id, set_generation, arm_id=rl_arm_id, slotted=use_slotted) - armature_action.use_fake_user = props.rigify_retarget_use_fake_user if use_ui_options else True - utils.log_info(f"Renaming armature action to: {armature_action.name}") - use_fake_user = props.rigify_retarget_use_fake_user if use_ui_options else True - rigutils.copy_action_shape_key_channels(rigify_rig, src_action, armature_action, fake_user=use_fake_user) - rigutils.load_motion_set(rigify_rig, armature_action) - rigutils.update_motion_set_index(armature_action) + if armature_action: + utils.log_info(f"Armature action retargetted to: {armature_action.name}") + # assign names and set data + rig_id = rigutils.get_rig_id(rigify_rig) + rl_id = utils.get_rl_id(rigify_rig) + motion_prefix = rigutils.get_motion_prefix(src_action) + custom_prefix = props.rigify_retarget_motion_prefix.strip() + if use_ui_options and custom_prefix: + motion_prefix = custom_prefix + motion_id = rigutils.get_motion_id(src_action, "Retarget") + motion_id = rigutils.get_unique_set_motion_id(rig_id, motion_id, motion_prefix, slotted=use_slotted, rlob=rigify_rig) + set_id, set_generation = rigutils.generate_motion_set(rigify_rig, motion_id, motion_prefix) + rigutils.set_armature_action_name(armature_action, rig_id, motion_id, motion_prefix, slotted=use_slotted) + rigutils.add_motion_set_data(armature_action, set_id, set_generation, arm_id=rl_id, slotted=use_slotted) + armature_action.use_fake_user = props.rigify_retarget_use_fake_user if use_ui_options else True + utils.log_info(f"Renaming armature action to: {armature_action.name}") + use_fake_user = props.rigify_retarget_use_fake_user if use_ui_options else True + rigutils.copy_action_shape_key_channels(rigify_rig, src_action, armature_action, fake_user=use_fake_user) + rigutils.load_motion_set(rigify_rig, armature_action) + rigutils.update_motion_set_index(armature_action) + else: + utils.log_error(f"Unable to bake retargetted action: {src_rig} / {src_action}") FK_BONE_GROUPS = ["FK", "Special", "Tweak", "Extra", "Root", "Face"] @@ -2850,6 +2900,9 @@ def adv_bake_retarget_to_rigify(op, chr_cache, source_rig, source_action): rigify_rig = chr_cache.get_armature() + # make sure preview range is off + bpy.context.scene.use_preview_range = False + # generate (or re-use) retargeting rig retarget_rig = adv_retarget_pair_rigs(op, chr_cache, source_rig, source_action) temp_action, temp_slot = utils.safe_get_action_slot(retarget_rig) @@ -3118,8 +3171,8 @@ def generate_export_rig(chr_cache, use_t_pose=False, t_pose_action=None, EXPORT_RIG = rigify_mapping_data.METARIG_EXPORT_RIG if export_rig: - utils.force_object_name(export_rig, chr_cache.character_name + "_Export") - utils.force_armature_name(export_rig.data, chr_cache.character_name + "_Export") + utils.force_object_name(export_rig, chr_cache.get_name() + "_Export") + utils.force_armature_name(export_rig.data, chr_cache.get_name() + "_Export") else: return None @@ -3646,8 +3699,8 @@ def bake_rig_animation(chr_cache, rig, source_rig, source_action, set_id = rigutils.generate_set_id() rig_id = rigutils.get_rig_id(rig) - arm_id = rigutils.get_armature_id(rig) - motion_id = rigutils.get_unique_set_motion_id(rig_id, motion_id, motion_prefix) + rl_id = rigutils.get_armature_id(rig) + motion_id = rigutils.get_unique_set_motion_id(rig_id, motion_id, motion_prefix, rlob=rig) is_slotted = prefs.use_action_slots() if utils.try_select_object(rig, True) and utils.set_active_object(rig): @@ -3700,14 +3753,14 @@ def bake_rig_animation(chr_cache, rig, source_rig, source_action, # armature action baked_action = utils.safe_get_action(rig) if baked_action: - rigutils.add_motion_set_data(baked_action, set_id, "Rigify+", arm_id=arm_id, slotted=is_slotted) + rigutils.add_motion_set_data(baked_action, set_id, "Rigify+", arm_id=rl_id, slotted=is_slotted) baked_action.name = armature_action_name baked_action.use_fake_user = True armature_action = baked_action utils.log_info(f"Baked armature action: {baked_action.name}") for obj_id, key_action in shape_key_actions.items(): rigutils.set_key_action_name(key_action, rig_id, motion_id, obj_id, motion_prefix) - rigutils.add_motion_set_data(baked_action, set_id, "Rigify+", obj_id=obj_id, arm_id=arm_id, slotted=is_slotted) + rigutils.add_motion_set_data(baked_action, set_id, "Rigify+", key_id=obj_id, arm_id=rl_id, slotted=is_slotted) utils.log_info(f"Baked key action: {key_action.name}") # restore view layers @@ -3844,7 +3897,7 @@ def check_armature_action(rig, action, fix_rotation_mode=True): elif data_path.endswith("rotation_axis_angle") and pose_bone.rotation_mode != "AXIS_ANGLE": pose_bone.rotation_mode = "AXIS_ANGLE" matching += 1 - if total == 0 or matching == 0: + if total == 0: return False return matching > 0 @@ -4145,7 +4198,7 @@ class CC3Rigifier(bpy.types.Operator): fix_rigify_bones(chr_cache, self.rigify_rig) add_def_bones(chr_cache, self.cc3_rig, self.rigify_rig) add_extension_bones(chr_cache, self.cc3_rig, self.rigify_rig, self.rigify_data.bone_mapping, acc_vertex_group_map) - store_source_bone_data(self.cc3_rig, self.rigify_rig, self.rigify_data) + store_source_bone_data(chr_cache, self.cc3_rig, self.rigify_rig, self.rigify_data) rigify_spring_rigs(chr_cache, self.cc3_rig, self.rigify_rig, self.rigify_data.bone_mapping) if self.use_expression_rig(chr_cache): facerig.build_facerig_drivers(chr_cache, self.rigify_rig) @@ -4588,6 +4641,7 @@ class CC3RigifierModal(bpy.types.Operator): def voxel_heat_skinning_start(self, context): # fix cc3 rig (bone lengths & deform settings) arm = self.chr_cache.get_armature() + utils.unhide(arm) rigutils.fix_cc3_standard_rig(arm) # unparent object(s) keep transform diff --git a/scripts/addons/cc_blender_tools-2_4_0/rigidbody.py b/scripts/addons/cc_blender_tools-2_4_2/rigidbody.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/rigidbody.py rename to scripts/addons/cc_blender_tools-2_4_2/rigidbody.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/rigify_mapping_data.py b/scripts/addons/cc_blender_tools-2_4_2/rigify_mapping_data.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/rigify_mapping_data.py rename to scripts/addons/cc_blender_tools-2_4_2/rigify_mapping_data.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/rigutils.py b/scripts/addons/cc_blender_tools-2_4_2/rigutils.py similarity index 62% rename from scripts/addons/cc_blender_tools-2_4_0/rigutils.py rename to scripts/addons/cc_blender_tools-2_4_2/rigutils.py index 8a84eb5..d61e9fc 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/rigutils.py +++ b/scripts/addons/cc_blender_tools-2_4_2/rigutils.py @@ -17,8 +17,8 @@ import bpy from mathutils import Vector, Matrix, Quaternion, Euler from random import random -import re, time, os -from . import springbones, bones, facerig, modifiers, rigify_mapping_data, lib, utils, vars +import re, time, os, json +from . import springbones, bones, facerig, modifiers, rigify_mapping_data, lib, ui, utils, vars from typing import List @@ -223,6 +223,7 @@ def get_armature_action_source_type(armature, action=None): def find_source_actions(source_action, source_rig=None): src_set_id, src_set_gen, src_type_id, src_object_id = get_motion_set_ids(source_action) + src_rlx_id = utils.get_prop(source_action, "rl_rlx_id") src_prefix = get_motion_prefix(source_action) src_prefix, src_rig_id, src_type_id, src_object_id, src_motion_id = decode_action_name(source_action) if not src_motion_id: @@ -240,6 +241,9 @@ def find_source_actions(source_action, source_rig=None): "armature": None, "armatures": [], "keys": {}, + "rlx": None, + "light": None, + "camera": None, "objects": {}, } @@ -248,11 +252,21 @@ def find_source_actions(source_action, source_rig=None): for action in bpy.data.actions: if "rl_set_id" in action: set_id, set_gen, type_id, object_id = get_motion_set_ids(action) + rlx_id = utils.get_prop(action, "rl_rlx_id") unused = utils.get_prop(action, "rl_unused_action", False) if set_id == src_set_id: if type_id == "KEY": utils.log_info(f" - Found shape-key action: {action.name} for {object_id}") actions["keys"][object_id] = action + elif type_id == "RLX" or (type_id == "SLOTTED" and rlx_id): + utils.log_info(f" - Found rlx action: {action.name}") + actions["rlx"] = action + elif type_id == "LIGHT" and rlx_id: + utils.log_info(f" - Found rlx light action: {action.name}") + actions["light"] = action + elif type_id == "CAMERA" and rlx_id: + utils.log_info(f" - Found rlx camera action: {action.name}") + actions["camera"] = action elif type_id == "ARM" or type_id == "SLOTTED": utils.log_info(f" - Found armature action: {action.name}") actions["armatures"].append(action) @@ -263,21 +277,31 @@ def find_source_actions(source_action, source_rig=None): # try matching actions by action name pattern if src_type_id and src_motion_id: - # match actions by name pattern - utils.log_info(f"Looking for shape-key actions matching: [<{src_prefix}>|]{src_rig_id}|[K|A]|[|]{src_motion_id}") - for action in bpy.data.actions: - motion_prefix, rig_id, type_id, object_id, motion_id = decode_action_name(action) - if (motion_id and object_id not in actions and - motion_prefix == src_prefix and - rig_id == src_rig_id and - utils.partial_match(motion_id, src_motion_id)): - if type_id == "K": - utils.log_info(f" - Found shape-key action: {action.name} for {object_id}") - actions["keys"][object_id] = action - elif type_id == "A": - utils.log_info(f" - Found armature action: {action.name}") - actions["armature"] = action - return actions + if is_slotted_action(source_action): + actions["armature"] = source_action + actions["armatures"].append(source_action) + actions["slotted"] = True + return actions + else: + # match actions by name pattern + found = False + utils.log_info(f"Looking for shape-key actions matching: [<{src_prefix}>|]{src_rig_id}|[K|A]|[|]{src_motion_id}") + for action in bpy.data.actions: + motion_prefix, rig_id, type_id, object_id, motion_id = decode_action_name(action) + if (motion_id and object_id not in actions and + motion_prefix == src_prefix and + rig_id == src_rig_id and + utils.partial_match(motion_id, src_motion_id)): + if type_id == "K": + utils.log_info(f" - Found shape-key action: {action.name} for {object_id}") + actions["keys"][object_id] = action + found = True + elif type_id == "A": + utils.log_info(f" - Found armature action: {action.name}") + actions["armature"] = action + found = True + if found: + return actions # try to fetch shape-key actions from source armature child objects, if supplied elif source_rig: @@ -452,12 +476,18 @@ def obj_has_action_shape_keys(obj, action: bpy.types.Action): def get_rig_id(rig): - rig_id = utils.strip_name(rig.name.strip()).replace("|", "_") - return rig_id + if rig: + rig_id = utils.strip_name(rig.name.strip()).replace("|", "_") + return rig_id + else: + return "" def get_armature_id(rig): - return utils.get_prop(rig, "rl_armature_id", None) + if rig: + return utils.get_prop(rig, "rl_armature_id", None) + else: + return "" def get_motion_id(action): @@ -490,7 +520,8 @@ def decode_action_name(action): ids[i] = id.strip() L = len(ids) K = L - if "A" in ids or "UA" in ids or "S" in ids: + TYPE_IDS = ["A", "UA", "S", "O", "UO", "L", "UL", "R", "UR", "C", "UC"] + if utils.any_in(ids, TYPE_IDS): K -= 1 elif "K" in ids or "UK" in ids: K -= 2 @@ -563,8 +594,12 @@ def get_formatted_prefix(motion_prefix): return motion_prefix -def get_unique_set_motion_id(rig_id, motion_id, motion_prefix, exclude_set_id=None, slotted=False): - test_name = make_armature_action_name(rig_id, motion_id, motion_prefix, slotted=slotted) +def get_unique_set_motion_id(rig_id, motion_id, motion_prefix, exclude_set_id=None, + slotted=False, rlob=None, rlx=False): + if rlx or (rlob and rlob.type != "ARMATURE"): + test_name = make_rlx_action_name(rig_id, motion_id, motion_prefix, slotted=slotted) + else: + test_name = make_armature_action_name(rig_id, motion_id, motion_prefix, slotted=slotted) base_name = test_name num_suffix = 0 while test_name in bpy.data.actions: @@ -610,6 +645,18 @@ def make_key_action_name(rig_id, motion_id, obj_id, motion_prefix): return generate_action_name(rig_id, "K", obj_id, motion_id, motion_prefix) +def make_rlx_action_name(rlob_id, motion_id, motion_prefix, slotted=False): + if slotted: + return generate_action_name(rlob_id, "", "", motion_id, motion_prefix) + else: + return generate_action_name(rlob_id, "O", "", motion_id, motion_prefix) + + +def make_data_action_name(obj, rlob_id, motion_id, motion_prefix): + code_id = obj.type[0] if type(obj) is bpy.types.Object else code_id[0] + return generate_action_name(rlob_id, code_id, "", motion_id, motion_prefix) + + def set_armature_action_name(action, rig_id, motion_id, motion_prefix, slotted=False): action.name = make_armature_action_name(rig_id, motion_id, motion_prefix, slotted=slotted) @@ -619,7 +666,8 @@ def set_key_action_name(action, rig_id, motion_id, obj_id, motion_prefix): def get_set_generation(rig): - return utils.get_prop(rig, "rl_set_generation", None) + set_generation = utils.get_prop(rig, "rl_set_generation", None) + return set_generation def update_rig_set_generation(rig): @@ -661,20 +709,40 @@ def get_motion_set_actions(action_or_set_id): return actions +def is_rlx_motion(actions_or_set_id): + T = type(actions_or_set_id) + if T is list: + actions = actions_or_set_id + else: + actions = get_motion_set_actions(actions_or_set_id) + for action in actions: + if "rl_rlx_id" in action: + return True + return False + + def get_actions_frame_range(actions): - action: bpy.types.Action = None frame_start = None frame_end = None - for action in actions: - start = action.frame_range[0] - end = action.frame_range[1] - if frame_start is None: - frame_start = start - frame_end = end - if start < frame_start: - frame_start = start - if end > frame_end: - frame_end = end + if actions: + if not type(actions) is list: + actions = [actions] + action: bpy.types.Action = None + for action in actions: + if action: + start = int(action.frame_range[0]) + end = int(action.frame_range[1]) + if frame_start is None: + frame_start = start + if frame_end is None: + frame_end = end + if start < frame_start: + frame_start = start + if end > frame_end: + frame_end = end + if frame_start is None or frame_end is None: + frame_start = 0 + frame_end = 0 return frame_start, frame_end @@ -697,18 +765,33 @@ def replace_motion_set(set_id, old_set_id): delete_motion_set(temp_id) -def add_motion_set_data(action, set_id, set_generation, obj_id=None, arm_id=None, slotted=False): +def add_motion_set_data(action, set_id, set_generation, + key_id=None, arm_id=None, rlx_id=None, + is_light=False, is_camera=False, + slotted=False): action["rl_set_id"] = set_id action["rl_set_generation"] = set_generation + if slotted: action["rl_action_type"] = "SLOTTED" - elif obj_id is not None: + elif arm_id and key_id: action["rl_action_type"] = "KEY" - action["rl_key_object"] = obj_id - else: + elif arm_id: action["rl_action_type"] = "ARM" - if arm_id is not None: + elif rlx_id and is_light: + action["rl_action_type"] = "LIGHT" + elif rlx_id and is_camera: + action["rl_action_type"] = "CAMERA" + elif rlx_id: + action["rl_action_type"] = "RLX" + + if arm_id: action["rl_armature_id"] = arm_id + if key_id: + action["rl_key_object"] = key_id + if rlx_id: + action["rl_rlx_id"] = rlx_id + def update_motion_set_index(action): @@ -716,7 +799,7 @@ def update_motion_set_index(action): props.action_set_list_index = utils.index_in_collection(action, bpy.data.actions) -def load_motion_set(rig, action, move=False, temp=None): +def load_motion_set(rig, action, move=False, temp=None, force=None): prefs = vars.prefs() if action: utils.log_info(f"Load Motion Set: {action.name} {'Move ' if move else ''}{'Temp ' if temp else ''}") @@ -728,52 +811,72 @@ def load_motion_set(rig, action, move=False, temp=None): if action: utils.log_indent() if prefs.use_action_slots(): - arm_action = load_slotted_action(rig, action, - move=move, temp=temp) + arm_action = load_slotted_action(rig, action, move=move, temp=temp, force=force) else: - arm_action = load_separate_actions(rig, action, - move=move, temp=temp) + arm_action = load_separate_actions(rig, action, move=move, temp=temp, force=force) if arm_action: update_motion_set_index(arm_action) utils.log_recess() + return arm_action -def new_motion_set(rig): +def new_motion_set(rig: bpy.types.Object, motion_id=None, motion_prefix=""): prefs = vars.prefs() + slotted = prefs.use_action_slots() rig_id = get_rig_id(rig) - rl_arm_id = utils.get_rl_object_id(rig) + rl_id = utils.get_rl_id(rig) set_id = generate_set_id() set_generation = utils.get_prop(rig, "rl_set_generation") - motion_id = "New" - motion_id = get_unique_set_motion_id(rig_id, motion_id, "", slotted=slotted) - action_name = make_armature_action_name(rig_id, motion_id, "", slotted=slotted) + if not motion_id: + motion_id = "New" + motion_id = get_unique_set_motion_id(rig_id, motion_id, motion_prefix, slotted=slotted, rlob=rig) + if rig.type == "ARMATURE": + action_name = make_armature_action_name(rig_id, motion_id, motion_prefix, slotted=slotted) + else: + action_name = make_rlx_action_name(rig_id, motion_id, motion_prefix, slotted=slotted) action = bpy.data.actions.new(action_name) slot, channel = add_action_ob_slot_channelbag(action, rig) - add_motion_set_data(action, set_id, set_generation, arm_id=rl_arm_id, slotted=slotted) + if rig.type == "ARMATURE": + add_motion_set_data(action, set_id, set_generation, arm_id=rl_id, slotted=slotted) + elif rig.type == "LIGHT" or rig.type == "CAMERA": + add_motion_set_data(action, set_id, set_generation, rlx_id=rl_id, slotted=slotted) utils.safe_set_action(rig, action, slot=slot) utils.safe_set_action(rig.data, None, create=False) - for child in rig.children: - if utils.object_exists_is_mesh(child) and utils.object_has_shape_keys(child): - utils.safe_set_action(child, None, create=False) - utils.safe_set_action(child.data, None, create=False) - utils.safe_set_action(child.data.shape_keys, None, create=False) + if rig.type == "ARMATURE": + for child in rig.children: + if utils.object_exists_is_mesh(child) and utils.object_has_shape_keys(child): + utils.safe_set_action(child, None, create=False) + utils.safe_set_action(child.data, None, create=False) + utils.safe_set_action(child.data.shape_keys, None, create=False) + elif rig.type == "LIGHT" or rig.type == "CAMERA": + utils.safe_set_action(rig.data, None, create=False) update_motion_set_index(action) return action +def ensure_motion_set(rlob, motion_id="New", motion_prefix=""): + action = utils.safe_get_action(rlob) + if not action: + action = new_motion_set(rlob, motion_id, motion_prefix) + if action: + load_motion_set(rlob, action) + + def clear_motion_set(rig): mode_selection = utils.store_mode_selection_state() has_actions = utils.safe_get_action(rig) - if not has_actions: + if rig.type == "ARMATURE" and not has_actions: reset_pose(rig) utils.safe_set_action(rig, None) + if rig.type == "LIGHT" or rig.type == "CAMERA": + utils.safe_set_action(rig.data, None, create=False) objects = utils.get_child_objects(rig) for obj in objects: if obj.type == "MESH": if utils.object_has_shape_keys(obj): - utils.safe_set_action(obj.data.shape_keys, None) + utils.safe_set_action(obj.data.shape_keys, None, create=False) if not has_actions: reset_shape_keys(obj) utils.restore_mode_selection_state(mode_selection) @@ -1224,25 +1327,83 @@ def delete_motion_set(action_or_set_id): def duplicate_motion_set(source_action): set_id = utils.get_prop(source_action, "rl_set_id") - set_generation = utils.get_prop(source_action, "rl_set_generation", None) - action_type_id = utils.get_prop(source_action, "rl_action_type", None) - arm_id = utils.get_prop(source_action, "rl_armature_id", None) + src_set_generation = utils.get_prop(source_action, "rl_set_generation", None) + src_action_type_id = utils.get_prop(source_action, "rl_action_type", None) new_set_id = generate_set_id() duplicates = [] + new_action = None if set_id: for action in bpy.data.actions: action_set_id = utils.get_prop(action, "rl_set_id") if action_set_id == set_id: - set_generation = utils.get_prop(action, "rl_set_generation", set_generation) - action_type_id = utils.get_prop(action, "rl_action_type", action_type_id) - obj_id = utils.get_prop(action, "rl_key_object", None) - arm_id = utils.get_prop(action, "rl_armature_id", arm_id) + utils.log_detail(f"Duplicating action: {action.name}") + set_generation = utils.get_prop(action, "rl_set_generation", src_set_generation) + action_type_id = utils.get_prop(action, "rl_action_type", src_action_type_id) + obj_id = utils.get_prop(action, "rl_key_object") + rlob_id = utils.get_rl_id(action) + rlx_id = utils.get_prop(action, "rl_rlx_id") + arm_id = utils.get_prop(action, "rl_armature_id") is_slotted = action_type_id == "SLOTTED" + is_light = action_type_id == "LIGHT" + is_camera = action_type_id == "CAMERA" + is_rlx = action_type_id == "RLX" or rlx_id is not None + rlx_id = rlob_id if is_rlx else None + arm_id = None if is_rlx else rlob_id duplicate_action = action.copy() - add_motion_set_data(duplicate_action, new_set_id, set_generation, obj_id, arm_id, slotted=is_slotted) + add_motion_set_data(duplicate_action, new_set_id, set_generation, + key_id=obj_id, arm_id=arm_id, rlx_id=rlx_id, + is_light=is_light, is_camera=is_camera, + slotted=is_slotted), duplicates.append(duplicate_action) - update_motion_set_index(source_action) + if action_type_id == "SLOTTED" or action_type_id == "ARM" or action_type_id == "RLX": + new_action = duplicate_action + if new_action: + update_motion_set_index(new_action) + utils.log_detail(f"Using duplicate main action: {new_action.name}") + else: + utils.log_error(f"Duplicate main action not found!") utils.update_ui(all=True) + #duplicate_set = [] + #if new_action: + # duplicate_set.append(new_action) + #for action in duplicates: + # if action not in duplicate_set: + # duplicate_set.append(action) + return new_action #, duplicate_set + + +def get_new_motion_set_action_name(action_type, set_id, prefix, rig_id, motion_id, rlob=None): + motion_id = get_unique_set_motion_id(rig_id, motion_id, prefix, + exclude_set_id=set_id, + slotted=(action_type == "SLOTTED"), rlob=rlob) + name = "" + if action_type == "SLOTTED": + name = make_armature_action_name(rig_id, motion_id, prefix, slotted=True) + else: + name = make_armature_action_name(rig_id, motion_id, prefix, slotted=False) + return name + + +def rename_motion_set(action_type, set_id, prefix, rig_id, motion_id): + actions = get_motion_set_actions(set_id) + is_rlx = is_rlx_motion(actions) + motion_id = get_unique_set_motion_id(rig_id, motion_id, prefix, + exclude_set_id=set_id, + slotted=(action_type == "SLOTTED"), rlx=is_rlx) + for action in actions: + a_set_id, a_set_generation, action_type_id, obj_id = get_motion_set_ids(action) + if action_type_id == "ARM": + name = make_armature_action_name(rig_id, motion_id, prefix, slotted=False) + action.name = name + elif action_type_id == "KEY": + name = make_key_action_name(rig_id, motion_id, obj_id, prefix) + action.name = name + elif action_type_id == "SLOTTED": + name = make_armature_action_name(rig_id, motion_id, prefix, slotted=True) + action.name = name + elif action_type_id == "RLX": + name = make_rlx_action_name(rig_id, motion_id, prefix, slotted=True) + action.name = name def rename_armature(arm, name): @@ -1341,8 +1502,9 @@ def set_bone_deform(bone: bpy.types.EditBone, use_deform, objects): except: ... -def fix_cc3_standard_rig(cc3_rig): +def fix_cc3_standard_rig(cc3_rig: bpy.types.Object): if edit_rig(cc3_rig): + cc3_rig.show_in_front = False objects = utils.get_child_objects(cc3_rig, of_type="MESH") left_eye = bones.get_edit_bone(cc3_rig, "CC_Base_L_Eye") right_eye = bones.get_edit_bone(cc3_rig, "CC_Base_R_Eye") @@ -1869,6 +2031,7 @@ def bake_rig_action(src_rig, dst_rig, reuse_action=False): if utils.try_select_object(dst_rig, True) and utils.set_active_object(dst_rig): utils.log_info(f"Baking action: {src_action.name} to {dst_rig.name}") # frame range + bpy.context.scene.use_preview_range = False if src_action: start_frame = int(src_action.frame_range[0]) end_frame = int(src_action.frame_range[1]) @@ -2191,7 +2354,7 @@ def get_bone_orientation(rig, bone_set: set): def get_widget_rig_collection(chr_cache): - try_names = [ chr_cache.character_name ] + try_names = [ chr_cache.get_name() ] rig = chr_cache.get_armature() rig_name = utils.strip_name(rig.name) if rig_name.endswith("_Rigify"): @@ -2212,7 +2375,7 @@ def get_widget_rig_collection(chr_cache): def get_expression_widgets(chr_cache, collection_name): - chr_name = chr_cache.character_name + chr_name = chr_cache.get_name() facial_profile, viseme_profile = chr_cache.get_facial_profile() tag = "" if facial_profile == "EXT": @@ -2528,738 +2691,22 @@ def de_pivot(chr_cache): -class CCICMotionSetFunctions(bpy.types.Operator): - bl_idname = "ccic.motion_set_funcs" - bl_label = "Motion Set Functions" - bl_options = {"REGISTER", "UNDO", "INTERNAL"} - param: bpy.props.StringProperty( - name = "param", - default = "" - ) - def execute(self, context): - props = vars.props() - - props.store_ui_list_indices() - action = props.action_set_list_action - #chr_cache = props.get_context_character_cache(context) - - if self.param == "DELETE": - delete_motion_set(action) - - elif self.param == "DUPLICATE": - duplicate_motion_set(action) - - return {"FINISHED"} - - @classmethod - def description(cls, context, properties): - return "Show motion set info" - - -class CCICMotionSetRename(bpy.types.Operator): - bl_idname = "ccic.motion_set_rename" - bl_label = "Rename Motion Set" - - action: bpy.props.StringProperty(name="Action", default="") - prefix: bpy.props.StringProperty(name="Motion Prefix", default="") - rig_id: bpy.props.StringProperty(name="Character / Rig ID", default="") - motion_id: bpy.props.StringProperty(name="Motion Name / ID", default="") - set_id = bpy.props.StringProperty(name="Set ID", default="") - action_type = bpy.props.StringProperty(name="Action Type", default="") - - def execute(self, context): - props = vars.props() - prefix = self.prefix - rig_id = self.rig_id - motion_id = get_unique_set_motion_id(rig_id, self.motion_id, prefix, - exclude_set_id=self.set_id, - slotted=(self.action_type == "SLOTTED")) - for action in bpy.data.actions: - if "rl_set_id" in action: - if action["rl_set_id"] == self.set_id: - set_id, set_generation, action_type_id, obj_id = get_motion_set_ids(action) - if action_type_id == "ARM": - name = make_armature_action_name(rig_id, motion_id, prefix, slotted=False) - action.name = name - elif action_type_id == "KEY": - name = make_key_action_name(rig_id, motion_id, obj_id, prefix) - action.name = name - elif action_type_id == "SLOTTED": - name = make_armature_action_name(rig_id, motion_id, prefix, slotted=True) - action.name = name - return {"FINISHED"} - - def invoke(self, context, event): - props = vars.props() - prefs = vars.prefs() - - props.store_ui_list_indices() - action = props.action_set_list_action - self.action = action.name - chr_cache = props.get_context_character_cache(context) - - if not action: - return {"FINISHED"} - - set_id, set_generation, action_type_id, key_object = get_motion_set_ids(action) - self.action_type = action_type_id - - if not set_id: - return {"FINISHED"} - - self.set_id = set_id - - prefix, rig_id, type_id, obj_id, motion_id = decode_action_name(action) - if prefix: - self.prefix = prefix - else: - self.prefix = "" - if rig_id: - self.rig_id = rig_id - else: - self.rig_id = "" - if motion_id: - self.motion_id = motion_id - elif action.name: - self.motion_id = action.name.split("|")[-1] - else: - self.motion_id = "Motion" - - return context.window_manager.invoke_props_dialog(self, width=400) - - def draw(self, context): - layout = self.layout - - split = layout.split(factor=0.35) - col_1 = split.column() - col_2 = split.column() - - col_1.label(text="Motion Set ID:") - col_2.label(text=self.set_id) - - col_1.separator() - col_2.separator() - - col_1.label(text="Prefix:") - col_2.prop(self, "prefix", text="") - - col_1.separator() - col_2.separator() - - col_1.label(text="Character / Rig ID:") - col_2.prop(self, "rig_id", text="") - - col_1.separator() - col_2.separator() - - col_1.label(text="Motion Name / ID:") - col_2.prop(self, "motion_id", text="") - - layout.separator() - - @classmethod - def description(cls, context, properties): - return "Change the name, prefix, and character/rig id of the motion set" - - -class CCICMotionSetInfo(bpy.types.Operator): - bl_idname = "ccic.motion_set_info" - bl_label = "Motion Set Info" - - prefix: bpy.props.StringProperty(name="Motion Prefix", default="") - rig_id: bpy.props.StringProperty(name="Character / Rig ID", default="") - motion_id: bpy.props.StringProperty(name="Motion Name / ID", default="") - set_id: bpy.props.StringProperty(name="Set ID", default="") - - def execute(self, context): - return {"FINISHED"} - - def invoke(self, context, event): - props = vars.props() - prefs = vars.prefs() - - props.store_ui_list_indices() - action = props.action_set_list_action - chr_cache = props.get_context_character_cache(context) - - self.delete_me = False - # TODO make delete an op button? - # TODO use_fake_user button - - if not action: - return {"FINISHED"} - - set_id, set_generation, action_type_id, key_object = get_motion_set_ids(action) - - if not set_id: - return {"FINISHED"} - - self.set_id = set_id - - prefix, rig_id, type_id, obj_id, motion_id = decode_action_name(action) - if prefix: - self.prefix = prefix - if rig_id: - self.rig_id = rig_id - elif chr_cache: - self.rig_id = chr_cache.character_name - else: - self.rig_id = "Rig" - if motion_id: - self.motion_id = motion_id - elif action.name: - self.motion_id = action.name.split("|")[-1] - else: - self.motion_id = "Motion" - - return context.window_manager.invoke_popup(self, width=600) - - def draw(self, context): - layout = self.layout - - split = layout.split(factor=0.25) - col_1 = split.column() - col_2 = split.column() - - col_1.label(text="Motion Set ID:") - col_2.label(text=self.set_id) - - col_1.separator() - col_2.separator() - - col_1.label(text="Prefix:") - col_2.label(text=self.prefix if self.prefix else "(None)") - col_1.label(text="Character / Rig ID:") - col_2.label(text=self.rig_id if self.rig_id else "(None)") - col_1.label(text="Motion Name / ID:") - col_2.label(text=self.motion_id if self.motion_id else "(None)") - - layout.separator() - - layout.label(text="Actions:") - - split = layout.split(factor=0.25) - col_1 = split.column() - col_2 = split.column() - for action in bpy.data.actions: - action_set_id = utils.get_prop(action, "rl_set_id") - action_type = utils.get_prop(action, "rl_action_type") - if action_set_id == self.set_id: - if action_type == "ARM": - col_1.label(text="Armature") - elif action_type == "KEY": - obj_id = utils.get_prop(action, "rl_key_object", "(None)") - col_1.label(text=obj_id) - else: - col_1.label(text="?") - col_2.label(text=action.name) - col_1.separator() - col_2.separator() - col_1.separator() - row = col_2.split(factor=0.5).column().row() - row.alert = True - row.scale_y = 1.5 - row.operator("ccic.rigutils", text="Delete Motion Set", icon="ERROR").param = "DELETE_MOTION_SET" - layout.separator() - layout.separator() - layout.separator() - - @classmethod - def description(cls, context, properties): - return "Show motion set info" - -#region Operators -class CCICRigUtils(bpy.types.Operator): - """Rig Utilities""" - bl_idname = "ccic.rigutils" - bl_label = "Rig Utils" - bl_options = {"REGISTER"} - - param: bpy.props.StringProperty( - name = "param", - default = "", - options={"HIDDEN"} - ) - - def execute(self, context): - props = vars.props() - prefs = vars.prefs() - chr_cache = props.get_context_character_cache(context) - rig = chr_cache.get_armature() if chr_cache else None - - if chr_cache: - - props.store_ui_list_indices() - - if rig: - if self.param == "TOGGLE_SHOW_FULL_RIG": - toggle_show_full_rig(rig) - - elif self.param == "TOGGLE_SHOW_BASE_RIG": - toggle_show_base_rig(rig) - - elif self.param == "TOGGLE_SHOW_SPRING_RIG": - toggle_show_spring_rig(rig) - - elif self.param == "TOGGLE_SHOW_RIG_POSE": - toggle_rig_rest_position(rig) - - elif self.param == "TOGGLE_SHOW_SPRING_BONES": - springbones.toggle_show_spring_bones(chr_cache) - - elif self.param == "TOGGLE_EXPRESSION_RIG_LOCK": - facerig.toggle_lock_position(chr_cache, rig) - - elif self.param == "BUTTON_RESET_POSE_SELECTED": - mode_selection = utils.store_mode_selection_state() - reset_pose(rig, use_selected=True) - utils.restore_mode_selection_state(mode_selection) - - elif self.param == "BUTTON_RESET_POSE": - mode_selection = utils.store_mode_selection_state() - reset_pose(rig, use_selected=False) - utils.restore_mode_selection_state(mode_selection) - - elif self.param == "RESET_EXPRESSION_POSE": - if chr_cache.rigified: - mode_selection = utils.store_mode_selection_state() - facerig.clear_expression_pose(chr_cache, rig) - utils.restore_mode_selection_state(mode_selection) - - elif self.param == "RESET_EXPRESSION_POSE_SELECTED": - if chr_cache.rigified: - mode_selection = utils.store_mode_selection_state() - facerig.clear_expression_pose(chr_cache, rig, selected=True) - utils.restore_mode_selection_state(mode_selection) - - elif self.param == "TOGGLE_SHOW_FACE_RIG": - toggle_show_only_face_rig(rig) - - elif self.param == "SET_LIMB_FK": - if chr_cache.rigified: - set_rigify_ik_fk_influence(rig, 1.0) - poke_rig(rig) - - elif self.param == "SET_LIMB_IK": - if chr_cache.rigified: - set_rigify_ik_fk_influence(rig, 0.0) - poke_rig(rig) - - elif self.param == "LOAD_MOTION_SET": - action = props.action_set_list_action - load_motion_set(rig, action) - - elif self.param == "PUSH_ACTION_SET": - action = props.action_set_list_action - auto_index = chr_cache.get_auto_index() - push_motion_set(rig, action, auto_index) - - elif self.param == "CLEAR_ACTION_SET": - clear_motion_set(rig) - - elif self.param == "NEW_ACTION_SET": - new_motion_set(rig) - - elif self.param == "DISABLE_CONSTRAINT_STRETCH": - mode_selection = utils.store_mode_selection_state() - rigify_rig = chr_cache.get_armature() - disable_ik_stretch(rigify_rig) - utils.restore_mode_selection_state(mode_selection) - - elif self.param == "ENABLE_CONSTRAINT_STRETCH": - mode_selection = utils.store_mode_selection_state() - rigify_rig = chr_cache.get_armature() - restore_ik_stretch(rigify_rig=rigify_rig) - utils.restore_mode_selection_state(mode_selection) - - if self.param == "SELECT_SET_STRIPS": - strip = context.active_nla_strip - select_strips_by_set(strip) - - elif self.param == "NLA_ALIGN_LEFT": - strips = context.selected_nla_strips - align_strips(strips, left=True) - - elif self.param == "NLA_ALIGN_TO_LEFT": - strips = context.selected_nla_strips - active_strip = context.active_nla_strip - align_strips(strips, to_strip=active_strip, left=True) - - elif self.param == "NLA_ALIGN_RIGHT": - strips = context.selected_nla_strips - align_strips(strips, left=False) - - elif self.param == "NLA_ALIGN_TO_RIGHT": - strips = context.selected_nla_strips - active_strip = context.active_nla_strip - align_strips(strips, to_strip=active_strip, left=False) - - elif self.param == "NLA_SIZE_SHORTEST": - strips = context.selected_nla_strips - size_strips(strips, longest=False) - - elif self.param == "NLA_SIZE_LONGEST": - strips = context.selected_nla_strips - size_strips(strips, longest=True) - - elif self.param == "NLA_SIZE_TO": - strips = context.selected_nla_strips - active_strip = context.active_nla_strip - size_strips(strips, to_strip=active_strip) - - elif self.param == "NLA_RESET_SIZE": - strips = context.selected_nla_strips - size_strips(strips, reset=True) - - elif self.param == "SET_FAKE_USER_ON": - action = props.action_set_list_action - set_action_set_fake_user(action, True) - - elif self.param == "SET_FAKE_USER_OFF": - action = props.action_set_list_action - set_action_set_fake_user(action, False) - - elif self.param == "DELETE_MOTION_SET": - action = props.action_set_list_action - delete_motion_set(action) - - props.restore_ui_list_indices() - - if self.param == "CLEAN_ACTIONS": - if utils.object_exists_is_camera(context.object) or utils.object_exists_is_light(context.object): - clean_actions(context.object) - elif rig: - clean_actions(rig) - - return {"FINISHED"} - - @classmethod - def description(cls, context, properties): - - if properties.param == "TOGGLE_SHOW_SPRING_BONES": - return "Quick toggle for the armature layers to show just the spring bones or just the body bones" - - elif properties.param == "TOGGLE_SHOW_FULL_RIG": - return "Toggles showing all the rig controls" - - elif properties.param == "TOGGLE_SHOW_BASE_RIG": - return "Toggles showing the base rig controls" - - elif properties.param == "TOGGLE_SHOW_SPRING_RIG": - return "Toggles showing just the spring rig controls" - - elif properties.param == "TOGGLE_SHOW_RIG_POSE": - return "Toggles the rig between pose mode and rest pose" - - elif properties.param == "TOGGLE_SHOW_FACE_RIG": - return "Toggles showing just the face expression rig controls" - - elif properties.param == "TOGGLE_EXPRESSION_RIG_LOCK": - return "Toggle locking the position of the expression rig and making unselectable" - - elif properties.param == "BUTTON_RESET_POSE": - return "Clears all pose transforms" - - elif properties.param == "BUTTON_RESET_POSE_SELECTED": - return "Clears the pose on all selected bones" - - elif properties.param == "RESET_EXPRESSION_POSE": - return "Clears the expression on all expression controls" - - elif properties.param == "RESET_EXPRESSION_POSE_SELECTED": - return "Clears the pose on all selected expression rig bones" - - elif properties.param == "LOAD_MOTION_SET": - return "Loads the chosen motion set (armature and shape key actions) into the all the character objects" - - elif properties.param == "PUSH_ACTION_SET": - return "Pushes the chosen motion set (armature and shape key actions) into the NLA tracks of all the character objects at the current frame. " \ - "A suitable track will be chosen to fit the actions. If there is no room available a new track will be added to contain the actions" - - elif properties.param == "CLEAR_ACTION_SET": - return "Removes all actions from the character" - - elif properties.param == "SELECT_SET_STRIPS": - return "Selects all the strips belonging to the same motion set and strip index" - - elif properties.param == "NLA_ALIGN_LEFT": - return "Aligns all selected strips to the left most of frame of all selected strips" - - elif properties.param == "NLA_ALIGN_RIGHT": - return "Aligns all selected strips to right most of frame of all selected strips" - - elif properties.param == "NLA_ALIGN_TO_LEFT": - return "Aligns all selected strips to the left hand frame of the active strip" - - elif properties.param == "NLA_ALIGN_TO_RIGHT": - return "Aligns all selected strips to the right hand frame of the active strip" - - elif properties.param == "NLA_SIZE_SHORTEST": - return "Sets the frame lengths of all selected strips to the length of the shortest strip in the selection" - - elif properties.param == "NLA_SIZE_TO": - return "Sets the frame lengths of all selected strips to the length of the active strip" - - elif properties.param == "NLA_SIZE_LONGEST": - return "Sets the frame lengths of all selected strips to the length of the longest strip in the selection" - - elif properties.param == "NLA_RESET_SIZE": - return "Resets the frame lengths of all selected strips to the length of the underlying action" - - elif properties.param == "SET_FAKE_USER_ON": - return "Set fake user on all actions in the motion set" - - elif properties.param == "SET_FAKE_USER_OFF": - return "Clear fake user on all actions in the motion set" - - elif properties.param == "DELETE_MOTION_SET": - return "Delete all actions in the motion set" - - elif properties.param == "DISABLE_CONSTRAINT_STRETCH": - return "Disable stretch in all IK mechanisms on the rig. By default the Blender Rigify rig allows a certain amount of stretch in the bones ease IK alignment.\n" \ - "But in other applications, this bone stretch is not possible, disabling the IK stretch system can aid with animation alignment problems" - - elif properties.param == "ENABLE_CONSTRAINT_STRETCH": - return "Re-enable the IK stretch mechanisms in the rig" - - return "" - - -class CCIC_ImportMixBones_UL_List(bpy.types.UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - if self.layout_type in {'DEFAULT', 'COMPACT'}: - layout.label(text=item.name if item else "", translate=False, icon_value=icon) - elif self.layout_type in {'GRID'}: - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) - - def filter_items(self, context, data, propname): - filtered = [] - ordered = [] - items = getattr(data, propname) - filtered = [self.bitflag_filter_item] * len(items) - for i, item in enumerate(items): - allowed = True - # filter by name - if self.filter_name and self.filter_name != "*": - if self.filter_name not in item.name: - allowed = False - # block not allowed - if not allowed: - filtered[i] &= ~self.bitflag_filter_item - return filtered, ordered - - -class CCICActionImportFunctions(bpy.types.Operator): - """Action Import Functions""" - bl_idname = "ccic.action_import_functions" - bl_label = "Action Import Functions" - bl_options = {"REGISTER", "UNDO"} - - param: bpy.props.StringProperty( - name = "param", - default = "", - options={"HIDDEN"} - ) - - def execute(self, context): - props = vars.props() - prefs = vars.prefs() - chr_cache = props.get_context_character_cache(context) - if chr_cache: - if self.param == "ADD_BONE": - self.add_bone(chr_cache) - elif self.param == "REMOVE_BONE": - self.remove_bone(chr_cache) - return {"FINISHED"} - - def add_bone(self, chr_cache): - arm = chr_cache.get_armature() - props = chr_cache.action_options - bone_index = props.rig_mix_bones_list_index - bone = arm.data.bones[bone_index] - for bone_item in props.import_mix_bones: - if bone_item.name == bone.name: - return - bone_item = props.import_mix_bones.add() - bone_item.name = bone.name - bone_item.weight = 1.0 - - def remove_bone(self, chr_cache): - props = chr_cache.action_options - index = props.import_mix_bones_list_index - try: - props.import_mix_bones.remove(index) - except: - print(f"Unable to remove import mix bones index: {index}") - - - @classmethod - def description(cls, context, properties): - return "" - - -class CCIC_RigMixBones_UL_List(bpy.types.UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - if self.layout_type in {'DEFAULT', 'COMPACT'}: - layout.label(text=item.name if item else "", translate=False, icon_value=icon) - elif self.layout_type in {'GRID'}: - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) - - def filter_items(self, context, data, propname): - props = vars.props() - filtered = [] - ordered = [] - items = getattr(data, propname) - filtered = [self.bitflag_filter_item] * len(items) - item : bpy.types.Action - chr_cache = props.get_context_character_cache(context) - if chr_cache: - arm = chr_cache.get_armature() - for i, item in enumerate(items): - allowed = True - # filter by name - if self.filter_name and self.filter_name != "*": - if self.filter_name not in item.name: - allowed = False - # block not allowed - if not allowed: - filtered[i] &= ~self.bitflag_filter_item - return filtered, ordered - - -class CCICActionImportOptions(bpy.types.Operator): - """Action Import Options""" - bl_idname = "ccic.action_import_options" - bl_label = "Action Import Options" - bl_options = {"REGISTER", "UNDO"} - - chr_cache = None - objects = {} - - @classmethod - def poll(cls, context): - return True - #props = vars.props() - #return props.get_context_character_cache(context) is not None - - @classmethod - def label(cls, context, chr_cache=None): - props = vars.props() - if not chr_cache: - chr_cache = props.get_context_character_cache(context, strict=True) - if not chr_cache: - opts = props.action_options - if chr_cache and chr_cache.action_options: - opts = chr_cache.action_options - action_text = { - "NEW": "New", - "REPLACE": "Replace", - "BLEND": "Overwrite", - } - frame_text = { - "START": "Start", - "CURRENT": "Current", - "MATCH": "Match", - } - mask_text = " (Mask)" if opts.use_masking else "" - return f"{action_text[opts.get_action_mode()]} | {frame_text[opts.get_frame_mode()]}{mask_text}" - - def draw(self, context): - prefs = vars.prefs() - props = vars.props() - layout = self.layout - column = layout.column() - - # prefs - column.label(text="Preferences:") - row = column.row() - grid = row.grid_flow(row_major=True, columns=2) - if utils.B440(): - grid.prop(prefs, "action_use_action_slots") - grid.prop(prefs, "action_add_key_slots_per_obj") - #grid.prop(prefs, "action_clean_actions") - grid.prop(prefs, "action_add_empty_key_channels") - - if True: - opts = props.action_options - column.separator() - column.row().label(text="Global Motion Options:") - column.row().prop(opts, "action_mode") - column.row().prop(opts, "frame_mode") - - if self.chr_cache and self.chr_cache.action_options: - arm = self.chr_cache.get_armature() - opts = self.chr_cache.action_options - - column.separator() - - row = column.row() - row.prop(opts, "override_global") - row.label(text=f"{self.chr_cache.character_name}") - column = column.column() - column.enabled = opts.override_global - column.row().prop(opts, "action_mode") - column.row().prop(opts, "frame_mode") - - return - - column.separator() - - column.row().prop(opts, "use_masking") - if opts.use_masking: - row = column.row() - row.template_list("CCIC_RigMixBones_UL_List", "rig_mix_bones_list", - arm.data, "bones", - opts, "rig_mix_bones_list_index", - rows=8, maxrows=8) - col = row.column() - col.separator(factor=4.0) - col.operator("ccic.action_import_functions", text="", icon="PLAY").param = "ADD_BONE" - col.separator(factor=4.0) - col.operator("ccic.action_import_functions", text="", icon="PLAY_REVERSE").param = "REMOVE_BONE" - col.separator(factor=4.0) - row.template_list("CCIC_ImportMixBones_UL_List", "import_mix_bones_list", - opts, "import_mix_bones", - opts, "import_mix_bones_list_index", - rows=8, maxrows=8) - - - def execute(self, context): - props = vars.props() - prefs = vars.prefs() - #self.chr_cache = props.get_context_character_cache(context) - return {'FINISHED'} - - def invoke(self, context, event): - props = vars.props() - prefs = vars.prefs() - utils.set_mode("OBJECT") - self.chr_cache = props.get_context_character_cache(context, strict=True) - return context.window_manager.invoke_props_dialog(self, width=500) - - @classmethod - def description(cls, context, properties): - return "Description" - -# endregion - - -def add_action_slot_channelbag(action, name, slot_type, reuse=False, clear=False): +#region Actions +def add_action_slot_channelbag(action, name: str, slot_type: str, reuse=False, clear=False): + slot_name = f"{name} ({slot_type.capitalize()})" if utils.B440(): channelbags = utils.get_action_channelbags(action) if reuse: for channelbag in channelbags: - if channelbag.slot and channelbag.slot.target_id_type == slot_type and channelbag.slot.name_display == name: + if channelbag.slot and channelbag.slot.target_id_type == slot_type and channelbag.slot.name_display == slot_name: slot = channelbag.slot channel = channelbag if clear: channel.fcurves.clear() return slot, channel - slot = action.slots.new(slot_type, name) + slot = action.slots.new(slot_type, slot_name) channel = channelbags.new(slot) else: slot = None @@ -3275,6 +2722,12 @@ def add_action_ob_slot_channelbag(action, obj: bpy.types.Object, reuse=False, cl return add_action_slot_channelbag(action, obj.name, "OBJECT", reuse=reuse, clear=clear) +def add_action_data_slot_channelbag(action, obj: bpy.types.Object, slot_type=None, reuse=False, clear=False): + if not slot_type: + slot_type = obj.type + return add_action_slot_channelbag(action, obj.name, slot_type, reuse=reuse, clear=clear) + + def is_slotted_action(action): return utils.B440() and len(action.slots) > 1 @@ -3296,6 +2749,7 @@ def copy_action_shape_key_channels(rig, src_action, dst_action, fake_user=True): copy_slot_actions(src_action, dst_action, "KEY") else: rig_id = get_rig_id(rig) + rl_id = utils.get_rl_id(rig) motion_set = get_motion_set_ids(dst_action) set_id = motion_set[0] set_generation = motion_set[1] @@ -3309,7 +2763,7 @@ def copy_action_shape_key_channels(rig, src_action, dst_action, fake_user=True): copy_action: bpy.types.Action = key_action.copy() copy_action.name = action_name copy_action.use_fake_user = fake_user - add_motion_set_data(copy_action, set_id, set_generation, obj_id=key_object, slotted=False) + add_motion_set_data(copy_action, set_id, set_generation, key_id=key_object, arm_id=rl_id, slotted=False) copy_actions.append(copy_action) return copy_actions @@ -3325,34 +2779,56 @@ def fetch_action_curve_database(source_action): source_actions = find_source_actions(source_action) if source_actions["armature"]: source_action = source_actions["armature"] - source_arm_actions = [a for a in source_actions["armatures"]] - source_key_actions = [a for a in source_actions["keys"].values()] - arm_actions = [a.copy() for a in source_arm_actions] - key_actions = [a.copy() for a in source_key_actions] + elif source_actions["rlx"]: + source_action = source_actions["rlx"] + source_arm_actions = [a for a in source_actions["armatures"] if a is not None] + source_key_actions = [a for a in source_actions["keys"].values() if a is not None] + source_rlx_action = source_actions["rlx"] + source_light_action = source_actions["light"] + source_camera_action = source_actions["camera"] + arm_actions = [a.copy() for a in source_arm_actions if a is not None] + key_actions = [a.copy() for a in source_key_actions if a is not None] + rlx_action = source_rlx_action.copy() if source_rlx_action else None + light_action = source_light_action.copy() if source_light_action else None + camera_action = source_camera_action.copy() if source_camera_action else None + source_rlx_actions = utils.list_if(source_rlx_action, source_light_action, source_camera_action) + rlx_actions = utils.list_if(rlx_action, light_action, camera_action) for arm_action in arm_actions: arm_action.name = "DB_AC_TMP" for key_action in key_actions: key_action.name = "DB_KC_TMP" - data_curves = { "OBJECT": {}, "KEY": {} } + if rlx_action: + rlx_action.name = "DB_RC_TMP" + if light_action: + light_action.name = "DB_LC_TMP" + if camera_action: + camera_action.name = "DB_CC_TMP" + data_curves = { "OBJECT": {}, "KEY": {}, "LIGHT": {}, "CAMERA": {} } database["source_action"] = source_action database["source_arm_actions"] = source_arm_actions database["source_key_actions"] = source_key_actions + database["source_rlx_actions"] = source_rlx_actions database["arm_actions"] = arm_actions database["key_actions"] = key_actions - all_actions = [] - database["all_actions"] = all_actions - for a in source_arm_actions + source_key_actions + arm_actions + key_actions: - if a not in all_actions: - all_actions.append(a) + database["rlx_actions"] = rlx_actions database["curves"] = data_curves first_frame = None if utils.B440(): - all_actions = arm_actions + key_actions + all_actions = arm_actions + key_actions + rlx_actions a: bpy.types.Action + done_channelbags = [] + done_actions = [] for a in all_actions: + if a in done_actions: continue + done_actions.append(a) channelbags = utils.get_action_channelbags(a) # build database of fcurves by slot for channel in channelbags: + if channel in done_channelbags: continue + if channel.slot == None: + utils.log_info(f"No slot in channel {channel}") + continue + done_channelbags.append(channel) slot = channel.slot is_source_channel = False for MC in SOURCE_CHANNELS: @@ -3360,6 +2836,7 @@ def fetch_action_curve_database(source_action): is_source_channel = True slot_type = slot.target_id_type if slot_type not in data_curves: + utils.log_info(f"Found unexpected slot type: {slot_type}") data_curves[slot_type] = {} slot_curves = data_curves[slot_type] fcurve: bpy.types.FCurve = None @@ -3379,9 +2856,19 @@ def fetch_action_curve_database(source_action): all_actions.append(("OBJECT", arm_action)) for key_action in key_actions: all_actions.append(("KEY", key_action)) + if rlx_action: + all_actions.append(("OBJECT", rlx_action)) + if light_action: + all_actions.append(("LIGHT", light_action)) + if camera_action: + all_actions.append(("CAMERA", camera_action)) + done_actions = [] for slot_type, a in all_actions: + if a in done_actions: continue + done_actions.append(a) # build database of fcurves by slot if slot_type not in data_curves: + utils.log_info(f"Found unexpected slot type: {slot_type}") data_curves[slot_type] = {} slot_curves = data_curves[slot_type] fcurve: bpy.types.FCurve = None @@ -3399,34 +2886,47 @@ def fetch_action_curve_database(source_action): return database -def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): +def get_channelbag_group(channelbag, group_name): + group = None + if group_name: + if group_name in channelbag.groups: + group = channelbag.groups[group_name] + else: + group = channelbag.groups.new(group_name) + return group + + +def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None, force=False): prefs = vars.prefs() # determine motion set info rig_id = get_rig_id(rig) - rl_arm_id = utils.get_rl_object_id(rig) + rl_id = utils.get_rl_id(rig) old_action = utils.safe_get_action(rig) set_id = utils.get_prop(action, "rl_set_id") set_generation = utils.get_prop(action, "rl_set_generation") motion_prefix = get_motion_prefix(action) motion_id = get_motion_id(action) - src_rl_arm_id = utils.get_prop(action, "rl_armature_id") + src_rl_id = utils.get_rl_id(action) use_fake_user = action.use_fake_user # if loading onto a different rig_id, we want to create a new motion set ... - use_new_motion_set = (rl_arm_id != src_rl_arm_id) or temp or move + use_new_motion_set = not force and (((rl_id or src_rl_id) and rl_id != src_rl_id) or temp or move) if temp: motion_prefix = "TEMP" motion_id = temp if use_new_motion_set: set_id = generate_set_id() + utils.log_info(f"Generating new motion set: {set_id}") # generate a database of fcurves in the motion set database = fetch_action_curve_database(action) # re-use the existing source armature action if use_new_motion_set: - motion_id = get_unique_set_motion_id(rig_id, motion_id, motion_prefix, slotted=True) + + # Point light, Export_20260630130902, Point light_1 + motion_id = get_unique_set_motion_id(rig_id, motion_id, motion_prefix, slotted=True, rlob=rig) action_name = generate_action_name(rig_id, "", "", motion_id, motion_prefix) # if temp, re-use any existing temp action @@ -3437,17 +2937,24 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): utils.force_action_name(action, action_name) # add motion set data - add_motion_set_data(action, set_id, set_generation, arm_id=rl_arm_id, slotted=True) + arm_id = rl_id if rig.type == "ARMATURE" else None + rlx_id = None if rig.type == "ARMATURE" else rl_id + add_motion_set_data(action, set_id, set_generation, arm_id=arm_id, rlx_id=rlx_id, slotted=True) action.use_fake_user = use_fake_user # if this is a temporary action, store the action it replaced if temp: action["rl_temp_action"] = old_action.name if old_action else "" + elif force: + # add motion set data + arm_id = rl_id if rig.type == "ARMATURE" else None + rlx_id = None if rig.type == "ARMATURE" else rl_id + add_motion_set_data(action, set_id, set_generation, arm_id=arm_id, rlx_id=rlx_id, slotted=True) else: action: bpy.types.Action = database["source_action"] # build channels for each object in rig - utils.log_info(f"Retargetting Slotted Action: {action.name}") + utils.log_info(f"Rebuilding Slotted Action: {action.name}") keep_slots = [] keep_channels = [] data_curves = database["curves"] @@ -3462,25 +2969,30 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): # add armature (object) slot rig_slot, rig_channel = add_action_ob_slot_channelbag(action, rig, reuse=True, clear=True) - # for each bone fcurve in the database in the order of the pose bones on the rig ... - for bone in rig.pose.bones: + if rig.type == "ARMATURE": + # for each bone fcurve in the database in the order of the pose bones on the rig ... + for bone in rig.pose.bones: + for id, fcurve in slot_curves.items(): + curve_path, index = id + # that exist on this rig: + if id not in used_ids and bone.name in curve_path and utils.has_data_path(rig, curve_path): + clone = copy_fcurve_to_channel(fcurve, rig_channel) + # add fcurve to bone group + group_name = utils.get_data_path_object_name(curve_path) + clone.group = get_channelbag_group(rig_channel, group_name) + # store used id's + used_ids.add(id) + + if True: #if rig.type == "LIGHT" or rig.type == "CAMERA": + # for each remaining fcurve data path in the database for id, fcurve in slot_curves.items(): curve_path, index = id - - # that exist on this rig: - if id not in used_ids and bone.name in curve_path and utils.has_data_path(rig, curve_path): - + # that have valid data paths on this rig: + if id not in used_ids and utils.has_data_path(rig, curve_path): clone = copy_fcurve_to_channel(fcurve, rig_channel) - - # add fcurve to bone group + # add fcurve to Object group group_name = utils.get_data_path_object_name(curve_path) - if group_name: - if group_name in rig_channel.groups: - group = rig_channel.groups[group_name] - else: - group = rig_channel.groups.new(group_name) - clone.group = group - + clone.group = get_channelbag_group(rig_channel, group_name) # store used id's used_ids.add(id) @@ -3489,7 +3001,9 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): keep_channels.append(rig_channel) utils.safe_set_action(rig, action, create=True, slot=rig_slot) - # determine unused bone fcurves + # determine unused fcurves + # NOTE: unused fcurves are added after the action is loaded on to the rig, + # as the unused curves can get stripped from the action if added before. unused_ids = set() for id in slot_curves: if id not in used_ids: @@ -3497,8 +3011,9 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): if unused_ids: - # add slot for unused bone fcurves - rig_slot, rig_channel = add_action_slot_channelbag(action, "Unused Object", "OBJECT", reuse=True, clear=True) + # add slot for unused fcurves + rig_slot, rig_channel = add_action_slot_channelbag(action, "Unused Object", "OBJECT", + reuse=True, clear=True) # add unused fcurves for id in unused_ids: @@ -3513,7 +3028,7 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): # EXCLUDE_KEYS = ["Basis"] SOURCE_MESH_NAMES = ["CC_Base_Body", "CC_Game_Body", "CC_Base_Tongue", "CC_Base_Eye"] - if "KEY" in data_curves: + if "KEY" in data_curves and rig.type == "ARMATURE": slot_curves = data_curves["KEY"] used_ids = set() @@ -3531,7 +3046,8 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): if not prefs.action_add_key_slots_per_obj: # add shape key slot/channel for all mesh objects - key_slot, key_channel = add_action_slot_channelbag(action, "All Keys", "KEY", reuse=True, clear=True) + key_slot, key_channel = add_action_slot_channelbag(action, "All Keys", "KEY", + reuse=True, clear=True) # done id's across all meshes done_ids = set() @@ -3545,7 +3061,8 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): if prefs.action_add_key_slots_per_obj: # generate a specific shape key slot for each mesh object - key_slot, key_channel = add_action_key_slot_channelbag(action, obj, reuse=True, clear=True) + key_slot, key_channel = add_action_key_slot_channelbag(action, obj, + reuse=True, clear=True) # done id's per mesh done_ids = set() @@ -3574,16 +3091,9 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): fcurve = slot_curves[id] key_name = utils.get_data_path_object_name(curve_path) clone = copy_fcurve_to_channel(fcurve, key_channel) - # add fcurve to expression group group_name = get_shape_key_group_name(key_name) - if group_name: - if group_name in key_channel.groups: - group = key_channel.groups[group_name] - else: - group = key_channel.groups.new(group_name) - clone.group = group - + clone.group = get_channelbag_group(key_channel, group_name) # store used id's done_keys.add(key_name) used_ids.add(id) @@ -3614,12 +3124,7 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): # add fcurve to expression group group_name = get_shape_key_group_name(key.name) - if group_name: - if group_name in key_channel.groups: - group = key_channel.groups[group_name] - else: - group = key_channel.groups.new(group_name) - fcurve.group = group + fcurve.group = get_channelbag_group(key_channel, group_name) # keep slots/channels that have assigned keys and load onto mesh slot, # remove slots/channels that have no assigned keys @@ -3650,6 +3155,63 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): keep_slots.append(key_slot) keep_channels.append(key_channel) + # LIGHT / CAMERA (DATA) ACTIONS + # + slot_types = ["LIGHT", "CAMERA"] + for slot_type in slot_types: + + if slot_type in data_curves: + + if rig.type != slot_type: + continue + + slot_curves = data_curves[slot_type] + used_ids = set() + + # add data slot + data_slot, data_channel = add_action_slot_channelbag(action, f"{rig.name} ({slot_type.capitalize()})", slot_type, + reuse=True, clear=True) + + add_action_data_slot_channelbag(action, rig, slot_type=slot_type, reuse=True, clear=True) + + # for each fcurve data path in the database + for id, fcurve in slot_curves.items(): + curve_path, index = id + # that exist on this rig: + if id not in used_ids and utils.has_data_path(rig.data, curve_path): + clone = copy_fcurve_to_channel(fcurve, data_channel) + # add fcurve to Object group + group_name = utils.get_data_path_object_name(curve_path) + clone.group = get_channelbag_group(data_channel, group_name) + # store used id's + used_ids.add(id) + + # keep this slot/channel and load onto data slot + keep_slots.append(data_slot) + keep_channels.append(data_channel) + utils.safe_set_action(rig.data, action, create=True, slot=data_slot) + + # determine unused fcurves + unused_ids = set() + for id in slot_curves: + if id not in used_ids: + unused_ids.add(id) + + if unused_ids: + + # add slot for unused fcurves + data_slot, data_channel = add_action_slot_channelbag(action, "Unused Data", slot_type, + reuse=True, clear=True) + + # add unused fcurves + for id in unused_ids: + fcurve = slot_curves[id] + copy_fcurve_to_channel(fcurve, data_channel) + + # always keep this slot/channel + keep_slots.append(data_slot) + keep_channels.append(data_channel) + # remove old and unused slots and channels delete_slots = [] delete_channels = [] @@ -3666,11 +3228,12 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): action.slots.remove(slot) # remove all database copies - delete_actions = database["arm_actions"] + database["key_actions"] + delete_actions = database["arm_actions"] + database["key_actions"] + database["rlx_actions"] if move or not use_new_motion_set: # remove old actions if moving to a new motion set - delete_actions += database["source_arm_actions"] + database["source_key_actions"] + delete_actions += database["source_arm_actions"] + database["source_key_actions"] + database["source_rlx_actions"] # remove actions, except this action + utils.log_detail(f"Deleting actions: {[a.name for a in delete_actions]}, except: {action.name}") for delete_action in delete_actions: if utils.action_exists(delete_action): if delete_action != action: @@ -3687,22 +3250,22 @@ def load_slotted_action(rig, action: bpy.types.Action, move=False, temp=None): return action -def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move=False, temp=None): +def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move=False, temp=None, force=False): prefs = vars.prefs() # determine motion set info rig_id = get_rig_id(rig) - rl_arm_id = utils.get_rl_object_id(rig) + rl_id = utils.get_rl_id(rig) old_action = utils.safe_get_action(rig) set_id = utils.get_prop(action, "rl_set_id") set_generation = utils.get_prop(action, "rl_set_generation") motion_prefix = get_motion_prefix(action) motion_id = get_motion_id(action) - src_rl_arm_id = utils.get_prop(action, "rl_armature_id") + src_rl_id = utils.get_rl_id(action) use_fake_user = action.use_fake_user # if loading onto a different rig_id, we want to create a new motion set ... - use_new_motion_set = (rl_arm_id != src_rl_arm_id) or temp or move + use_new_motion_set = not force and (((rl_id or src_rl_id) and rl_id != src_rl_id) or temp or move) if temp: motion_prefix = "TEMP" motion_id = temp @@ -3727,9 +3290,9 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # re-use the existing source armature action if use_new_motion_set: - motion_id = get_unique_set_motion_id(rig_id, motion_id, motion_prefix, slotted=False) - action_name = generate_action_name(rig_id, "A", "", motion_id, motion_prefix) - + motion_id = get_unique_set_motion_id(rig_id, motion_id, motion_prefix, slotted=False, rlob=rig) + action_name = generate_action_name(rig_id, "A" if rig.type == "ARMATURE" else "O", + "", motion_id, motion_prefix) # if temp, re-use any existing temp action if temp and action_name in bpy.data.actions: rig_action = bpy.data.actions[action_name] @@ -3742,32 +3305,37 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # add slot and motion set data rig_slot, rig_channel = add_action_ob_slot_channelbag(rig_action, rig, reuse=True, clear=True) - add_motion_set_data(rig_action, set_id, set_generation, arm_id=rl_arm_id, slotted=False) + add_motion_set_data(rig_action, set_id, set_generation, arm_id=rl_id, slotted=False) rig_action.use_fake_user = use_fake_user # if this is a temporary action, store the action it replaced if temp: rig_action["rl_temp_action"] = old_action.name if old_action else "" - # for each bone fcurve in the database in the order of the pose bones on the rig ... - for bone in rig.pose.bones: + if rig.type == "ARMATURE": + # for each bone fcurve in the database in the order of the pose bones on the rig ... + for bone in rig.pose.bones: + for id, fcurve in slot_curves.items(): + curve_path, index = id + # that exist on this rig: + if id not in used_ids and bone.name in curve_path and utils.has_data_path(rig, curve_path): + clone = copy_fcurve_to_channel(fcurve, rig_channel) + # add fcurve to bone group + group_name = utils.get_data_path_object_name(curve_path) + clone.group = get_channelbag_group(rig_channel, group_name) + # store used id's + used_ids.add(id) + + if True: #if rig.type == "LIGHT" or rig.type == "CAMERA": + # for each remaining fcurve data path in the database for id, fcurve in slot_curves.items(): curve_path, index = id - - # that exist on this rig: - if id not in used_ids and bone.name in curve_path and utils.has_data_path(rig, curve_path): - + # that have valid data paths on this rig: + if id not in used_ids and utils.has_data_path(rig, curve_path): clone = copy_fcurve_to_channel(fcurve, rig_channel) - - # add fcurve to bone group + # add fcurve to Object group group_name = utils.get_data_path_object_name(curve_path) - if group_name: - if group_name in rig_channel.groups: - group = rig_channel.groups[group_name] - else: - group = rig_channel.groups.new(group_name) - clone.group = group - + clone.group = get_channelbag_group(rig_channel, group_name) # store used id's used_ids.add(id) @@ -3794,7 +3362,7 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # add slot and motion set data rig_slot, rig_channel = add_action_slot_channelbag(rig_action, "Unused Object", "OBJECT", reuse=True, clear=True) - add_motion_set_data(rig_action, set_id, set_generation, arm_id=rl_arm_id, slotted=False) + add_motion_set_data(rig_action, set_id, set_generation, arm_id=rl_id, slotted=False) rig_action.use_fake_user = use_fake_user # if this is a temporary action, store the action it replaced @@ -3813,7 +3381,7 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # EXCLUDE_KEYS = ["Basis"] SOURCE_MESH_NAMES = ["CC_Base_Body", "CC_Game_Body", "CC_Base_Tongue", "CC_Base_Eye"] - if "KEY" in data_curves: + if "KEY" in data_curves and rig.type == "ARMATURE": slot_curves = data_curves["KEY"] used_ids = set() @@ -3839,7 +3407,7 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= utils.force_action_name(key_action, action_name) # add slot and motion set data - add_motion_set_data(key_action, set_id, set_generation, obj_id="ALL", slotted=False) + add_motion_set_data(key_action, set_id, set_generation, key_id="ALL", arm_id=rl_id, slotted=False) key_slot, key_channel = add_action_slot_channelbag(key_action, "Key", "KEY", reuse=True, clear=True) key_action.use_fake_user = use_fake_user @@ -3869,7 +3437,7 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # add slot and motion set data key_slot, key_channel = add_action_key_slot_channelbag(key_action, obj, reuse=True, clear=True) - add_motion_set_data(key_action, set_id, set_generation, obj_id=obj_id, slotted=False) + add_motion_set_data(key_action, set_id, set_generation, key_id=obj_id, arm_id=rl_id, slotted=False) key_action.use_fake_user = use_fake_user # if this is a temporary action, store the action it replaced @@ -3894,25 +3462,16 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # whose data path exists on this object and is not driven if utils.has_data_path(obj.data.shape_keys, curve_path) and curve_path not in driver_paths: - # this action has keys for this mesh has_keys = True - # if not already added for this mesh if id not in done_ids: fcurve = slot_curves[id] key_name = utils.get_data_path_object_name(curve_path) clone = copy_fcurve_to_channel(fcurve, key_channel) - # add fcurve to expression group group_name = get_shape_key_group_name(key_name) - if group_name: - if group_name in key_channel.groups: - group = key_channel.groups[group_name] - else: - group = key_channel.groups.new(group_name) - clone.group = group - + clone.group = get_channelbag_group(key_channel, group_name) # store used id's done_keys.add(key_name) used_ids.add(id) @@ -3943,12 +3502,7 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # add fcurve to expression group group_name = get_shape_key_group_name(key.name) - if group_name: - if group_name in key_channel.groups: - group = key_channel.groups[group_name] - else: - group = key_channel.groups.new(group_name) - fcurve.group = group + fcurve.group = get_channelbag_group(key_channel, group_name) # keep actions that have assigned keys and load onto mesh, # remove actions that have no assigned keys @@ -3978,7 +3532,7 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # add slot and motion set data key_slot, key_channel = add_action_slot_channelbag(key_action, "Unused Keys", "KEY", reuse=True, clear=True) - add_motion_set_data(key_action, set_id, set_generation, obj_id="UNUSED", slotted=False) + add_motion_set_data(key_action, set_id, set_generation, key_id="UNUSED", arm_id=rl_id, slotted=False) key_action.use_fake_user = use_fake_user # if this is a temporary action, store the action it replaced @@ -3993,12 +3547,91 @@ def load_separate_actions(rig: bpy.types.Object, action: bpy.types.Action, move= # always keep this keep_actions.append(key_action) + # LIGHT / CAMERA (DATA) ACTIONS + # + slot_types = ["LIGHT", "CAMERA"] + for slot_type in slot_types: + + if slot_type in data_curves: + + if rig.type != slot_type: continue + + slot_curves = data_curves[slot_type] + used_ids = set() + + action_name = generate_action_name(rig_id, slot_type[0], "", motion_id, motion_prefix) + # if temp, re-use any existing temp action + if temp and action_name in bpy.data.actions: + data_action = bpy.data.actions[action_name] + else: + data_action = bpy.data.actions.new(action_name) + utils.force_action_name(data_action, action_name) + # add slot and motion set data + data_slot, data_channel = add_action_data_slot_channelbag(data_action, rig, slot_type=slot_type, reuse=True, clear=True) + add_motion_set_data(rig_action, set_id, set_generation, rlx_id=rl_id, slotted=False) + data_action.use_fake_user = use_fake_user + # if this is a temporary action, store the action it replaced + if temp: + data_action["rl_temp_action"] = old_action.name if old_action else "" + + # for each fcurve data path in the database + for id, fcurve in slot_curves.items(): + curve_path, index = id + # that exist on this rig: + if id not in used_ids and utils.has_data_path(rig.data, curve_path): + clone = copy_fcurve_to_channel(fcurve, data_channel) + # add fcurve to Object group + group_name = utils.get_data_path_object_name(curve_path) + clone.group = get_channelbag_group(data_channel, group_name) + # store used id's + used_ids.add(id) + + # keep this slot/channel and load onto rig slot + keep_actions.append(data_action) + utils.safe_set_action(rig.data, data_action, create=True, slot=data_slot) + + # determine unused fcurves + unused_ids = set() + for id in slot_curves: + if id not in used_ids: + unused_ids.add(id) + + if unused_ids: + + # generate an action for unused bones + action_name = generate_action_name(rig_id, "U"+slot_type[0], "", motion_id, motion_prefix) + if temp and action_name in bpy.data.actions: + data_action = bpy.data.actions[action_name] + else: + data_action = bpy.data.actions.new(action_name) + utils.force_action_name(data_action, action_name) + data_action["rl_unused_action"] = True + + # add slot and motion set data + data_slot, data_channel = add_action_slot_channelbag(data_action, "Unused Data", slot_type, reuse=True, clear=True) + add_motion_set_data(data_action, set_id, set_generation, rlx_id=rl_id, slotted=False) + data_action.use_fake_user = use_fake_user + + # if this is a temporary action, store the action it replaced + if temp: + data_action["rl_temp_action"] = old_action.name if old_action else "" + + # add unused fcurves + for id in unused_ids: + fcurve = slot_curves[id] + copy_fcurve_to_channel(fcurve, data_channel) + + # always keep this slot/channel + keep_actions.append(data_action) + # remove database copies ... remove_actions += database["arm_actions"] remove_actions += database["key_actions"] + remove_actions += database["rlx_actions"] if move or not use_new_motion_set: remove_actions += database["source_arm_actions"] remove_actions += database["source_key_actions"] + remove_actions += database["source_rlx_actions"] for del_action in remove_actions: if utils.action_exists(del_action) and del_action not in keep_actions: bpy.data.actions.remove(del_action) @@ -4036,10 +3669,33 @@ def get_shape_key_group_name(key_name): return "Custom" -def copy_fcurve_to_channel(from_fcurve: bpy.types.FCurve, to_channel) -> bpy.types.FCurve: - if utils.B500(): - to_fcurve: bpy.types.FCurve = to_channel.fcurves.new_from_fcurve(from_fcurve) +def copy_fcurve_to_channel(from_fcurve: bpy.types.FCurve, to_channel, no_frames=False) -> bpy.types.FCurve: + if from_fcurve and to_channel: + if utils.B500(): + to_fcurve: bpy.types.FCurve = to_channel.fcurves.new_from_fcurve(from_fcurve) + if no_frames: + to_fcurve.keyframe_points.clear() + else: + data_path = from_fcurve.data_path + index = from_fcurve.array_index + to_fcurve: bpy.types.FCurve = to_channel.fcurves.new(data_path, index=index) + to_fcurve.auto_smoothing = from_fcurve.auto_smoothing + to_fcurve.color = from_fcurve.color + to_fcurve.color_mode = from_fcurve.color_mode + to_fcurve.extrapolation = from_fcurve.extrapolation + to_fcurve.mute = from_fcurve.mute + to_fcurve.lock = from_fcurve.lock + if not no_frames: + from_keyframes, num_points = get_fcurve_keyframe_data(from_fcurve) + apply_fcurve_keyframe_data(to_fcurve, from_keyframes) + to_fcurve.update() + return to_fcurve else: + return None + + +def clone_fcurve_to_channel(from_fcurve: bpy.types.FCurve, to_channel) -> bpy.types.FCurve: + if from_fcurve and to_channel: data_path = from_fcurve.data_path index = from_fcurve.array_index to_fcurve: bpy.types.FCurve = to_channel.fcurves.new(data_path, index=index) @@ -4049,10 +3705,9 @@ def copy_fcurve_to_channel(from_fcurve: bpy.types.FCurve, to_channel) -> bpy.typ to_fcurve.extrapolation = from_fcurve.extrapolation to_fcurve.mute = from_fcurve.mute to_fcurve.lock = from_fcurve.lock - from_keyframes, num_points = get_fcurve_keyframe_data(from_fcurve) - apply_fcurve_keyframe_data(to_fcurve, from_keyframes) - to_fcurve.update() - return to_fcurve + return to_fcurve + else: + return None def refactor_to_slotted_action(objects, actions): @@ -4280,7 +3935,7 @@ def reset_fcurve_interpolation(fcurve: bpy.types.FCurve, interpolation="LINEAR") fcurve.update() -def shift_actions(action, to_frame, frame_start = 1): +def shift_action(action, to_frame, frame_start = 1): if to_frame == frame_start: return fcurves = utils.get_action_fcurves(action) @@ -4300,6 +3955,10 @@ def mix_actions(src_action, src_slot, dst_action, dst_slot, frame_start, frame_e src_channelbag = utils.get_action_channelbag(src_action, src_slot) dst_channelbag = utils.get_action_channelbag(dst_action, dst_slot) + #root_bone = "CC_Base_BoneRoot" + #src_root_transform_set = fetch_action_bone_transform_set(src_channelbag, root_bone) + #dst_root_transform_set = fetch_action_bone_transform_set(dst_channelbag, root_bone) + if not src_channelbag: utils.log_error(f"Mix Actions: No source channels!") return @@ -4433,9 +4092,40 @@ def target_slot(target): return None +def fetch_target_channels(rig, rig_action=None): + rig_slot = target_slot(rig) + if not rig_action: + rig_action = utils.safe_get_action(rig) + rig_channelbag = utils.get_action_channelbag(rig_action, slot=rig_slot) + rig_channel = (rig_action, rig_slot, rig_channelbag) + key_channels = {} + for child in rig.children: + if utils.object_exists_has_shape_keys(child): + key_slot = target_slot(child.data.shape_keys) + key_action = utils.safe_get_action(child.data.shape_keys) + key_channelbag = utils.get_action_channelbag(key_action, slot=key_slot) + key_channels[child.name] = (key_action, key_slot, key_channelbag) + data_slot = target_slot(rig.data) + if rig.type == "LIGHT" or rig.type == "CAMERA": + data_action = utils.safe_get_action(rig.data) + else: + data_action = None + data_channelbag = utils.get_action_channelbag(data_action, slot=data_slot) + data_channel = (data_action, data_slot, data_channelbag) + return rig_channel, key_channels, data_channel + + +def fetch_key_targets(rig): + key_targets = [] + for child in rig.children: + if utils.object_exists_has_shape_keys(child): + key_targets.append(child.name) + return key_targets + + def mix_motion_set(rig, action_store_id, frame_start, frame_end): props = vars.props() - stored_main_action = props.fetch_stored_rig_action(action_store_id) + stored_main_action, stored_main_slot = props.fetch_stored_rig_channel(action_store_id) done = False objects = [rig] if utils.object_exists_is_armature(rig): @@ -4497,10 +4187,20 @@ def mix_motion_set(rig, action_store_id, frame_start, frame_end): #delete_motion_set(set_id) +def shift_motion_frames(motion_action, to_frame): + if motion_action: + actions = get_motion_set_actions(motion_action) + action_range = get_actions_frame_range(actions) + utils.log_info(f"Moving motion frames: {motion_action.name} {action_range} to {to_frame}") + for action in actions: + shift_action(action, to_frame, action_range[0]) + + def add_slot_channels_to_rig_motion(rig, obj, slot_type, action, reuse=False, clear=False): prefs = vars.prefs() is_rig = utils.object_exists_is_armature(rig) + arm_id = utils.get_rl_id(rig) if prefs.use_action_slots(): @@ -4547,7 +4247,8 @@ def add_slot_channels_to_rig_motion(rig, obj, slot_type, action, reuse=False, cl action.use_fake_user = use_fake_user if set_id: - add_motion_set_data(action, set_id, set_generation, obj_id=obj_id, slotted=False) + add_motion_set_data(action, set_id, set_generation, + key_id=obj_id, arm_id=arm_id, slotted=False) # add slot and motion set data if slot_type == "OBJECT": @@ -4582,7 +4283,8 @@ def copy_channels_to_rig_motion(rig, obj, slot_type, src_action, src_slot, dst_a return dst_action, dst_slot -def finalize_rlx_import(obj, actions, action_store_id, action_mode, frame_start=None, frame_end=None): +def finalize_rlx_import(rlx_cache, obj, actions, action_store_id, frame_start=None, frame_end=None, overwrite=False): + """TODO can this be merged with finalize_motion_import""" props = vars.props() motion_action = actions[0] if actions else None @@ -4605,81 +4307,2209 @@ def finalize_rlx_import(obj, actions, action_store_id, action_mode, frame_start= if actions: stored_actions = props.fetch_stored_actions(action_store_id) + motion_rig_channel, motion_key_channels, motion_data_channel = fetch_target_channels(obj, motion_action) + key_targets = [] + stored_rig_channel = props.fetch_stored_rig_channel(action_store_id) + stored_key_channels = props.fetch_stored_key_channels(action_store_id) + stored_data_channel = props.fetch_stored_data_channel(action_store_id) + opts, blend_in_data, blend_out_data, use_masked = get_import_opts(rlx_cache) + if opts.relative_root or opts.current_root or opts.use_blend or use_masked or overwrite: + resample_blend(rlx_cache, obj, + motion_rig_channel, motion_key_channels, motion_data_channel, + stored_rig_channel, stored_key_channels, stored_data_channel, + key_targets, + frame_start, frame_end, + relative_root=opts.relative_root, + current_root=opts.current_root, + use_blend=opts.use_blend, + overwrite=overwrite, + blend_in_frames=opts.blend_in_frames, + blend_out_frames=opts.blend_out_frames, + blend_in_data=blend_in_data, + blend_out_data=blend_out_data, + overall_strength=opts.blend_strength) - # now decide what to do with the actions based on the action_mode and frame_mode + if overwrite: + stored_action = props.fetch_stored_rig_action(action_store_id) + # reload target (stored) motion set + load_motion_set(obj, stored_action) + # delete the source motion set + if motion_action and motion_action != stored_action: + delete_motion_set(motion_action) + utils.log_info(f"Re-Loaded target motion set: {stored_action.name}") + else: + # Do nothing else + utils.log_info(f"Loaded new motion set: {motion_action.name}") - if action_mode == "NEW": - # add new, do nothing else - utils.log_info(f"Action Mode NEW: Loaded new motion set: {motion_action.name}") - - elif not stored_actions: - # no existing actions to replace or blend, do nothing else - utils.log_info(f"No current motion: Loaded new motion set: {motion_action.name}") - - elif action_mode == "REPLACE": - # get the stored motion set data (and name) and apply it to the new motion_action - old_action = stored_actions[0] - utils.log_info(f"Action Mode REPLACE: updating motion set to: {old_action.name}") - stored_actions = props.fetch_stored_actions(action_store_id) - utils.delete_actions(stored_actions) - - elif action_mode == "BLEND": - old_action = stored_actions[0] - utils.log_info(f"Action Mode BLEND: blending motion set: {motion_action.name} over existing motion: {old_action.name}") - mix_motion_set(obj, action_store_id, frame_start, frame_end) - utils.delete_actions(actions) + #if action_mode == "BLEND": + # old_action = stored_actions[0] + # utils.log_info(f"Action Mode BLEND: blending motion set: {motion_action.name} over existing motion: {old_action.name}") + # mix_motion_set(obj, action_store_id, frame_start, frame_end) + # utils.delete_actions(actions) props.delete_action_store(action_store_id) -def finalize_motion_import(rig, motion_action, action_store_id, action_mode, frame_start=None, frame_end=None): - props = vars.props() +def get_import_opts(chr_rlx_cache): + blend_in = None + blend_out = None + opts = chr_rlx_cache.action_options if chr_rlx_cache else None + use_masked = opts and (opts.use_bone_masking or opts.use_key_masking) + if opts and opts.use_blend: + blend_in = ui.get_fcurve_data(chr_rlx_cache, "motion_blend_in", shape="IN") + blend_out = ui.get_fcurve_data(chr_rlx_cache, "motion_blend_out", shape="OUT") + return opts, blend_in, blend_out, use_masked - motion_start_frame, motion_end_frame = get_motion_set_frame_range(motion_action) + +def finalize_motion_import(chr_cache, rig, motion_rig_action, action_store_id, + frame_start=None, frame_end=None, overwrite=False): + props = vars.props() + motion_start_frame, motion_end_frame = get_motion_set_frame_range(motion_rig_action) if frame_start is None: frame_start = motion_start_frame if frame_end is None: frame_end = motion_end_frame utils.log_info("Finalize motion import:") - utils.log_info(f"motion action: {motion_action.name}") + utils.log_info(f"motion action: {motion_rig_action.name}") utils.log_info(f"start frame: {frame_start}") utils.log_info(f"end frame: {frame_end}") if frame_start is None or frame_end is None: - utils.log_error(f"Unable to fetch frame range from action!: {motion_action}") + utils.log_error(f"Unable to fetch frame range from action!: {motion_rig_action}") props.restore_actions(action_store_id) props.delete_action_store(action_store_id) return - if motion_action: - set_id = get_motion_set_ids(motion_action)[0] + if motion_rig_action: + set_id = get_motion_set_ids(motion_rig_action)[0] stored_actions = props.fetch_stored_actions(action_store_id) + motion_rig_channel, motion_key_channels, motion_data_channel = fetch_target_channels(rig, motion_rig_action) + key_targets = fetch_key_targets(rig) + stored_rig_channel = props.fetch_stored_rig_channel(action_store_id) + stored_key_channels = props.fetch_stored_key_channels(action_store_id) + stored_data_channel = props.fetch_stored_data_channel(action_store_id) + #masked_bones: list = [] + #mask_bones(rig, motion_rig_action, motion_rig_slot, masked_bones) + opts, blend_in_data, blend_out_data, use_masked = get_import_opts(chr_cache) + if opts.relative_root or opts.current_root or opts.use_blend or use_masked or overwrite: + resample_blend(chr_cache, rig, + motion_rig_channel, motion_key_channels, motion_data_channel, + stored_rig_channel, stored_key_channels, stored_data_channel, + key_targets, + frame_start, frame_end, + relative_root=opts.relative_root, + current_root=opts.current_root, + use_blend=opts.use_blend, + overwrite=overwrite, + blend_in_frames=opts.blend_in_frames, + blend_out_frames=opts.blend_out_frames, + blend_in_data=blend_in_data, + blend_out_data=blend_out_data, + overall_strength=opts.blend_strength) - # now decide what to do with the actions based on the action_mode and frame_mode - - if action_mode == "NEW": + if overwrite: + stored_rig_action = props.fetch_stored_rig_action(action_store_id) + # reload target (stored) motion set + load_motion_set(rig, stored_rig_action) + # delete the source motion set + if motion_rig_action and motion_rig_action != stored_rig_action: + delete_motion_set(motion_rig_action) + utils.log_info(f"Re-Loaded target motion set: {stored_rig_action.name}") + else: # already loaded, do nothing else - utils.log_info(f"Action Mode NEW: Loaded new motion set: {motion_action.name} / ({set_id})") + utils.log_info(f"Loaded new motion set: {motion_rig_action.name} / ({set_id})") - elif not stored_actions: - # already loaded, do nothing else - utils.log_info(f"No current motion: Loaded new motion set: {motion_action.name} / ({set_id})") - - elif action_mode == "REPLACE": - # get the stored motion set data (and name) and apply it to the new motion_action - old_action = stored_actions[0] - old_set_id = get_motion_set_ids(old_action)[0] - utils.log_info(f"Action Mode REPLACE: deleting old motion set: {old_action.name} / ({old_set_id})") - #replace_motion_set(set_id, old_set_id) - delete_motion_set(old_set_id) - - elif action_mode == "BLEND": - old_action = stored_actions[0] - old_set_id = get_motion_set_ids(old_action)[0] - utils.log_info(f"Action Mode BLEND: blending motion set: {motion_action.name} / ({set_id}) over existing motion: {old_action.name} / ({old_set_id})") - mix_motion_set(rig, action_store_id, frame_start, frame_end) - delete_motion_set(set_id) + #if action_mode == "BLEND": + # old_action = stored_actions[0] + # old_set_id = get_motion_set_ids(old_action)[0] + # utils.log_info(f"Action Mode BLEND: blending motion set: {motion_rig_action.name} / ({set_id}) over existing motion: {old_action.name} / ({old_set_id})") + # mix_motion_set(rig, action_store_id, frame_start, frame_end) + # delete_motion_set(set_id) props.delete_action_store(action_store_id) + +def resample_relative_root(rig, motion_action, motion_slot, stored_action, stored_slot, from_frame): + root_bone_name = rig.pose.bones[0].name + motion_channelbag = utils.get_action_channelbag(motion_action, slot=motion_slot) + stored_channelbag = utils.get_action_channelbag(stored_action, slot=stored_slot) + stored_transform_set, stored_has_curves = fetch_action_bone_transform_set(stored_channelbag, root_bone_name) + motion_transform_set, motion_has_curves = fetch_action_bone_transform_set(motion_channelbag, root_bone_name) + stored_start_loc, stored_start_rot = evaluate_action_bone_transform_set(stored_transform_set, from_frame) + motion_start_loc, motion_start_rot = evaluate_action_bone_transform_set(motion_transform_set, 1) + + utils.log_info(f"Resampling root motion: {motion_action.name}") + + # relative transformation between from_frame of stored motion and start of new motion + MS = utils.make_transform_matrix(stored_start_loc, stored_start_rot) + MM = utils.make_transform_matrix(motion_start_loc, motion_start_rot) + MD = MS @ MM.inverted() + + # build a list of motion root transform fcurves + motion_loc_curves, motion_rot_curves = motion_transform_set + motion_curves = motion_loc_curves + motion_rot_curves + # should be 7 + num_curves = len(motion_curves) + + # go through motion root transform fcurves and fetch the raw data, + # and build a list of discrete frames + frames = set() + fcurve: bpy.types.FCurve = None + for fcurve in motion_curves: + num_points = len(fcurve.keyframe_points) + data = [0.0, 0.0] * num_points + fcurve.keyframe_points.foreach_get('co', data) + for i in range(0, len(data), 2): + frames.add(data[i]) + + # sort the frames in order + num_frames = len(frames) + sorted_frames = list(frames) + sorted_frames.sort() + utils.log_info(f" - {num_frames} resampled frames") + + # generate resampled data lists for the curves in the motion root transform + resampled_data = [] + for i in range(0, num_curves): + data = [0.0, 0.0] * num_frames + resampled_data.append(data) + + # evaluate the motion root transform at each frame and + # transpose relative to the stored root transform + for f, frame in enumerate(sorted_frames): + motion_loc, motion_rot = evaluate_action_bone_transform_set(motion_transform_set, frame) + MF = utils.make_transform_matrix(motion_loc, motion_rot) + MR = MD @ MF + rel_loc = MR.to_translation() + rel_rot = MR.to_quaternion() + flat_transform = [rel_loc.x, rel_loc.y, rel_loc.z, + rel_rot.w, rel_rot.x, rel_rot.y, rel_rot.z] + # write to the resampled curve data + index = f * 2 + for i in range(0, num_curves): + resampled_data[i][index] = frame + resampled_data[i][index+1] = flat_transform[i] + + # write the resampled transposed curve data back to the motion root transform curves + for i, fcurve in enumerate(motion_curves): + fcurve.keyframe_points.clear() + fcurve.keyframe_points.add(num_frames) + fcurve.keyframe_points.foreach_set('co', resampled_data[i]) + reset_fcurve_interpolation(fcurve) + + return + + +def get_blend_mask_bones(chr_cache): + mask_bones = {} + use_blend = False + props = chr_cache.action_options + if props.use_bone_masking: + for item in props.import_bones: + bone_name = item.name + bone_weight = item.weight + mask_bones[bone_name] = bone_weight + if bone_weight < 1.0: + use_blend = True + return mask_bones, use_blend, props.use_bone_masking + + +def get_blend_mask_keys(chr_cache): + mask_keys = {} + use_blend = False + props = chr_cache.action_options + if props.use_key_masking: + for item in props.import_keys: + key_name = item.name + key_weight = item.weight + mask_keys[key_name] = key_weight + if key_weight < 1.0: + use_blend = True + return mask_keys, use_blend, props.use_key_masking + + +def get_shape_key_names(rig) -> set: + keys = set() + for obj in rig.children: + if utils.object_exists_has_shape_keys(obj): + for i, key in enumerate(obj.data.shape_keys.key_blocks): + if i > 0: + keys.add(key.name) + return keys + + +def remove_bone_from_channelbag(channelbag, bone_name): + path = f"pose.bones[\"{bone_name}\"]" + to_remove = [] + # TODO which Blender versions does this work in? + fcurves = channelbag.fcurves + for fcurve in fcurves: + if fcurve.data_path.startswith(path): + to_remove.append(fcurve) + for fcurve in to_remove: + fcurves.remove(fcurve) + + +def remove_key_from_channelbag(channelbag, key_name): + path = f"key_blocks[\"{key_name}\"]" + to_remove = [] + # TODO which Blender versions does this work in? + fcurves = channelbag.fcurves + for fcurve in fcurves: + if fcurve.data_path.startswith(path): + to_remove.append(fcurve) + for fcurve in to_remove: + fcurves.remove(fcurve) + + +def resample_blend(chr_rlx_cache, rig, + motion_rig_channel, motion_key_channels, motion_data_channel, + stored_rig_channel, stored_key_channels, stored_data_channel, + key_targets, + from_frame, to_frame, + relative_root=False, + current_root=False, + bone_names=None, + key_names=None, + data_paths=None, + use_blend=False, + overwrite=False, + blend_in_frames=0, + blend_out_frames=0, + blend_in_data=None, + blend_out_data=None, + overall_strength=1.0): + """Resamples the motion set, and blends with the stored motion set""" + + # clamp overall_strength + overall_strength = max(0, min(1, overall_strength)) + + blend_mask_bones = {} + use_mask_bones = False + blend_mask_keys = {} + use_mask_keys = False + blend_mask_data = {} + use_mask_data = False + + if chr_rlx_cache and rig.type == "ARMATURE": + blend_mask_bones, use_mask_bone_blend, use_mask_bones = get_blend_mask_bones(chr_rlx_cache) + blend_mask_keys, use_mask_key_blend, use_mask_keys = get_blend_mask_keys(chr_rlx_cache) + + if chr_rlx_cache and rig.type == "LIGHT": + ... + + if chr_rlx_cache and rig.type == "CAMERA": + ... + + overall_strength = overall_strength if use_blend else 1.0 + blend_in_frames = blend_in_frames if use_blend else 0 + blend_out_frames = blend_out_frames if use_blend else 0 + + frame_range = to_frame - from_frame + 1 + total_blend_frames = blend_in_frames + blend_out_frames + if total_blend_frames > frame_range: + blend_in_frames = round(frame_range * blend_in_frames / total_blend_frames) + blend_out_frames = frame_range - blend_in_frames + utils.log_warn(f"Blend range larger than motion. Rescaling to - in: {blend_in_frames}, out: {blend_out_frames}") + + if (not relative_root and + not current_root and + not use_blend and + not overwrite): + return + + utils.log_info(f"Resampling motion: {from_frame} - {to_frame}, blend in: {blend_in_frames}, blend out: {blend_out_frames}, use_blend: {use_blend}") + + processed_fcurves: List[bpy.types.FCurve] = [] + + # resample bone tracks + # + if rig.type == "ARMATURE": + + if not bone_names: + if use_blend or overwrite: + bone_names = [ b.name for b in rig.pose.bones ] + elif relative_root or current_root: + bone_names = [ rig.pose.bones[0].name ] + + blend_bone_curves(motion_rig_channel, stored_rig_channel, + from_frame, to_frame, + rig, bone_names, relative_root, current_root, + use_blend, overwrite, overall_strength, + blend_in_frames, blend_out_frames, + blend_in_data, blend_out_data, + use_mask_bones, blend_mask_bones, + processed_fcurves=processed_fcurves) + + blend_data_curves(motion_rig_channel, stored_rig_channel, + from_frame, to_frame, + use_blend, overwrite, overall_strength, + blend_in_frames, blend_out_frames, + blend_in_data, blend_out_data, + processed_fcurves=processed_fcurves) + + # resample shape key tracks + # + if rig.type == "ARMATURE": + + if not key_names: + key_names = get_shape_key_names(rig) if (use_blend or overwrite) else [] + + for obj_name in key_targets: + + motion_key_channel = motion_key_channels[obj_name] \ + if obj_name in motion_key_channels \ + else (None, None, None) + stored_key_channel = stored_key_channels[obj_name] \ + if obj_name in stored_key_channels \ + else (None, None, None) + + blend_key_curves(motion_key_channel, stored_key_channel, + from_frame, to_frame, + obj_name, key_names, + use_blend, overwrite, overall_strength, + blend_in_frames, blend_out_frames, + blend_in_data, blend_out_data, + use_mask_keys, blend_mask_keys, + processed_fcurves=processed_fcurves) + + # process data + # + if rig.type == "LIGHT" or rig.type == "CAMERA": + + blend_data_curves(motion_rig_channel, stored_rig_channel, + from_frame, to_frame, + use_blend, overwrite, overall_strength, + blend_in_frames, blend_out_frames, + blend_in_data, blend_out_data, + processed_fcurves=processed_fcurves) + + blend_data_curves(motion_data_channel, stored_data_channel, + from_frame, to_frame, + use_blend, overwrite, overall_strength, + blend_in_frames, blend_out_frames, + blend_in_data, blend_out_data, + processed_fcurves=processed_fcurves) + return + + +def blend_bone_curves(motion_channel, stored_channel, + from_frame, to_frame, + rig, bone_names, relative_root, current_root, + use_blend, overwrite, overall_strength, + blend_in_frames, blend_out_frames, + blend_in_data, blend_out_data, + use_mask_bones, blend_mask_bones, + processed_fcurves: List[bpy.types.FCurve]=None): + + motion_action, motion_slot, motion_channelbag = motion_channel + stored_action, stored_slot, stored_channelbag = stored_channel + + blend_in_frames = blend_in_frames if use_blend else 0 + blend_out_frames = blend_out_frames if use_blend else 0 + + base_frames = set() + # include all blend in / out frames + if blend_in_frames > 0: + for i in range(from_frame, from_frame + blend_in_frames - 1 + 1): + base_frames.add(i) + if blend_out_frames > 0: + for i in range(to_frame - blend_out_frames + 1, to_frame + 1): + base_frames.add(i) + + root_bone: bpy.types.PoseBone = rig.pose.bones[0] + + for bone_name in bone_names: + + is_root = (bone_name == root_bone.name) + motion_transform_set, motion_has_curves = fetch_action_bone_transform_set(motion_channelbag, bone_name) + stored_transform_set, stored_has_curves = fetch_action_bone_transform_set(stored_channelbag, bone_name) + + if not motion_has_curves and not stored_has_curves: + continue + + if is_root and relative_root: + # relative transformation between from_frame of stored motion and start of new motion + stored_start_loc, stored_start_rot = evaluate_action_bone_transform_set(stored_transform_set, from_frame) + motion_start_loc, motion_start_rot = evaluate_action_bone_transform_set(motion_transform_set, 1) + MS = utils.make_transform_matrix(stored_start_loc, stored_start_rot) + MM = utils.make_transform_matrix(motion_start_loc, motion_start_rot) + MD = MS @ MM.inverted() + elif is_root and current_root: + # relative transformation between current rig root bone position and start of new motion + motion_start_loc, motion_start_rot = evaluate_action_bone_transform_set(motion_transform_set, 1) + MS = root_bone.matrix.copy() + MM = utils.make_transform_matrix(motion_start_loc, motion_start_rot) + MD = MS @ MM.inverted() + + + utils.log_detail(f"Blending motion bone: {bone_name} {from_frame}-{to_frame}") + + # build a list of motion bone transform fcurves + motion_loc_curves, motion_rot_curves = motion_transform_set + motion_curves = motion_loc_curves + motion_rot_curves + # should be 7 + num_curves = len(motion_curves) + + # build a list of stored bone transform fcurves + stored_loc_curves, stored_rot_curves = stored_transform_set + stored_curves = stored_loc_curves + stored_rot_curves + + frames = base_frames.copy() + + # include all frames from the motion curve + fcurve: bpy.types.FCurve = None + for fcurve in motion_curves: + if fcurve: + num_points = len(fcurve.keyframe_points) + data = [0.0, 0.0] * num_points + fcurve.keyframe_points.foreach_get('co', data) + for i in range(0, len(data), 2): + frame = data[i] + frames.add(frame) + + # include all frames from the source curve + if use_blend or overwrite: + for fcurve in stored_curves: + if fcurve: + num_points = len(fcurve.keyframe_points) + data = [0.0, 0.0] * num_points + fcurve.keyframe_points.foreach_get('co', data) + for i in range(0, len(data), 2): + frame = data[i] + frames.add(frame) + + # sort the frames in order + num_frames = len(frames) + sorted_frames = list(frames) + sorted_frames.sort() + utils.log_detail(f" - {num_frames} resampled frames") + + # generate resampled data lists for the curves in the motion bone transform + resampled_data = [] + for i in range(0, num_curves): + data = [0.0, 0.0] * num_frames + resampled_data.append(data) + + # evaluate the motion root transform at each frame ... + for f, frame in enumerate(sorted_frames): + motion_loc, motion_rot = evaluate_action_bone_transform_set(motion_transform_set, frame) + + # apply relative root: + if is_root and (relative_root or current_root): + motion_loc, motion_rot = evaluate_action_bone_transform_set(motion_transform_set, frame) + MF = utils.make_transform_matrix(motion_loc, motion_rot) + MR = MD @ MF + motion_loc = MR.to_translation() + motion_rot = MR.to_quaternion() + + # if blending, apply motion blend: + if use_blend or overwrite: + blend_strength = overall_strength if use_blend else 1.0 + stored_loc, stored_rot = evaluate_action_bone_transform_set(stored_transform_set, frame) + if frame < from_frame or frame > to_frame: + blend_strength = 0 + elif blend_in_frames > 0 and frame < (from_frame + blend_in_frames): + pos = (frame - from_frame + 1) / (blend_in_frames + 1) + blend_strength *= ui.eval_curve(blend_in_data, pos) + elif blend_out_frames > 0 and frame > (to_frame - blend_out_frames): + pos = (frame - to_frame + blend_out_frames) / (blend_out_frames + 1) + blend_strength *= ui.eval_curve(blend_out_data, pos) + if use_mask_bones: + if bone_name in blend_mask_bones: + blend_strength *= blend_mask_bones[bone_name] + else: + blend_strength = 0.0 + + if blend_strength <= 0: + blend_loc = stored_loc + blend_rot = stored_rot + elif blend_strength >= 1: + blend_loc = motion_loc + blend_rot = motion_rot + else: + blend_loc = stored_loc.lerp(motion_loc, blend_strength) + blend_rot = stored_rot.slerp(motion_rot, blend_strength) + + # if not blending, use the motion transform directly + else: + blend_loc = motion_loc + blend_rot = motion_rot + + transform_data = [blend_loc.x, blend_loc.y, blend_loc.z, + blend_rot.w, blend_rot.x, blend_rot.y, blend_rot.z] + + # write frame to the resampled curve data: + index = f * 2 + for i in range(0, num_curves): + resampled_data[i][index] = frame + resampled_data[i][index+1] = transform_data[i] + + if overwrite: + # write the resampled curve data back to the stored transform curves: + for i, fcurve in enumerate(stored_curves): + if processed_fcurves: + if fcurve and fcurve in processed_fcurves: continue + processed_fcurves.append(fcurve) + if not fcurve: + fcurve = clone_fcurve_to_channel(motion_curves[i], stored_channelbag) + if fcurve: + utils.log_detail(f"Added bone fcurve {bone_name}[{i}]") + if fcurve: + fcurve.keyframe_points.clear() + fcurve.keyframe_points.add(num_frames) + fcurve.keyframe_points.foreach_set('co', resampled_data[i]) + reset_fcurve_interpolation(fcurve) + else: + # write the resampled curve data back to the motion transform curves: + for i, fcurve in enumerate(motion_curves): + if processed_fcurves: + if fcurve and fcurve in processed_fcurves: continue + processed_fcurves.append(fcurve) + if not fcurve: + fcurve = clone_fcurve_to_channel(stored_curves[i], motion_channelbag) + if fcurve: + utils.log_detail(f"Added bone fcurve {bone_name}[{i}]") + if fcurve: + fcurve.keyframe_points.clear() + fcurve.keyframe_points.add(num_frames) + fcurve.keyframe_points.foreach_set('co', resampled_data[i]) + reset_fcurve_interpolation(fcurve) + + +def blend_key_curves(motion_channel, stored_channel, + from_frame, to_frame, + obj_name, key_names, + use_blend, overwrite, overall_strength, + blend_in_frames, blend_out_frames, + blend_in_data, blend_out_data, + use_mask_keys, blend_mask_keys, + processed_fcurves: List[bpy.types.FCurve]=None): + + motion_action, motion_slot, motion_channelbag = motion_channel + stored_action, stored_slot, stored_channelbag = stored_channel + + blend_in_frames = blend_in_frames if use_blend else 0 + blend_out_frames = blend_out_frames if use_blend else 0 + + base_frames = set() + # include all blend in / out frames + if blend_in_frames > 0: + for i in range(from_frame, from_frame + blend_in_frames - 1 + 1): + base_frames.add(i) + if blend_out_frames > 0: + for i in range(to_frame - blend_out_frames + 1, to_frame + 1): + base_frames.add(i) + + for key_name in key_names: + + obj = bpy.data.objects[obj_name] + if not utils.object_has_shape_key(obj, key_name): + continue + + stored_key_curve = fetch_action_key_curve(stored_channelbag, key_name) + motion_key_curve = fetch_action_key_curve(motion_channelbag, key_name) + + if processed_fcurves: + if motion_key_curve and motion_key_curve in processed_fcurves: continue + processed_fcurves.append(motion_key_curve) + + if not stored_key_curve and not motion_key_curve: + continue + + utils.log_detail(f"Blending motion key: {key_name} {from_frame}-{to_frame}") + + # go through motion bone transform fcurves and build a list of discrete frames + frames = base_frames.copy() + fcurve: bpy.types.FCurve = None + + # add frames from the motion curve + if motion_key_curve: + num_points = len(motion_key_curve.keyframe_points) + data = [0.0, 0.0] * num_points + motion_key_curve.keyframe_points.foreach_get('co', data) + for i in range(0, len(data), 2): + frame = data[i] + frames.add(frame) + + # also include frames from the source curve + if use_blend or overwrite: + if stored_key_curve: + num_points = len(stored_key_curve.keyframe_points) + data = [0.0, 0.0] * num_points + stored_key_curve.keyframe_points.foreach_get('co', data) + for i in range(0, len(data), 2): + frame = data[i] + frames.add(frame) + + # sort the frames in order + num_frames = len(frames) + sorted_frames = list(frames) + sorted_frames.sort() + utils.log_detail(f" - {num_frames} resampled frames") + + # generate resampled data lists for the motion curve + resampled_data = [0.0, 0.0] * num_frames + + # evaluate the motion curve at each frame ... + for f, frame in enumerate(sorted_frames): + motion_value = evaluate_action_curve(motion_key_curve, frame) + + # if blending, apply motion blend: + if use_blend or overwrite: + blend_strength = overall_strength if use_blend else 1.0 + stored_value = evaluate_action_curve(stored_key_curve, frame) + + if frame < from_frame or frame > to_frame: + blend_strength = 0 + elif blend_in_frames > 0 and frame < (from_frame + blend_in_frames): + pos = (frame - from_frame + 1) / (blend_in_frames + 1) + blend_strength *= ui.eval_curve(blend_in_data, pos) + elif blend_out_frames > 0 and frame > (to_frame - blend_out_frames): + pos = (frame - to_frame + blend_out_frames) / (blend_out_frames + 1) + blend_strength *= ui.eval_curve(blend_out_data, pos) + if use_mask_keys: + if key_name in blend_mask_keys: + blend_strength *= blend_mask_keys[key_name] + else: + blend_strength = 0.0 + + if blend_strength <= 0: + blend_value = stored_value + elif blend_strength >= 1: + blend_value = motion_value + else: + blend_value = utils.lerp(stored_value, motion_value, blend_strength) + + # otherwise use motion value directly + else: + blend_value = motion_value + + # write frame to the resampled curve data: + index = f * 2 + resampled_data[index] = frame + resampled_data[index+1] = blend_value + + if overwrite: + # write the resampled curve data back to the stored curve: + if not stored_key_curve: + stored_key_curve = clone_fcurve_to_channel(motion_key_curve, stored_channelbag) + if stored_key_curve: + utils.log_detail(f"Added key fcurve {obj_name}/{key_name}") + if stored_key_curve: + stored_key_curve.keyframe_points.clear() + stored_key_curve.keyframe_points.add(num_frames) + stored_key_curve.keyframe_points.foreach_set('co', resampled_data) + reset_fcurve_interpolation(stored_key_curve) + else: + # write the resampled curve data back to the motion curve: + if not motion_key_curve: + motion_key_curve = clone_fcurve_to_channel(stored_key_curve, motion_channelbag) + if motion_key_curve: + utils.log_detail(f"Added key fcurve {obj_name}/{key_name}") + if motion_key_curve: + motion_key_curve.keyframe_points.clear() + motion_key_curve.keyframe_points.add(num_frames) + motion_key_curve.keyframe_points.foreach_set('co', resampled_data) + reset_fcurve_interpolation(motion_key_curve) + + +def blend_data_curves(motion_channel, stored_channel, + from_frame, to_frame, + use_blend, overwrite, overall_strength, + blend_in_frames, blend_out_frames, + blend_in_data, blend_out_data, + processed_fcurves: List[bpy.types.FCurve]=None): + + motion_action, motion_slot, motion_channelbag = motion_channel + stored_action, stored_slot, stored_channelbag = stored_channel + + blend_in_frames = blend_in_frames if use_blend else 0 + blend_out_frames = blend_out_frames if use_blend else 0 + + base_frames = set() + # include all blend in / out frames + if blend_in_frames > 0: + for i in range(from_frame, from_frame + blend_in_frames - 1 + 1): + base_frames.add(i) + if blend_out_frames > 0: + for i in range(to_frame - blend_out_frames + 1, to_frame + 1): + base_frames.add(i) + + paths = {} + if motion_channelbag: + for fcurve in motion_channelbag.fcurves: + if (fcurve.data_path.startswith("pose.bones") or + fcurve.data_path.startswith("key_blocks")): + continue + path_id = (fcurve.data_path, fcurve.array_index) + paths[path_id] = [fcurve, None] + if stored_channelbag: + for fcurve in stored_channelbag.fcurves: + if (fcurve.data_path.startswith("pose.bones") or + fcurve.data_path.startswith("key_blocks")): + continue + path_id = (fcurve.data_path, fcurve.array_index) + if path_id in paths: + paths[path_id][1] = fcurve + else: + paths[path_id] = [None, fcurve] + + for path_id, curves in paths.items(): + data_path, array_index = path_id + motion_data_curve = curves[0] + stored_data_curve = curves[1] + + if processed_fcurves: + if motion_data_curve and motion_data_curve in processed_fcurves: continue + processed_fcurves.append(motion_data_curve) + + utils.log_info(f"Blending motion data: {data_path} {from_frame}-{to_frame}") + + # go through motion bone transform fcurves and build a list of discrete frames + frames = base_frames.copy() + fcurve: bpy.types.FCurve = None + + # add frames from the motion curve + if motion_data_curve: + num_points = len(motion_data_curve.keyframe_points) + data = [0.0, 0.0] * num_points + motion_data_curve.keyframe_points.foreach_get('co', data) + for i in range(0, len(data), 2): + frame = data[i] + frames.add(frame) + + # also include frames from the source curve + if use_blend or overwrite: + if stored_data_curve: + num_points = len(stored_data_curve.keyframe_points) + data = [0.0, 0.0] * num_points + stored_data_curve.keyframe_points.foreach_get('co', data) + for i in range(0, len(data), 2): + frame = data[i] + frames.add(frame) + + # sort the frames in order + num_frames = len(frames) + sorted_frames = list(frames) + sorted_frames.sort() + utils.log_detail(f" - {num_frames} resampled frames") + + # generate resampled data lists for the motion curve + resampled_data = [0.0, 0.0] * num_frames + + # evaluate the motion curve at each frame ... + for f, frame in enumerate(sorted_frames): + motion_value = evaluate_action_curve(motion_data_curve, frame) + + # if blending, apply motion blend: + if use_blend or overwrite: + blend_strength = overall_strength if use_blend else 1.0 + stored_value = evaluate_action_curve(stored_data_curve, frame) + + if frame < from_frame or frame > to_frame: + blend_strength = 0 + elif blend_in_frames > 0 and frame < (from_frame + blend_in_frames): + pos = (frame - from_frame + 1) / (blend_in_frames + 1) + blend_strength *= ui.eval_curve(blend_in_data, pos) + elif blend_out_frames > 0 and frame > (to_frame - blend_out_frames): + pos = (frame - to_frame + blend_out_frames) / (blend_out_frames + 1) + blend_strength *= ui.eval_curve(blend_out_data, pos) + #if use_mask_paths: + # if data_path in blend_mask_paths: + # blend_strength *= blend_mask_paths[data_path] + # else: + # blend_strength = 0.0 + + if blend_strength <= 0: + blend_value = stored_value + elif blend_strength >= 1: + blend_value = motion_value + else: + blend_value = utils.lerp(stored_value, motion_value, blend_strength) + + # otherwise use motion value directly + else: + blend_value = motion_value + + # write frame to the resampled curve data: + index = f * 2 + resampled_data[index] = frame + resampled_data[index+1] = blend_value + + if overwrite: + # write the resampled curve data back to the stored curve: + if not stored_data_curve: + stored_data_curve = clone_fcurve_to_channel(motion_data_curve, stored_channelbag) + if stored_data_curve: + utils.log_detail(f"Added data fcurve {path_id}") + if stored_data_curve: + stored_data_curve.keyframe_points.clear() + stored_data_curve.keyframe_points.add(num_frames) + stored_data_curve.keyframe_points.foreach_set('co', resampled_data) + reset_fcurve_interpolation(stored_data_curve) + else: + # write the resampled curve data back to the motion curve: + if not motion_data_curve: + motion_data_curve = clone_fcurve_to_channel(stored_data_curve, motion_channelbag) + if motion_data_curve: + utils.log_detail(f"Added data fcurve {path_id}") + if motion_data_curve: + motion_data_curve.keyframe_points.clear() + motion_data_curve.keyframe_points.add(num_frames) + motion_data_curve.keyframe_points.foreach_set('co', resampled_data) + reset_fcurve_interpolation(motion_data_curve) + + +def get_relative_transformation(motion_loc: Vector, motion_rot: Quaternion, + stored_loc: Vector, stored_rot: Quaternion): + delta_loc = stored_loc - motion_loc + M: Matrix = motion_rot.to_matrix().to_4x4() + S: Matrix = stored_rot.to_matrix().to_4x4() + D = M.inverted() @ S + return delta_loc, D + + +def fetch_action_key_curve(channelbag, key_name: str): + if channelbag and key_name: + key_path = f"key_blocks[\"{key_name}\"].value" + for fcurve in channelbag.fcurves: + if fcurve.data_path == key_path: + return fcurve + return None + + +def fetch_action_bone_transform_set(channelbag, bone_name: str): + # TODO instead build a dict in one pass of transforms[bone_name] = (loc[fx,fy,fz], rot[fw,fx,fy,fz]) + fcurve: bpy.types.FCurve = None + loc_path = f"pose.bones[\"{bone_name}\"].location" + rot_path = f"pose.bones[\"{bone_name}\"].rotation_quaternion" + # TODO what if it's not a quaternion? + loc_curves = [None, None, None] + rot_curves = [None, None, None, None] + has_loc_curves = False + has_rot_curves = False + if channelbag: + for fcurve in channelbag.fcurves: + if fcurve.data_path == loc_path: + loc_curves[fcurve.array_index] = fcurve + has_loc_curves = True + elif fcurve.data_path == rot_path: + rot_curves[fcurve.array_index] = fcurve + has_rot_curves = True + return (loc_curves, rot_curves), has_loc_curves or has_rot_curves + + +def evaluate_action_bone_transform_set(transform_set: tuple, frame: int): + loc_curves, rot_curves = transform_set + try: + loc = Vector((loc_curves[0].evaluate(frame), + loc_curves[1].evaluate(frame), + loc_curves[2].evaluate(frame))) + except: + loc = Vector((0,0,0)) + try: + rot = Quaternion((rot_curves[0].evaluate(frame), + rot_curves[1].evaluate(frame), + rot_curves[2].evaluate(frame), + rot_curves[3].evaluate(frame))) + except: + rot = Quaternion((1,0,0,0)) + return loc, rot + + +def evaluate_action_curve(key_curve: tuple, frame: int): + try: + value = key_curve.evaluate(frame) + except: + value = 0.0 + return value +#endregion + + +#region Operators +class CCICMotionSetFunctions(bpy.types.Operator): + bl_idname = "ccic.motion_set_funcs" + bl_label = "Motion Set Functions" + bl_options = {"REGISTER", "UNDO", "INTERNAL"} + + param: bpy.props.StringProperty( + name = "param", + default = "" + ) + + def execute(self, context): + props = vars.props() + + props.store_ui_list_indices() + action = props.action_set_list_action + #chr_cache = props.get_context_character_cache(context) + + if self.param == "DELETE": + delete_motion_set(action) + + elif self.param == "DUPLICATE": + duplicate_motion_set(action) + + return {"FINISHED"} + + @classmethod + def description(cls, context, properties): + return "Show motion set info" + + +class CCICMotionSetRename(bpy.types.Operator): + bl_idname = "ccic.motion_set_rename" + bl_label = "Rename Motion Set" + + action: bpy.props.StringProperty(name="Action", default="") + prefix: bpy.props.StringProperty(name="Motion Prefix", default="") + rig_id: bpy.props.StringProperty(name="Character / Rig ID", default="") + motion_id: bpy.props.StringProperty(name="Motion Name / ID", default="") + set_id = bpy.props.StringProperty(name="Set ID", default="") + action_type = bpy.props.StringProperty(name="Action Type", default="") + + def execute(self, context): + rename_motion_set(self.action_type, self.set_id, self.prefix, self.rig_id, self.motion_id) + return {"FINISHED"} + + def invoke(self, context, event): + props = vars.props() + prefs = vars.prefs() + + props.store_ui_list_indices() + action = props.action_set_list_action + self.action = action.name + chr_cache = props.get_context_character_cache(context) + + if not action: + return {"FINISHED"} + + set_id, set_generation, action_type_id, key_object = get_motion_set_ids(action) + self.action_type = action_type_id + + if not set_id: + return {"FINISHED"} + + self.set_id = set_id + + prefix, rig_id, type_id, obj_id, motion_id = decode_action_name(action) + if prefix: + self.prefix = prefix + else: + self.prefix = "" + if rig_id: + self.rig_id = rig_id + else: + self.rig_id = "" + if motion_id: + self.motion_id = motion_id + elif action.name: + self.motion_id = action.name.split("|")[-1] + else: + self.motion_id = "Motion" + + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context): + layout = self.layout + + split = layout.split(factor=0.35) + col_1 = split.column() + col_2 = split.column() + + col_1.label(text="Motion Set ID:") + col_2.label(text=self.set_id) + + col_1.separator() + col_2.separator() + + col_1.label(text="Prefix:") + col_2.prop(self, "prefix", text="") + + col_1.separator() + col_2.separator() + + col_1.label(text="Character / Rig ID:") + col_2.prop(self, "rig_id", text="") + + col_1.separator() + col_2.separator() + + col_1.label(text="Motion Name / ID:") + col_2.prop(self, "motion_id", text="") + + layout.separator() + + @classmethod + def description(cls, context, properties): + return "Change the name, prefix, and character/rig id of the motion set" + + +class CCICMotionSetInfo(bpy.types.Operator): + bl_idname = "ccic.motion_set_info" + bl_label = "Motion Set Info" + + prefix: bpy.props.StringProperty(name="Motion Prefix", default="") + rig_id: bpy.props.StringProperty(name="Character / Rig ID", default="") + motion_id: bpy.props.StringProperty(name="Motion Name / ID", default="") + set_id: bpy.props.StringProperty(name="Set ID", default="") + + def execute(self, context): + return {"FINISHED"} + + def invoke(self, context, event): + props = vars.props() + prefs = vars.prefs() + + props.store_ui_list_indices() + action = props.action_set_list_action + chr_cache = props.get_context_character_cache(context) + + self.delete_me = False + # TODO make delete an op button? + # TODO use_fake_user button + + if not action: + return {"FINISHED"} + + set_id, set_generation, action_type_id, key_object = get_motion_set_ids(action) + + if not set_id: + return {"FINISHED"} + + self.set_id = set_id + + prefix, rig_id, type_id, obj_id, motion_id = decode_action_name(action) + if prefix: + self.prefix = prefix + if rig_id: + self.rig_id = rig_id + elif chr_cache: + self.rig_id = chr_cache.get_name() + else: + self.rig_id = "Rig" + if motion_id: + self.motion_id = motion_id + elif action.name: + self.motion_id = action.name.split("|")[-1] + else: + self.motion_id = "Motion" + + return context.window_manager.invoke_popup(self, width=600) + + def draw(self, context): + layout = self.layout + + split = layout.split(factor=0.25) + col_1 = split.column() + col_2 = split.column() + + col_1.label(text="Motion Set ID:") + col_2.label(text=self.set_id) + + col_1.separator() + col_2.separator() + + col_1.label(text="Prefix:") + col_2.label(text=self.prefix if self.prefix else "(None)") + col_1.label(text="Character / Rig ID:") + col_2.label(text=self.rig_id if self.rig_id else "(None)") + col_1.label(text="Motion Name / ID:") + col_2.label(text=self.motion_id if self.motion_id else "(None)") + + layout.separator() + + layout.label(text="Actions:") + + split = layout.split(factor=0.25) + col_1 = split.column() + col_2 = split.column() + for action in bpy.data.actions: + action_set_id = utils.get_prop(action, "rl_set_id") + action_type = utils.get_prop(action, "rl_action_type") + if action_set_id == self.set_id: + if action_type == "ARM": + col_1.label(text="Armature") + elif action_type == "KEY": + obj_id = utils.get_prop(action, "rl_key_object", "(None)") + col_1.label(text=obj_id) + else: + col_1.label(text="?") + col_2.label(text=action.name) + col_1.separator() + col_2.separator() + col_1.separator() + row = col_2.split(factor=0.5).column().row() + row.alert = True + row.scale_y = 1.5 + row.operator("ccic.rigutils", text="Delete Motion Set", icon="ERROR").param = "DELETE_MOTION_SET" + layout.separator() + layout.separator() + layout.separator() + + @classmethod + def description(cls, context, properties): + return "Show motion set info" + + +class CCICRigUtils(bpy.types.Operator): + """Rig Utilities""" + bl_idname = "ccic.rigutils" + bl_label = "Rig Utils" + bl_options = {"REGISTER"} + + param: bpy.props.StringProperty( + name = "param", + default = "", + options={"HIDDEN"} + ) + + def execute(self, context): + props = vars.props() + prefs = vars.prefs() + + act_cache = props.get_context_actor_cache(context) + rlob = act_cache.get_primary_object() if act_cache else None + + if act_cache: + + props.store_ui_list_indices() + + if rlob: + if rlob.type == "ARMATURE": + if self.param == "TOGGLE_SHOW_FULL_RIG": + toggle_show_full_rig(rlob) + + elif self.param == "TOGGLE_SHOW_BASE_RIG": + toggle_show_base_rig(rlob) + + elif self.param == "TOGGLE_SHOW_SPRING_RIG": + toggle_show_spring_rig(rlob) + + elif self.param == "TOGGLE_SHOW_RIG_POSE": + toggle_rig_rest_position(rlob) + + elif self.param == "TOGGLE_SHOW_SPRING_BONES": + springbones.toggle_show_spring_bones(act_cache) + + elif self.param == "TOGGLE_EXPRESSION_RIG_LOCK": + facerig.toggle_lock_position(act_cache, rlob) + + elif self.param == "BUTTON_RESET_POSE_SELECTED": + mode_selection = utils.store_mode_selection_state() + reset_pose(rlob, use_selected=True) + utils.restore_mode_selection_state(mode_selection) + + elif self.param == "BUTTON_RESET_POSE": + mode_selection = utils.store_mode_selection_state() + reset_pose(rlob, use_selected=False) + utils.restore_mode_selection_state(mode_selection) + + elif self.param == "RESET_EXPRESSION_POSE": + if act_cache.rigified: + mode_selection = utils.store_mode_selection_state() + facerig.clear_expression_pose(act_cache, rlob) + utils.restore_mode_selection_state(mode_selection) + + elif self.param == "RESET_EXPRESSION_POSE_SELECTED": + if act_cache.rigified: + mode_selection = utils.store_mode_selection_state() + facerig.clear_expression_pose(act_cache, rlob, selected=True) + utils.restore_mode_selection_state(mode_selection) + + elif self.param == "TOGGLE_SHOW_FACE_RIG": + toggle_show_only_face_rig(rlob) + + elif self.param == "SET_LIMB_FK": + if act_cache.rigified: + set_rigify_ik_fk_influence(rlob, 1.0) + poke_rig(rlob) + + elif self.param == "SET_LIMB_IK": + if act_cache.rigified: + set_rigify_ik_fk_influence(rlob, 0.0) + poke_rig(rlob) + + elif self.param == "DISABLE_CONSTRAINT_STRETCH": + mode_selection = utils.store_mode_selection_state() + rigify_rig = act_cache.get_armature() + disable_ik_stretch(rigify_rig) + utils.restore_mode_selection_state(mode_selection) + + elif self.param == "ENABLE_CONSTRAINT_STRETCH": + mode_selection = utils.store_mode_selection_state() + rigify_rig = act_cache.get_armature() + restore_ik_stretch(rigify_rig=rigify_rig) + utils.restore_mode_selection_state(mode_selection) + + if self.param == "LOAD_MOTION_SET": + action = props.action_set_list_action + load_motion_set(rlob, action) + + elif self.param == "PUSH_ACTION_SET": + action = props.action_set_list_action + auto_index = act_cache.get_auto_index() + push_motion_set(rlob, action, auto_index) + + elif self.param == "CLEAR_ACTION_SET": + clear_motion_set(rlob) + + elif self.param == "NEW_ACTION_SET": + new_motion_set(rlob) + + if self.param == "SELECT_SET_STRIPS": + strip = context.active_nla_strip + select_strips_by_set(strip) + + elif self.param == "NLA_ALIGN_LEFT": + strips = context.selected_nla_strips + align_strips(strips, left=True) + + elif self.param == "NLA_ALIGN_TO_LEFT": + strips = context.selected_nla_strips + active_strip = context.active_nla_strip + align_strips(strips, to_strip=active_strip, left=True) + + elif self.param == "NLA_ALIGN_RIGHT": + strips = context.selected_nla_strips + align_strips(strips, left=False) + + elif self.param == "NLA_ALIGN_TO_RIGHT": + strips = context.selected_nla_strips + active_strip = context.active_nla_strip + align_strips(strips, to_strip=active_strip, left=False) + + elif self.param == "NLA_SIZE_SHORTEST": + strips = context.selected_nla_strips + size_strips(strips, longest=False) + + elif self.param == "NLA_SIZE_LONGEST": + strips = context.selected_nla_strips + size_strips(strips, longest=True) + + elif self.param == "NLA_SIZE_TO": + strips = context.selected_nla_strips + active_strip = context.active_nla_strip + size_strips(strips, to_strip=active_strip) + + elif self.param == "NLA_RESET_SIZE": + strips = context.selected_nla_strips + size_strips(strips, reset=True) + + elif self.param == "SET_FAKE_USER_ON": + action = props.action_set_list_action + set_action_set_fake_user(action, True) + + elif self.param == "SET_FAKE_USER_OFF": + action = props.action_set_list_action + set_action_set_fake_user(action, False) + + elif self.param == "DELETE_MOTION_SET": + action = props.action_set_list_action + delete_motion_set(action) + + props.restore_ui_list_indices() + + if self.param == "CLEAN_ACTIONS": + clean_actions(rlob) + + return {"FINISHED"} + + @classmethod + def description(cls, context, properties): + + if properties.param == "TOGGLE_SHOW_SPRING_BONES": + return "Quick toggle for the armature layers to show just the spring bones or just the body bones" + + elif properties.param == "TOGGLE_SHOW_FULL_RIG": + return "Toggles showing all the rig controls" + + elif properties.param == "TOGGLE_SHOW_BASE_RIG": + return "Toggles showing the base rig controls" + + elif properties.param == "TOGGLE_SHOW_SPRING_RIG": + return "Toggles showing just the spring rig controls" + + elif properties.param == "TOGGLE_SHOW_RIG_POSE": + return "Toggles the rig between pose mode and rest pose" + + elif properties.param == "TOGGLE_SHOW_FACE_RIG": + return "Toggles showing just the face expression rig controls" + + elif properties.param == "TOGGLE_EXPRESSION_RIG_LOCK": + return "Toggle locking the position of the expression rig and making unselectable" + + elif properties.param == "BUTTON_RESET_POSE": + return "Clears all pose transforms" + + elif properties.param == "BUTTON_RESET_POSE_SELECTED": + return "Clears the pose on all selected bones" + + elif properties.param == "RESET_EXPRESSION_POSE": + return "Clears the expression on all expression controls" + + elif properties.param == "RESET_EXPRESSION_POSE_SELECTED": + return "Clears the pose on all selected expression rig bones" + + elif properties.param == "LOAD_MOTION_SET": + return "Loads the chosen motion set (armature and shape key actions) into the all the character objects" + + elif properties.param == "PUSH_ACTION_SET": + return "Pushes the chosen motion set (armature and shape key actions) into the NLA tracks of all the character objects at the current frame. " \ + "A suitable track will be chosen to fit the actions. If there is no room available a new track will be added to contain the actions" + + elif properties.param == "CLEAR_ACTION_SET": + return "Removes all actions from the character" + + elif properties.param == "SELECT_SET_STRIPS": + return "Selects all the strips belonging to the same motion set and strip index" + + elif properties.param == "NLA_ALIGN_LEFT": + return "Aligns all selected strips to the left most of frame of all selected strips" + + elif properties.param == "NLA_ALIGN_RIGHT": + return "Aligns all selected strips to right most of frame of all selected strips" + + elif properties.param == "NLA_ALIGN_TO_LEFT": + return "Aligns all selected strips to the left hand frame of the active strip" + + elif properties.param == "NLA_ALIGN_TO_RIGHT": + return "Aligns all selected strips to the right hand frame of the active strip" + + elif properties.param == "NLA_SIZE_SHORTEST": + return "Sets the frame lengths of all selected strips to the length of the shortest strip in the selection" + + elif properties.param == "NLA_SIZE_TO": + return "Sets the frame lengths of all selected strips to the length of the active strip" + + elif properties.param == "NLA_SIZE_LONGEST": + return "Sets the frame lengths of all selected strips to the length of the longest strip in the selection" + + elif properties.param == "NLA_RESET_SIZE": + return "Resets the frame lengths of all selected strips to the length of the underlying action" + + elif properties.param == "SET_FAKE_USER_ON": + return "Set fake user on all actions in the motion set" + + elif properties.param == "SET_FAKE_USER_OFF": + return "Clear fake user on all actions in the motion set" + + elif properties.param == "DELETE_MOTION_SET": + return "Delete all actions in the motion set" + + elif properties.param == "DISABLE_CONSTRAINT_STRETCH": + return "Disable stretch in all IK mechanisms on the rig. By default the Blender Rigify rig allows a certain amount of stretch in the bones ease IK alignment.\n" \ + "But in other applications, this bone stretch is not possible, disabling the IK stretch system can aid with animation alignment problems" + + elif properties.param == "ENABLE_CONSTRAINT_STRETCH": + return "Re-enable the IK stretch mechanisms in the rig" + + return "" + + +class CCIC_IMPORTMIX_UL_List(bpy.types.UIList): + + visible_bone_indices = [] + visible_key_indices = [] + + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.label(text=item.name if item else "", translate=False, icon_value=icon) + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + def filter_items(self, context, data, propname): + filtered = [] + ordered = [] + items = getattr(data, propname) + visible_indices = self.visible_bone_indices if propname == "import_bones" else self.visible_key_indices + visible_indices.clear() + filtered = [self.bitflag_filter_item] * len(items) + LFN = self.filter_name.lower() + for i, item in enumerate(items): + allowed = True + # filter by name + if LFN and LFN != "*": + if LFN not in item.name.lower(): + allowed = False + # block not allowed + if not allowed: + filtered[i] &= ~self.bitflag_filter_item + else: + visible_indices.append(i) + visible_indices.sort() + return filtered, ordered + + +class CCIC_AVAILABLEMIX_UL_List(bpy.types.UIList): + + # static + visible_bone_indices = [] + visible_key_indices = [] + + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.label(text=item.name if item else "", translate=False, icon_value=icon) + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + def is_rigify_bone(bone: bpy.types.Bone): + return False + + def filter_items(self, context, data, propname): + props = vars.props() + filtered = [] + ordered = [] + items = getattr(data, propname) + filtered = [self.bitflag_filter_item] * len(items) + item : bpy.types.Action + chr_cache = props.get_context_character_cache(context) + visible_indices = self.visible_bone_indices if propname == "available_bones" else self.visible_key_indices + visible_indices.clear() + LFN = self.filter_name.lower() + if propname == "available_bones": + selected_items = [ bone_item.name for bone_item in chr_cache.action_options.import_bones ] if chr_cache else [] + elif propname == "available_keys": + selected_items = [ key_item.name for key_item in chr_cache.action_options.import_keys ] if chr_cache else [] + else: + selected_items = [] + for i, item in enumerate(items): + allowed = item.name not in selected_items + # filter by name + if LFN and LFN != "*": + if LFN not in item.name.lower(): + allowed = False + # block not allowed + if not allowed: + filtered[i] &= ~self.bitflag_filter_item + else: + visible_indices.append(i) + visible_indices.sort() + return filtered, ordered + + +class CCICActionImportFunctions(bpy.types.Operator): + """Action Import Functions""" + bl_idname = "ccic.action_import_functions" + bl_label = "Action Import Functions" + bl_options = {"REGISTER", "UNDO"} + + param: bpy.props.StringProperty( + name = "param", + default = "", + options={"HIDDEN"} + ) + + def execute(self, context): + props = vars.props() + prefs = vars.prefs() + chr_cache = props.get_context_character_cache(context) + if chr_cache: + if self.param == "ADD_BONE": + self.add_bone(chr_cache) + elif self.param == "ADD_ALL_BONES": + self.add_bone(chr_cache, all=True) + elif self.param == "REMOVE_BONE": + self.remove_bone(chr_cache) + elif self.param == "REMOVE_ALL_BONES": + self.remove_bone(chr_cache, all=True) + elif self.param == "ADD_KEY": + self.add_key(chr_cache) + elif self.param == "ADD_ALL_KEYS": + self.add_key(chr_cache, all=True) + elif self.param == "REMOVE_KEY": + self.remove_key(chr_cache) + elif self.param == "REMOVE_ALL_KEYS": + self.remove_key(chr_cache, all=True) + return {"FINISHED"} + + def find_remaining_next_index(self, visible: List[int], current, removes=False): + """After current index is removed from the visible list""" + try: + i = visible.index(current) + if i == len(visible) - 1: + if removes: + return visible[-2] + else: + return visible[-2] + else: + if removes: + return visible[i + 1] - 1 + else: + return visible[i + 1] + except: + return -1 + + def has_name(self, collection, name): + for item in collection: + if item.name == name: + return True + return False + + def add_bone(self, chr_cache, all=False): + try: + props = chr_cache.action_options + if all: + visible_bone_indices = CCIC_AVAILABLEMIX_UL_List.visible_bone_indices + for index in visible_bone_indices: + try: + bone_name = props.available_bones[index].name + if not self.has_name(props.import_bones, bone_name): + bone_item = props.import_bones.add() + bone_item.name = bone_name + bone_item.weight = 1.0 + except: ... + props.available_bones_index = -1 + else: + bone_index = props.available_bones_index + bone_name = props.available_bones[bone_index].name + props.available_bones_index = self.find_remaining_next_index(CCIC_AVAILABLEMIX_UL_List.visible_bone_indices, + bone_index) + for bone_item in props.import_bones: + if bone_item.name == bone_name: + return + bone_item = props.import_bones.add() + bone_item.name = bone_name + bone_item.weight = 1.0 + except: ... + + def remove_bone(self, chr_cache, all=False): + props = chr_cache.action_options + if all: + visible_bone_indices = CCIC_IMPORTMIX_UL_List.visible_bone_indices + for i in range(len(visible_bone_indices), 0, -1): + index = visible_bone_indices[i - 1] + props.import_bones.remove(index) + props.import_bones_index = -1 + else: + bone_index = props.import_bones_index + props.import_bones_index = self.find_remaining_next_index(CCIC_IMPORTMIX_UL_List.visible_bone_indices, + bone_index, + removes=True) + try: + props.import_bones.remove(bone_index) + except: + utils.log_info(f"Unable to remove import bones index: {bone_index}") + + def add_key(self, chr_cache, all=False): + try: + props = chr_cache.action_options + if all: + visible_key_indices = CCIC_AVAILABLEMIX_UL_List.visible_key_indices + for index in visible_key_indices: + try: + key_name = props.available_keys[index].name + if not self.has_name(props.import_keys, key_name): + key_item = props.import_keys.add() + key_item.name = key_name + key_item.weight = 1.0 + except: ... + props.available_bones_index = -1 + else: + key_index = props.available_keys_index + key_name = props.available_keys[key_index].name + props.available_keys_index = self.find_remaining_next_index(CCIC_AVAILABLEMIX_UL_List.visible_key_indices, + key_index) + for key_item in props.import_keys: + if key_item.name == key_name: + return + key_item = props.import_keys.add() + key_item.name = key_name + key_item.weight = 1.0 + except: ... + + def remove_key(self, chr_cache, all=False): + props = chr_cache.action_options + if all: + visible_key_indices = CCIC_IMPORTMIX_UL_List.visible_key_indices + for i in range(len(visible_key_indices), 0, -1): + index = visible_key_indices[i - 1] + props.import_keys.remove(index) + props.import_keys_index = -1 + else: + key_index = props.import_keys_index + props.import_keys_index = self.find_remaining_next_index(CCIC_IMPORTMIX_UL_List.visible_key_indices, + key_index, + removes=True) + try: + props.import_keys.remove(key_index) + if props.import_keys_index >= len(props.import_keys): + props.import_keys_index = len(props.import_keys) - 1 + except: + utils.log_info(f"Unable to remove import keys index: {key_index}") + + @classmethod + def description(cls, context, properties): + if properties.param == "ADD_BONE": + return "Add the selected bone to the bone mask" + if properties.param == "ADD_ALL_BONES": + return "Add all availble bones to the bone mask" + if properties.param == "REMOVE_BONE": + return "Remove the selected bone from the bone mask" + if properties.param == "REMOVE_ALL_BONES": + return "Remove all bones from the bone mask" + if properties.param == "ADD_KEY": + return "Add the selected shape key to the key mask" + if properties.param == "ADD_ALL_KEYS": + return "Add all available shape keys to the key mask" + if properties.param == "REMOVE_KEY": + return "Remove the selected key from the key mask" + if properties.param == "REMOVE_ALL_KEYS": + return "Remove all shape keys from the key mask" + return "" + +class CCICMotionBlendFunctions(bpy.types.Operator): + """Motion Blend Operator Functions""" + bl_idname = "ccic.motion_blend_funcs" + bl_label = "Motion Blend Functions" + bl_options = {"REGISTER", "UNDO"} + + param: bpy.props.StringProperty( + name = "param", + default = "", + options={"HIDDEN"} + ) + + def reset_opts(self, opts): + opts.frame_mode = "MATCH" + opts.use_bone_masking = False + opts.use_key_masking = False + opts.relative_root = False + opts.current_root = False + opts.use_blend = False + + def get_item(self, collection, name, create=False): + for item in collection: + if item.name == name: + return item + if create: + item = collection.add() + return item + return None + + def remove_item(self, collection, name): + for i, item in enumerate(collection): + if item.name == name: + collection.remove(i) + + def save_preset(self, opts): + props = vars.props() + collections = { + "Bones": opts.import_bones, + "Keys": opts.import_keys, + } + folder = utils.get_resource_folder("Blend Presets") + file_name = os.path.join(folder, f"{opts.preset_mask_name}.json") + json_data = {} + if True: # dev option append preset + if os.path.exists(file_name): + file = open(file_name, "rt") + text_data = file.read() + file.close() + json_data = json.loads(text_data) + for type, collection in collections.items(): + if type not in json_data: + json_data[type] = {} + for item in collection: + json_data[type][item.name] = item.weight + text_data = json.dumps(json_data, indent = 4) + with open(file_name, "w") as write_file: + write_file.write(text_data) + props.blend_mask_presets = opts.preset_mask_name + + def delete_preset(self, opts): + props = vars.props() + folder = utils.get_resource_folder("Blend Presets") + file_name = os.path.join(folder, f"{opts.preset_mask_name}.json") + if os.path.exists(file_name) and os.path.isfile(file_name): + os.unlink(file_name) + + def apply_preset(self, opts, remove=False): + props = vars.props() + folder = utils.get_resource_folder("Blend Presets") + preset_name = props.blend_mask_presets + file_name = os.path.join(folder, f"{preset_name}.json") + if os.path.exists(file_name): + file = open(file_name, "rt") + text_data = file.read() + file.close() + json_data = json.loads(text_data) + for type in json_data: + if type == "Bones": + available = opts.available_bones + collection = opts.import_bones + else: + available = opts.available_keys + collection = opts.import_keys + if collection is not None: + for name, weight in json_data[type].items(): + available_item = self.get_item(available, name, create=False) + if available_item: + if remove: + self.remove_item(collection, name) + else: + import_item = self.get_item(collection, name, create=True) + if import_item: + import_item.name = name + import_item.weight = weight + if type == "Bones": + opts.use_bone_masking = True + else: + opts.use_key_masking = True + + def execute(self, context): + props = vars.props() + prefs = vars.prefs() + chr_cache = props.get_context_character_cache(context, strict=True) + + if chr_cache: + + opts = chr_cache.action_options + + if self.param == "SAVE_MODE_ON": + opts.mode_save_mask_preset = True + opts.preset_mask_name = props.blend_mask_presets + + if self.param == "SAVE_MODE_OFF": + opts.mode_save_mask_preset = False + + if self.param == "SAVE_PRESET": + opts.mode_save_mask_preset = False + self.save_preset(opts) + + if self.param == "DELETE_PRESET": + opts.mode_save_mask_preset = False + self.delete_preset(opts) + + if self.param == "APPLY_PRESET": + self.apply_preset(opts) + + if self.param == "REMOVE_PRESET": + self.apply_preset(opts, remove=True) + + if self.param == "RESET_OPTS": + self.reset_opts(opts) + + return {'FINISHED'} + + @classmethod + def description(cls, context, properties): + if properties.param == "SAVE_MODE_ON": + return "Enter Preset Save Mode" + if properties.param == "SAVE_MODE_ON": + return "Exit Preset Save Mode" + if properties.param == "SAVE_PRESET": + return "Save the preset" + if properties.param == "DELETE_PRESET": + return "Delete the preset" + if properties.param == "APPLY_PRESET": + return "Add the preset to the masks" + if properties.param == "REMOVE_PRESET": + return "Remove the preset from the masks" + return "" + + +class CCICMotionBlend(bpy.types.Operator): + """Motion Import / Blend Options""" + bl_idname = "ccic.motion_blend" + bl_label = "Motion Blend Options" + bl_options = {"REGISTER", "UNDO"} + + param: bpy.props.StringProperty( + name = "param", + default = "", + options={"HIDDEN"} + ) + + # pointer properties don't work in operators + #source_motion: bpy.props.PointerProperty(type=bpy.types.Action) + #target_motion: bpy.props.PointerProperty(type=bpy.types.Action) + + blend_in_data_global = None + blend_out_data_global = None + blend_in_data_chr = None + blend_out_data_chr = None + + chr_rlx_cache = None + objects = {} + + @classmethod + def poll(cls, context): + return True + #props = vars.props() + #return props.get_context_character_cache(context) is not None + + @classmethod + def label(cls, context, chr_cache=None): + props = vars.props() + opts = props.action_options + if not chr_cache: + rlx_cache = props.get_context_staging_cache(context) + chr_cache = props.get_context_character_cache(context, strict=True) + if rlx_cache: + opts = rlx_cache.action_options + elif chr_cache: + opts = chr_cache.action_options + frame_text = { + "MATCH": "Match", + "CURRENT": "Current", + "START": "Start", + } + mask_text = "Mask|" if opts and (opts.use_bone_masking or opts.use_key_masking) else "" + blend_text = "Blend|" if opts and opts.use_blend else "" + label_text = f"{mask_text}{blend_text}{frame_text[opts.get_frame_mode()]}" + return label_text + + def preset_group(self, layout, opts): + props = vars.props() + row = layout.row() + c1, c2, c3 = utils.ui_three_column_layout(row, (3,14,3)) + if opts.mode_save_mask_preset: + c1.label(text="Save Preset:") + c2.prop(opts, "preset_mask_name", text="") + r = c3.row(align=False) + r.operator("ccic.motion_blend_funcs", text="", icon="FILE_TICK", depress=True).param = "SAVE_MODE_OFF" + r.operator("ccic.motion_blend_funcs", text="", icon="CHECKMARK").param = "SAVE_PRESET" + r.operator("ccic.motion_blend_funcs", text="", icon="TRASH").param = "DELETE_PRESET" + else: + c1.label(text="Load Preset:") + c2.prop(props, "blend_mask_presets", text="") + r = c3.row(align=False) + r.operator("ccic.motion_blend_funcs", text="", icon="FILE_TICK").param = "SAVE_MODE_ON" + r.operator("ccic.motion_blend_funcs", text="", icon="TRIA_UP").param = "APPLY_PRESET" + r.operator("ccic.motion_blend_funcs", text="", icon="TRIA_DOWN").param = "REMOVE_PRESET" + + + def draw(self, context): + prefs = vars.prefs() + props = vars.props() + layout = self.layout + column = layout.column() + + if not self.chr_rlx_cache: + layout.label(text="No Character Selected!") + return + + col_1, col_2, col_3 = utils.ui_three_column_layout(column, (2, 5, 3)) + row_3 = col_3.row() + if self.param == "BLEND": + col_1.label(text="Blend Options:") + col_2.label(text=f"{self.chr_rlx_cache.get_name()}") + row_3.label(text="") + + else: + col_1.label(text="Motion Options:") + col_2.label(text=f"{self.chr_rlx_cache.get_name()}") + row_3.label(text="") + + opts = self.chr_rlx_cache.action_options + + if utils.ui_fake_drop_down(row_3, "", opts, "show_motion_options", align="RIGHT"): + col_1.label(text="Frame Mode:") + col_2.prop(opts, "frame_mode", text="") + col_3.prop(opts, "relative_root") + if self.param == "BLEND": + col_1.separator() + col_2.separator() + col_3.separator() + source_range = get_motion_set_frame_range(opts.source_motion) + target_range = get_motion_set_frame_range(opts.target_motion) + col_1.label(text="Source Motion:") + col_2.prop(opts, "source_motion", text="") + col_3.label(text=f"Frames: {source_range[0]} - {source_range[1]}") + col_1.label(text="Destination Motion:") + col_2.prop(opts, "target_motion", text="") + col_3.label(text=f"Frames: {target_range[0]} - {target_range[1]}") + + if opts.frame_mode == "START": + start = 1 + end = source_range[1] - source_range[0] + start + elif opts.frame_mode == "CURRENT": + start = bpy.context.scene.frame_current + end = source_range[1] - source_range[0] + start + elif opts.frame_mode == "MATCH": + start = source_range[0] + end = source_range[1] + tot_start = min(start, target_range[0]) + tot_end = max(end, target_range[1]) + + col_1.label(text="As New Motion") + col_2.prop(opts, "motion_prefix", text="") + col_3.label(text=f"Blend Frames: {start} - {end}") + col_1.label(text="") + col_2.prop(opts, "motion_id", text="") + col_3.label(text=f"Total Frames: {tot_start} - {tot_end}") + name = self.get_new_motion_name() + col_1.label(text="Motion Name:") + col_2.label(text=name) + col_3.label(text="") + + row = column.row() + row.prop(opts, "use_blend") + if opts.use_blend: + if utils.ui_fake_drop_down(row, "", opts, "show_blend_options", align="RIGHT"): + grid = column.grid_flow(row_major=True, columns=2) + grid.prop(opts, "blend_in_frames") + grid.prop(opts, "blend_out_frames") + box_in = grid.box() + box_out = grid.box() + box_in.template_curve_mapping(self.blend_in_data_chr, "mapping") + box_out.template_curve_mapping(self.blend_out_data_chr, "mapping") + #box_in.enabled = opts.blend_in_frames > 0 + #box_out.enabled = opts.blend_out_frames > 0 + column.prop(opts, "blend_strength", slider=True) + + if self.chr_rlx_cache.is_avatar() or self.chr_rlx_cache.is_prop(): + # bone masking + row = column.row() + row.prop(opts, "use_bone_masking") + if opts.use_bone_masking: + if utils.ui_fake_drop_down(row, "", opts, "show_bone_mask_options", align="RIGHT"): + b_prop = self.get_mix_bone_prop(self.chr_rlx_cache) + row = column.row() + c1, c2, c3 = utils.ui_three_column_layout(row, (12,1,12)) + c1.template_list("CCIC_AVAILABLEMIX_UL_List", "available_bones_list", + opts, "available_bones", + opts, "available_bones_index", + rows=5, maxrows=4) + col = c2.column() + #col.separator(factor=4.0) + col.operator("ccic.action_import_functions", text="", icon="TRIA_RIGHT_BAR").param = "ADD_ALL_BONES" + col.operator("ccic.action_import_functions", text="", icon="TRIA_RIGHT").param = "ADD_BONE" + col.separator(factor=2.0) + col.operator("ccic.action_import_functions", text="", icon="TRIA_LEFT").param = "REMOVE_BONE" + col.operator("ccic.action_import_functions", text="", icon="TRIA_LEFT_BAR").param = "REMOVE_ALL_BONES" + #col.separator(factor=4.0) + rows = 4 if b_prop else 5 + c3.template_list("CCIC_IMPORTMIX_UL_List", "import_bones_list", + opts, "import_bones", + opts, "import_bones_index", + rows=rows, maxrows=rows) + if b_prop: + c3.prop(b_prop, "weight", text="Blend Weight", slider=True) + + # key masking + row = column.row() + row.prop(opts, "use_key_masking") + if opts.use_key_masking: + if utils.ui_fake_drop_down(row, "", opts, "show_key_mask_options", align="RIGHT"): + k_prop = self.get_mix_key_prop(self.chr_rlx_cache) + row = column.row() + c1, c2, c3 = utils.ui_three_column_layout(row, (12,1,12)) + c1.template_list("CCIC_AVAILABLEMIX_UL_List", "available_keys_list", + opts, "available_keys", + opts, "available_keys_index", + rows=5, maxrows=4) + col = c2.column() + col.operator("ccic.action_import_functions", text="", icon="TRIA_RIGHT_BAR").param = "ADD_ALL_KEYS" + col.operator("ccic.action_import_functions", text="", icon="TRIA_RIGHT").param = "ADD_KEY" + col.separator(factor=2.0) + col.operator("ccic.action_import_functions", text="", icon="TRIA_LEFT").param = "REMOVE_KEY" + col.operator("ccic.action_import_functions", text="", icon="TRIA_LEFT_BAR").param = "REMOVE_ALL_KEYS" + col = row.column() + rows = 4 if k_prop else 5 + c3.template_list("CCIC_IMPORTMIX_UL_List", "import_keys_list", + opts, "import_keys", + opts, "import_keys_index", + rows=rows, maxrows=rows) + if k_prop: + c3.prop(k_prop, "weight", text="Blend Weight", slider=True) + + # presets + if opts.use_bone_masking or opts.use_key_masking: + row = layout.row() + self.preset_group(row, opts) + + def get_mix_bone_prop(self, chr_cache): + bone_item = None + if chr_cache: + props = chr_cache.action_options + index = props.import_bones_index + try: + bone_item = props.import_bones[index] + except: ... + return bone_item + + def get_mix_key_prop(self, chr_cache): + key_item = None + if chr_cache: + props = chr_cache.action_options + index = props.import_keys_index + try: + key_item = props.import_keys[index] + except: ... + return key_item + + def is_valid_bone(self, bone: bpy.types.Bone): + if self.chr_rlx_cache.rigified: + # also ignore bones with drivers? + if (bone.name.startswith("facerig")): + return False + if (bone.name.startswith("CTRL_") and + (bone.name.endswith("_line") or bone.name.endswith("_box"))): + return False + if ("VIS" in bone.name): + return False + collection: bpy.types.BoneCollection + for collection in bone.collections: + if ("Hidden" in collection.name or + "ORG" in collection.name or + "MCH" in collection.name or + "DEF" in collection.name): + return False + return True + + def build_available(self): + if self.chr_rlx_cache: + props = self.chr_rlx_cache.action_options + props.available_bones.clear() + props.available_keys.clear() + + arm = self.chr_rlx_cache.get_armature() + if arm: + for bone in arm.data.bones: + if self.is_valid_bone(bone): + bone_item = props.available_bones.add() + bone_item.name = bone.name + + keys = [] + for obj in arm.children: + if utils.object_exists_is_mesh(obj) and utils.object_has_shape_keys(obj): + for i, key in enumerate(obj.data.shape_keys.key_blocks): + if i > 0 and key.name not in keys: + keys.append(key.name) + keys.sort() + for key in keys: + key_item = props.available_keys.add() + key_item.name = key + + def get_new_motion_name(self): + name = "" + if self.chr_rlx_cache: + opts = self.chr_rlx_cache.action_options + actor_obj = self.chr_rlx_cache.get_primary_object() + if actor_obj: + target_motion = opts.target_motion + set_id, set_generation, action_type_id, key_object = get_motion_set_ids(target_motion) + rig_id = get_rig_id(actor_obj) + name = get_new_motion_set_action_name(action_type_id, set_id, opts.motion_prefix, rig_id, opts.motion_id, rlob=actor_obj) + return name + + def execute(self, context): + props = vars.props() + prefs = vars.prefs() + + if self.chr_rlx_cache and self.param == "BLEND": + opts = self.chr_rlx_cache.action_options + source_motion = opts.source_motion + target_motion = opts.target_motion + actor_obj = self.chr_rlx_cache.get_primary_object() + if actor_obj and source_motion: + current_motion = utils.safe_get_action(actor_obj) + duplicate_motion = duplicate_motion_set(source_motion) + if duplicate_motion: + set_id, set_generation, action_type_id, key_object = get_motion_set_ids(duplicate_motion) + rig_id = get_rig_id(actor_obj) + rename_motion_set(action_type_id, set_id, opts.motion_prefix, rig_id, opts.motion_id) + # TODO if the target motion is not of this rig id, load_motion_set will make a new motion ... + if current_motion != target_motion: + load_motion_set(actor_obj, target_motion) + # store target motion + action_store_id = props.store_actions(actor_obj) + # load the source motion + # TODO likewise with the duplicate if the source motion is not of this rig id ... + load_motion_set(actor_obj, duplicate_motion, force=True) + # shift motion frames based on frame mode option + if opts.frame_mode == "START": + shift_motion_frames(duplicate_motion, 1) + elif opts.frame_mode == "CURRENT": + shift_motion_frames(duplicate_motion, bpy.context.scene.frame_current) + finalize_motion_import(self.chr_rlx_cache, actor_obj, duplicate_motion, + action_store_id) + else: + utils.log_error(f"Unable to duplicate motion set: {source_motion.name}") + return {'FINISHED'} + + def invoke(self, context, event): + props = vars.props() + prefs = vars.prefs() + utils.set_mode("OBJECT") + props.ensure_context_object(context) + rlx_cache = props.get_context_staging_cache(context) + chr_cache = props.get_context_character_cache(context, strict=True) + self.chr_rlx_cache = chr_cache if not rlx_cache else rlx_cache + self.blend_in_data_global = ui.get_fcurve_data(None, "motion_blend_in", shape="IN") + self.blend_out_data_global = ui.get_fcurve_data(None, "motion_blend_out", shape="OUT") + self.blend_in_data_chr = ui.get_fcurve_data(self.chr_rlx_cache, "motion_blend_in", shape="IN") + self.blend_out_data_chr = ui.get_fcurve_data(self.chr_rlx_cache, "motion_blend_out", shape="OUT") + if self.chr_rlx_cache: + actor_obj = self.chr_rlx_cache.get_primary_object() + if actor_obj: + opts = self.chr_rlx_cache.action_options + opts.source_motion = utils.collection_at_index(props.action_set_list_index, bpy.data.actions) + opts.target_motion = utils.safe_get_action(actor_obj) + self.build_available() + + if self.param == "BLEND": + if self.chr_rlx_cache: + return context.window_manager.invoke_props_dialog(self, width=600) + else: + return context.window_manager.invoke_props_dialog(self, width=600) + + return {'FINISHED'} + + @classmethod + def description(cls, context, properties): + if properties.param == "BLEND": + return "Blend motions using options including: blending, overwriting, frame shifting, bone and shape key masking" + else: + return "Options for importing motions, including: blending, overwriting, frame shifting, bone and shape key masking" + return "" +# endregion +# region prefs +class CCICMotionPrefs(bpy.types.Operator): + """Motion Prefs""" + bl_idname = "ccic.motion_prefs" + bl_label = "Motion Preferences" + bl_options = {"REGISTER", "UNDO"} + + param: bpy.props.StringProperty( + name = "param", + default = "", + options={"HIDDEN"} + ) + + def draw(self, context): + prefs = vars.prefs() + props = vars.props() + layout = self.layout + column = layout.column() + + # prefs + column.label(text="Preferences:") + row = column.row() + grid = row.grid_flow(row_major=True, columns=1) + if utils.B440(): + grid.prop(prefs, "action_use_action_slots") + grid.prop(prefs, "action_add_key_slots_per_obj") + #grid.prop(prefs, "action_clean_actions") + grid.prop(prefs, "action_add_empty_key_channels") + column.separator() + + def execute(self, context): + props = vars.props() + prefs = vars.prefs() + return {'FINISHED'} + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width=300) + + @classmethod + def description(cls, context, properties): + return "Motion preferences" +# endregion + diff --git a/scripts/addons/cc_blender_tools-2_4_0/rlx.py b/scripts/addons/cc_blender_tools-2_4_2/rlx.py similarity index 89% rename from scripts/addons/cc_blender_tools-2_4_0/rlx.py rename to scripts/addons/cc_blender_tools-2_4_2/rlx.py index 1c3622e..93305f7 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/rlx.py +++ b/scripts/addons/cc_blender_tools-2_4_2/rlx.py @@ -130,6 +130,43 @@ def remap_file(file_path, data_folder): return file_path +def get_rlx_generation(obj): + data_type = str.capitalize(str(obj.type)) + if obj.type == "LIGHT": + data_type = str.capitalize(str(obj.data.type)) + data_type + return data_type + + +def add_rlx_motion_set_data(obj, actions, old_action=None): + acts = set() + data_action = actions[1] + if actions is None: + acts = set([utils.safe_get_action(obj), utils.safe_get_action(obj.data)]) + if type(actions) is bpy.types.Action: + acts.add(actions) + else: + acts = set(actions) + if None in acts: + acts.remove(None) + actions = list(acts) + if obj: + obj_id = utils.get_rl_id(obj) + set_generation = get_rlx_generation(obj) + if old_action: + set_id = rigutils.get_motion_set_ids(old_action)[0] + else: + set_id = rigutils.generate_set_id() + is_slotted = len(actions) == 1 and rigutils.is_slotted_action(actions[0]) + for action in actions: + is_light = action == data_action and obj.type == "LIGHT" + is_camera = action == data_action and obj.type == "CAMERA" + rigutils.add_motion_set_data(action, set_id, set_generation, + rlx_id=obj_id, slotted=is_slotted, + is_light=is_light, is_camera=is_camera) + return True + return False + + def prep_rlx_actions(actor: 'RLXActor', motion_id, timestamp=False, motion_prefix=None): props = vars.props() prefs = vars.prefs() @@ -144,17 +181,24 @@ def prep_rlx_actions(actor: 'RLXActor', motion_id, timestamp=False, motion_prefi actor.action_store_id = props.store_actions(obj) # generate names T = utils.get_slot_type_for(obj.data) - ob_name = rigutils.generate_action_name(name, "O", "", motion_id, motion_prefix) - data_name = rigutils.generate_action_name(name, T[0], "", motion_id, motion_prefix) + use_slotted = prefs.use_action_slots() + ob_name = rigutils.make_rlx_action_name(name, motion_id, motion_prefix, slotted=use_slotted) + ob_slot_name = f"{obj.name} (Object)" + data_name = rigutils.make_data_action_name(obj, name, motion_id, motion_prefix) + data_slot_name = f"{obj.name} ({obj.type.capitalize()})" ob_action = bpy.data.actions.new(ob_name) - if prefs.use_action_slots(): + if use_slotted: data_action = ob_action - ob_slot = ob_action.slots.new("OBJECT", ob_name) - data_slot = data_action.slots.new(T, data_name) else: data_action = bpy.data.actions.new(data_name) + if utils.B440(): + ob_slot = ob_action.slots.new("OBJECT", ob_slot_name) + data_slot = data_action.slots.new(T, data_slot_name) + else: ob_slot = None data_slot = None + # motion set ids + add_rlx_motion_set_data(obj, [ob_action, data_action]) # set the actions utils.safe_set_action(obj, ob_action, slot=ob_slot) utils.safe_set_action(obj.data, data_action, slot=data_slot) @@ -168,15 +212,25 @@ class RLXActor(): def __init__(self, object): self.object = object self.name = object.name + self.get_rlx_cache(create=True) - def get_import_modes(self): + def get_rlx_cache(self, create=False): props = vars.props() - action_mode = props.action_options.get_action_mode() - frame_mode = props.action_options.get_frame_mode() - return action_mode, frame_mode + return props.get_staging_cache(self.object, create=create) + + def get_link_id(self): + if self.object: + return utils.get_rl_link_id(self.object) + else: + rlx_cache = self.get_rlx_cache() + if rlx_cache: + return rlx_cache.get_link_id() + return None def import_rlx_light(data: BinaryData, data_folder, start_frame=1): + props = vars.props() + light_data = data.json() # make the light link_id = light_data["link_id"] @@ -257,13 +311,14 @@ def import_rlx_light(data: BinaryData, data_folder, start_frame=1): store_frame(light, render_cache, frame, start_frame, 0.0 if active else 1.0) actor = RLXActor(light) + rlx_cache = actor.get_rlx_cache(create=True) ob_action, light_action, ob_slot, light_slot = prep_rlx_actions(actor, "Export", timestamp=True) add_cache_fcurves(ob_action, light.path_from_id("location"), loc_cache, num_frames, "Location", slot=ob_slot) add_cache_rotation_fcurves(light, ob_action, rot_cache, num_frames, slot=ob_slot) add_cache_fcurves(ob_action, light.path_from_id("scale"), sca_cache, num_frames, "Scale", slot=ob_slot) - add_cache_fcurves(light_action, light.path_from_id("hide_viewport"), visible_cache, num_frames, "Hide Viewport", slot=ob_slot, interpolation="CONSTANT") - add_cache_fcurves(light_action, light.path_from_id("hide_render"), render_cache, num_frames, "Hide Render", slot=ob_slot, interpolation="CONSTANT") + add_cache_fcurves(ob_action, light.path_from_id("hide_viewport"), visible_cache, num_frames, "Hide Viewport", slot=ob_slot, interpolation="CONSTANT") + add_cache_fcurves(ob_action, light.path_from_id("hide_render"), render_cache, num_frames, "Hide Render", slot=ob_slot, interpolation="CONSTANT") add_cache_fcurves(light_action, light.data.path_from_id("color"), color_cache, num_frames, "Color", slot=light_slot) add_cache_fcurves(light_action, light.data.path_from_id("energy"), energy_cache, num_frames, "Energy", slot=light_slot) add_cache_fcurves(light_action, light.data.path_from_id("cutoff_distance"), cutoff_distance_cache, num_frames, "Cutoff Distance", slot=light_slot) @@ -271,8 +326,7 @@ def import_rlx_light(data: BinaryData, data_folder, start_frame=1): add_cache_fcurves(light_action, light.data.path_from_id("spot_blend"), spot_blend_cache, num_frames, "Spot Blend", slot=light_slot) add_cache_fcurves(light_action, light.data.path_from_id("spot_size"), spot_size_cache, num_frames, "Spot Size", slot=light_slot) - action_mode, frame_mode = actor.get_import_modes() - rigutils.finalize_rlx_import(light, [ob_action, light_action], actor.action_store_id, action_mode) + rigutils.finalize_rlx_import(rlx_cache, light, [ob_action, light_action], actor.action_store_id) def import_rlx_camera(data: BinaryData, data_folder, start_frame=1): @@ -338,6 +392,7 @@ def import_rlx_camera(data: BinaryData, data_folder, start_frame=1): active_cache.append((frame, time, active)) actor = RLXActor(camera) + rlx_cache = actor.get_rlx_cache(create=True) ob_action, cam_action, ob_slot, cam_slot = prep_rlx_actions(actor, "Export", timestamp=True) add_cache_fcurves(ob_action, "location", loc_cache, num_frames, "Location", slot=ob_slot) @@ -349,9 +404,7 @@ def import_rlx_camera(data: BinaryData, data_folder, start_frame=1): add_cache_fcurves(cam_action, "dof.aperture_fstop", f_stop_cache, num_frames, "DOF", slot=cam_slot) add_camera_markers(camera, active_cache, num_frames, start_frame) - - action_mode, frame_mode = actor.get_import_modes() - rigutils.finalize_rlx_import(camera, [ob_action, cam_action], actor.action_store_id, action_mode) + rigutils.finalize_rlx_import(rlx_cache, camera, [ob_action, cam_action], actor.action_store_id) def frame_rotation_cache(obj, frames): @@ -503,6 +556,9 @@ def add_camera_markers(camera, cache, num_frames, start): def decode_rlx_light(light_data, light: bpy.types.Object=None, container=None): + props = vars.props() + + rlx_cache = props.get_staging_cache(light) # static properties link_id = light_data["link_id"] name: str = light_data["name"] @@ -548,6 +604,14 @@ def decode_rlx_light(light_data, light: bpy.types.Object=None, container=None): light = add_spot_light(light_data["name"], container) utils.set_rl_link_id(light, link_id) + utils.set_prop(light, "rl_set_generation", get_rlx_generation(light)) + + if rlx_cache: + rlx_cache.update_object(light) + else: + rlx_cache = props.add_staging_cache() + rlx_cache.update_object(light) + utils.safe_set_action(light, ob_action, slot=ob_slot) utils.safe_set_action(light.data, light_action, light_slot) @@ -635,6 +699,9 @@ def apply_light_pose(light, loc, rot, sca, color, active, multiplier, range, ang def decode_rlx_camera(camera_data, camera): + props = vars.props() + + rlx_cache = props.get_staging_cache(camera) # static properties link_id = camera_data["link_id"] name: str = camera_data["name"] @@ -673,6 +740,14 @@ def decode_rlx_camera(camera_data, camera): camera = add_camera(name) utils.set_rl_link_id(camera, link_id) + utils.set_prop(camera, "rl_set_generation", get_rlx_generation(camera)) + + if rlx_cache: + rlx_cache.update_object(camera) + else: + rlx_cache = props.add_staging_cache() + rlx_cache.update_object(camera) + utils.safe_set_action(camera, ob_action, slot=ob_slot) utils.safe_set_action(camera.data, cam_action, slot=cam_slot) diff --git a/scripts/addons/cc_blender_tools-2_4_0/scene.py b/scripts/addons/cc_blender_tools-2_4_2/scene.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/scene.py rename to scripts/addons/cc_blender_tools-2_4_2/scene.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/sculpting.py b/scripts/addons/cc_blender_tools-2_4_2/sculpting.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/sculpting.py rename to scripts/addons/cc_blender_tools-2_4_2/sculpting.py index 0c87c65..ef8c573 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/sculpting.py +++ b/scripts/addons/cc_blender_tools-2_4_2/sculpting.py @@ -247,7 +247,7 @@ def save_skin_gen_bake(chr_cache, body, layer_target): utils.log_info(f"Texture save path: {bake_dir}") os.makedirs(bake_dir, exist_ok=True) - character_name = chr_cache.character_name + character_name = chr_cache.get_name() if body: for mat in body.data.materials: @@ -398,7 +398,7 @@ def setup_bake_nodes(context, chr_cache, multires_mesh, layer_target): bsdf_node, shader_node, mixer_node = nodeutils.get_shader_nodes(mat, shader_name) # base the image name on the character name - character_name = chr_cache.character_name + character_name = chr_cache.get_name() mix_node_name = f"{layer_target}_{LAYER_MIX_SUFFIX}" sculpt_mix_node_name = f"{LAYER_TARGET_SCULPT}_{LAYER_MIX_SUFFIX}" detail_mix_node_name = f"{LAYER_TARGET_DETAIL}_{LAYER_MIX_SUFFIX}" @@ -557,7 +557,7 @@ def flatten_bake_layers(context, chr_cache): prefs = vars.prefs() bake_dir = get_bake_dir(chr_cache) - prefix = chr_cache.character_name + "_Flattened" + prefix = chr_cache.get_name() + "_Flattened" context_obj = utils.get_context_mesh(context) if context_obj: @@ -774,7 +774,7 @@ def add_multires_mesh(context, chr_cache, layer_target, sub_target = "ALL"): body_id = body_cache.object_id if body_cache else None body_objects = chr_cache.get_objects_of_type("BODY") cache_objects = chr_cache.get_cache_objects() - is_body_type = body_id and utils.get_rl_object_id(context_object) == body_id + is_body_type = body_id and utils.get_rl_id(context_object) == body_id multires_mesh = None multires_source = None diff --git a/scripts/addons/cc_blender_tools-2_4_0/shaders.py b/scripts/addons/cc_blender_tools-2_4_2/shaders.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/shaders.py rename to scripts/addons/cc_blender_tools-2_4_2/shaders.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/springbones.py b/scripts/addons/cc_blender_tools-2_4_2/springbones.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/springbones.py rename to scripts/addons/cc_blender_tools-2_4_2/springbones.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/textures/RL_CC3_Plus_EyeTearlineNormal.TGA b/scripts/addons/cc_blender_tools-2_4_2/textures/RL_CC3_Plus_EyeTearlineNormal.TGA similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/textures/RL_CC3_Plus_EyeTearlineNormal.TGA rename to scripts/addons/cc_blender_tools-2_4_2/textures/RL_CC3_Plus_EyeTearlineNormal.TGA diff --git a/scripts/addons/cc_blender_tools-2_4_0/textures/RL_SkinMicroCavityMap.png b/scripts/addons/cc_blender_tools-2_4_2/textures/RL_SkinMicroCavityMap.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/textures/RL_SkinMicroCavityMap.png rename to scripts/addons/cc_blender_tools-2_4_2/textures/RL_SkinMicroCavityMap.png diff --git a/scripts/addons/cc_blender_tools-2_4_0/textures/RL_SkinSpecDetail.png b/scripts/addons/cc_blender_tools-2_4_2/textures/RL_SkinSpecDetail.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/textures/RL_SkinSpecDetail.png rename to scripts/addons/cc_blender_tools-2_4_2/textures/RL_SkinSpecDetail.png diff --git a/scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set123.png b/scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set123.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set123.png rename to scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set123.png diff --git a/scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set1A.png b/scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set1A.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set1A.png rename to scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set1A.png diff --git a/scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set1B.png b/scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set1B.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set1B.png rename to scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set1B.png diff --git a/scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set2.png b/scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set2.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set2.png rename to scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set2.png diff --git a/scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set3.png b/scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set3.png similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/textures/RL_WrinkleMask_Set3.png rename to scripts/addons/cc_blender_tools-2_4_2/textures/RL_WrinkleMask_Set3.png diff --git a/scripts/addons/cc_blender_tools-2_4_2/ui.py b/scripts/addons/cc_blender_tools-2_4_2/ui.py new file mode 100644 index 0000000..f8790c0 --- /dev/null +++ b/scripts/addons/cc_blender_tools-2_4_2/ui.py @@ -0,0 +1,50 @@ +import bpy + +DATA_NODE_GROUP = "RL_Data_Group" + + +def get_data_group(): + if DATA_NODE_GROUP not in bpy.data.node_groups: + bpy.data.node_groups.new(DATA_NODE_GROUP, 'ShaderNodeTree') + data_group = bpy.data.node_groups[DATA_NODE_GROUP] + return data_group + + +def get_node_id(chr_cache, id): + if id: + chr_id = "Global" if chr_cache is None else chr_cache.get_name() + return f"RL_{chr_id}_{id}" + return "" + + +def get_rgbcurve_node(data_group, node_id, shape=None): + if data_group and node_id: + for node in data_group.nodes: + if node.name == node_id: + return node + node: bpy.types.ShaderNodeRGBCurve = data_group.nodes.new("ShaderNodeRGBCurve") + node.name = node_id + if shape == "IN": + P = node.mapping.curves[3].points + P[0].location = (0, 0) + P[1].location = (1, 1) + elif shape == "OUT": + P = node.mapping.curves[3].points + P[0].location = (0, 1) + P[1].location = (1, 0) + return node + return None + + +def eval_curve(fcurve_data: bpy.types.ShaderNodeRGBCurve, position: float): + return fcurve_data.mapping.evaluate(fcurve_data.mapping.curves[3], position) + + +def get_fcurve_data(chr_cache, id, shape=None): + data_group = get_data_group() + node_id = get_node_id(chr_cache, id) + node = get_rgbcurve_node(data_group, node_id, shape=shape) + return node + + + diff --git a/scripts/addons/cc_blender_tools-2_4_0/utils.py b/scripts/addons/cc_blender_tools-2_4_2/utils.py similarity index 92% rename from scripts/addons/cc_blender_tools-2_4_0/utils.py rename to scripts/addons/cc_blender_tools-2_4_2/utils.py index 7097a35..55944fe 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/utils.py +++ b/scripts/addons/cc_blender_tools-2_4_2/utils.py @@ -22,6 +22,7 @@ import difflib import random import re, json import traceback +import math from mathutils import Vector, Quaternion, Matrix, Euler, Color from hashlib import md5 import bpy @@ -264,7 +265,7 @@ def unique_object_name(name, obj=None, capitalize=False, start_index=1, suffix=" def un_suffix_name(name): """Removes any combination of numerical suffixes from the end of a string""" - base_name = re.sub("([._+|/\,]\d+)*$", "", name) + base_name = re.sub("([._+|/\\,]\\d+)*$", "", name) return base_name @@ -372,6 +373,21 @@ def object_exists_is_mesh(obj): return False +def object_exists_has_shape_keys(obj): + """Test if Object: obj still exists as an object in the scene, and is a mesh.""" + if obj is None: + return False + try: + name = obj.name + return (len(obj.users_scene) > 0 and + obj.type == "MESH" and + obj.data.shape_keys is not None and + obj.data.shape_keys.key_blocks is not None and + len(obj.data.shape_keys.key_blocks) > 1) + except: + return False + + def object_exists_is_armature(obj) -> bool: """Test if Object: obj still exists as an object in the scene, and is an armature.""" if obj is None: @@ -475,9 +491,11 @@ def get_selected_mesh(): return None -def get_selected_meshes(context = None): +def get_selected_meshes(context=None, selection=None): """Gets selected meshes and includes any current context mesh""" - objects = [ obj for obj in bpy.context.selected_objects if object_exists_is_mesh(obj) ] + if not selection: + selection = bpy.context.selected_objects + objects = [ obj for obj in selection if object_exists_is_mesh(obj) ] if context and context.object: if object_exists_is_mesh(context.object): if context.object not in objects: @@ -617,6 +635,37 @@ def remap(from_min, from_max, to_min, to_max, x): return to_min + ((x - from_min) * (to_max - to_min) / (from_max - from_min)) + +def slerp(q0: Quaternion, q1: Quaternion, t: float, dot_threshold=0.9995): + + omt = max(0, 1.0 - t) + + q0.normalize() + q1.normalize() + + dot = q0.dot(q1) + if dot < 0.0: + q1 = -q1 + dot = -dot + + dot = max(min(dot, 1.0), -1.0) + + if dot > dot_threshold: + # LERP then normalize + out = (q0 * (omt)) + (q1 * t) + return out.normalized() + + theta_0 = math.acos(dot) # angle between input quaternions + sin_theta_0 = math.sin(theta_0) + theta = theta_0 * t + sin_theta = math.sin(theta) + s0 = math.cos(theta) - dot * sin_theta / sin_theta_0 + s1 = sin_theta / sin_theta_0 + out = (q0 * s0) + (q1 * s1) + + return out.normalized() + + def lerp(v0, v1, t, clamp=True): if clamp: t = max(0, min(1, t)) @@ -1820,16 +1869,23 @@ def move_object_to_scene_collections(obj, collections, exclude_rbw = True): def store_mode_selection_state(): mode = get_mode() active = get_active_object() - selection = bpy.context.selected_objects.copy() - return [mode, active, selection, + selection = [ o.name for o in bpy.context.selected_objects ] + return [mode, active.name if active else "", selection, (bpy.context.scene.frame_current, bpy.context.scene.frame_start, bpy.context.scene.frame_end)] def restore_mode_selection_state(store, include_frames=True): try: set_mode("OBJECT") - try_select_objects(store[2], True) - set_active_object(store[1]) + objects = [] + for obj_name in store[2]: + if obj_name in bpy.data.objects: + objects.append(bpy.data.objects[obj_name]) + active = None + if store[1] in bpy.data.objects: + active = bpy.data.objects[store[1]] + try_select_objects(objects, clear_selection=True) + set_active_object(active) set_mode(store[0]) if include_frames: bpy.context.scene.frame_current = store[3][0] @@ -2036,15 +2092,21 @@ def has_data_path(obj: bpy.types.Object, path: str): def get_data_path_object_name(data_path: str): - if data_path.startswith("pose.bones["): - start = data_path.find('"', 0) + 1 - end = data_path.find('"', start) - return data_path[start:end] - elif data_path.startswith("key_blocks["): - start = data_path.find('"', 0) + 1 - end = data_path.find('"', start) - return data_path[start:end] - return "" + try: + if data_path.startswith("pose.bones["): + start = data_path.find('"', 0) + 1 + end = data_path.find('"', start) + return data_path[start:end] + elif data_path.startswith("key_blocks["): + start = data_path.find('"', 0) + 1 + end = data_path.find('"', start) + return data_path[start:end] + else: + names = data_path.split(".") + last = names[-1].replace("_", " ") + return last.title() + except: + return "" def copy_action(action: bpy.types.Action, new_name, reuse=False): @@ -2085,11 +2147,17 @@ def make_action_slot(action, slot_type, slot_name): return None -def get_action_slot(action, slot_type): +def get_action_slot(action: bpy.types.Action, slot_type: str=None, slot_id=None): if action and B440(): for slot in action.slots: - if slot.target_id_type == slot_type: + print(f"slot.target_id_type={slot.target_id_type}, slot.identifier={slot.identifier} == slot_type={slot_type}, slot_id={slot_id}") + if slot_type and slot.target_id_type == slot_type: return slot + if slot_id and slot.identifier == slot_id: + return slot + # fall back to creating a slot if the type is supplied + if slot_type: + action.slots.new(slot_type, slot_id if slot_id else slot_type.capitalize()) return None @@ -2136,21 +2204,15 @@ def set_action_slot(obj, action, slot=None): the first action slot with the matching slot_type""" if obj and action and B440(): if slot: - try: - obj.animation_data.action_slot = slot - except Exception as e: - log_error(f"Unable to set action slot {action} / {slot}", e) + obj.animation_data.action_slot = slot return True else: slot_type = get_slot_type_for(obj) - slot = get_action_slot(action, slot_type) + slot = get_action_slot(action, slot_type=slot_type) if slot: - try: - obj.animation_data.action_slot = slot - except: - log_error(f"Unable to set action slot by type: {slot_type} / {action} / {slot}") - return False - return True + obj.animation_data.action_slot = slot + return True + return False def safe_get_action(obj) -> bpy.types.Action: @@ -2178,17 +2240,15 @@ def safe_get_action_slot(obj) -> bpy.types.Action: def safe_set_action(obj, action, create=True, slot=None): result = False if obj: - try: + #try: if create and not obj.animation_data: obj.animation_data_create() if obj.animation_data: obj.animation_data.action = action - set_action_slot(obj, action, slot) - result = True - except Exception as e: - action_name = action.name if action else "None" - log_error(f"Unable to set action {action_name} to {obj.name}", e) - result = False + result = set_action_slot(obj, action, slot) + #except Exception as e: + # action_name = action.name if action else "None" + # log_error(f"Unable to set action {action_name} to {obj.name}", e) return result @@ -2277,7 +2337,7 @@ def get_action_channelbag(action: bpy.types.Action, slot=None, slot_type=None): else: strip = layer.strips[0] if slot_type and not slot: - slot = get_action_slot(action, slot_type) + slot = get_action_slot(action, slot_type=slot_type) if slot: channelbag = strip.channelbag(slot, ensure=True) if channelbag: @@ -2445,6 +2505,9 @@ def B440(): def B500(): return is_blender_version("5.0.0") +def B510(): + return is_blender_version("5.1.0") + VER_CACHE = {} def is_blender_version(version: str, test = "GTE"): """e.g. is_blender_version("3.0.0", "GTE")""" @@ -2727,7 +2790,61 @@ def reset_shape_keys(objects, exclude=None): key.value = 0.0 -INVALID_EXPORT_CHARACTERS = "`¬!\"£$%^&*()+-=[]{}:@~;'#<>?,./\| " +def ui_two_column_layout(layout: bpy.types.UILayout, widths: tuple, align=False): + factor = widths[0] / (widths[0] + widths[1]) + split = layout.split(factor=factor, align=align) + col_1 = split.column(align=align) + col_2 = split.column(align=align) + return col_1, col_2 + + +def ui_three_column_layout(layout: bpy.types.UILayout, widths: tuple, align=False): + factor_1 = widths[0] / (widths[0] + widths[1] + widths[2]) + factor_2 = widths[1] / (widths[1] + widths[2]) + split_1 = layout.split(factor=factor_1, align=align) + col_1 = split_1.column(align=align) + col_2 = split_1.column(align=align) + split_2 = col_2.split(factor=factor_2, align=align) + col_3 = split_2.column(align=align) + col_4 = split_2.column(align=align) + return col_1, col_3, col_4 + +def ui_four_column_layout(layout: bpy.types.UILayout, widths: tuple, align=False): + factor_1 = widths[0] / (widths[0] + widths[1] + widths[2] + widths[3]) + factor_2 = widths[1] / (widths[1] + widths[2] + widths[3]) + factor_3 = widths[2] / (widths[2] + widths[3]) + split_1 = layout.split(factor=factor_1, align=align) + col_1 = split_1.column(align=align) + col_2 = split_1.column(align=align) + split_2 = col_2.split(factor=factor_2, align=align) + col_3 = split_2.column(align=align) + col_4 = split_2.column(align=align) + split_3 = col_4.split(factor=factor_3, align=align) + col_5 = split_3.column(align=align) + col_6 = split_3.column(align=align) + return col_1, col_3, col_5, col_6 + + +def ui_fake_drop_down(layout: bpy.types.UILayout, label: str, + props: any, prop_name: str, + icon = "TRIA_DOWN", icon_closed = "TRIA_RIGHT", + align="LEFT"): + row = layout.row() + row.alignment="LEFT" + prop_bool_value = getattr(props, prop_name) + if prop_bool_value: + row.prop(props, prop_name, icon=icon, text=label, emboss=False) + else: + row.prop(props, prop_name, icon=icon_closed, text=label, emboss=False) + if icon != "TRIA_DOWN": + if prop_bool_value: + row.prop(props, prop_name, icon="TRIA_DOWN", icon_only=True, emboss=False) + else: + row.prop(props, prop_name, icon="TRIA_RIGHT", icon_only=True, emboss=False) + return prop_bool_value + + +INVALID_EXPORT_CHARACTERS = "`¬!\"£$%^&*()+-=[]{}:@~;'#<>?,./\\| " DIGITS = "0123456789" @@ -3012,7 +3129,7 @@ def get_rl_link_id(obj): return None -def set_rl_object_id(obj, new_id=None): +def set_rl_id(obj, new_id=None): if new_id is None: new_id = generate_random_id(20) if obj: @@ -3025,12 +3142,19 @@ def set_rl_object_id(obj, new_id=None): return new_id -def get_rl_object_id(obj): +def get_rl_id(obj): if object_exists(obj): - if obj.type == "ARMATURE" and "rl_armature_id" in obj: + if "rl_armature_id" in obj: return obj["rl_armature_id"] if "rl_object_id" in obj: return obj["rl_object_id"] + if action_exists(obj): + if "rl_armature_id" in obj: + return obj["rl_armature_id"] + if "rl_object_id" in obj: + return obj["rl_object_id"] + if "rl_rlx_id" in obj: + return obj["rl_rlx_id"] return None @@ -3042,6 +3166,24 @@ def merge(a: list, b: list): return c +def append_if(l: list, *args): + def append_if_item(l, a): + T = type(a) + if T is tuple or T is list: + for b in a: + append_if_item(l, b) + elif a is not None: + l.append(a) + for a in args: + append_if_item(l, a) + + +def list_if(*args) -> list: + l = [] + append_if(l, *args) + return l + + def fix_texture_rel_path(rel_path: str): """Fixes json texture relative path export bug in CC4 when exporting character directly to the root folder of a drive""" @@ -3152,6 +3294,21 @@ def smallest_index(items: list): return index +def any_in(a: list, b: list): + for c in a: + if c in b: + return True + return False + + +def first(a: list): + if a is not None: + for i in a: + if i is not None: + return i + return None + + def safe_free_bake(point_cache): if B320(): with bpy.context.temp_override(point_cache=point_cache): diff --git a/scripts/addons/cc_blender_tools-2_4_0/vars.py b/scripts/addons/cc_blender_tools-2_4_2/vars.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/vars.py rename to scripts/addons/cc_blender_tools-2_4_2/vars.py index ec25126..2856bc6 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/vars.py +++ b/scripts/addons/cc_blender_tools-2_4_2/vars.py @@ -18,11 +18,11 @@ import bpy -VERSION_STRING = "v2.4.0" +VERSION_STRING = "v2.4.2" DEV = False #DEV = True PLUGIN_COMPATIBLE = [ - "2.4.0", + "2.4.1", "2.4.2", ] def set_version_string(bl_info): @@ -136,6 +136,8 @@ ENUM_OBJECT_TYPES = [ ("OCCLUSION_PLUS", "Eye Occlusion Plus", "Eye occlusion plus object"), ("TEARLINE", "Tearline", "Tear line object"), ("TEARLINE_PLUS", "Tearline Plus", "Tear line plus object"), + ("LIGHT", "Light", "Light"), + ("CAMERA", "Camera", "Camera"), ] CHARACTER_GENERATION = { diff --git a/scripts/addons/cc_blender_tools-2_4_0/world.py b/scripts/addons/cc_blender_tools-2_4_2/world.py similarity index 100% rename from scripts/addons/cc_blender_tools-2_4_0/world.py rename to scripts/addons/cc_blender_tools-2_4_2/world.py diff --git a/scripts/addons/cc_blender_tools-2_4_0/wrinkle.py b/scripts/addons/cc_blender_tools-2_4_2/wrinkle.py similarity index 99% rename from scripts/addons/cc_blender_tools-2_4_0/wrinkle.py rename to scripts/addons/cc_blender_tools-2_4_2/wrinkle.py index 0201c4a..d9c912b 100644 --- a/scripts/addons/cc_blender_tools-2_4_0/wrinkle.py +++ b/scripts/addons/cc_blender_tools-2_4_2/wrinkle.py @@ -274,6 +274,7 @@ def add_wrinkle_mappings(mat, node, body_obj, mat_json): def add_wrinkle_node_driver(mat, node, socket_name, obj, expr_macro : str, wrinkle_defs, overall_weight): + source_macro = expr_macro s = expr_macro.find(r"{") if s == -1: utils.log_error(f"No braces in wrinkle macro expression! {expr_macro}") @@ -297,6 +298,10 @@ def add_wrinkle_node_driver(mat, node, socket_name, obj, expr_macro : str, wrink socket: bpy.types.NodeSocket = node.inputs[socket_name] expr_code = f"{WRINKLE_STRENGTH_VAR} * ({expr_macro})" + if len(expr_code) >= 255: + utils.log_error(f"wrinkle driver ({socket_name}, {source_macro}) too long: {expr_code}") + else: + utils.log_info(f"wrinkle driver ({len(expr_code)}) ({socket_name}, {source_macro}) OK: {expr_code}") driver = drivers.make_driver(socket, "default_value", "SCRIPTED", expr_code) # global vars