├── tools ├── fct_init.py ├── fchar_tools_pnl.py └── fchar_tools_op.py ├── property_bake ├── FPB_init.py ├── FPB_property_bake_pnl.py └── FPB_property_bake_op.py ├── cam_rig_auto_visibility ├── cav_init.py ├── cav_frame_handler_pnl.py └── cav_frame_handler_op.py ├── rig_tools ├── frt_init.py ├── fchar_rig_tools_pnl.py ├── rig_update │ ├── frt_rig_updater_pnl.py │ └── frt_bone_list_io.py └── fchar_rig_tools_op.py ├── animation_mover ├── anm_init.py ├── anm_mover_pnl.py └── anm_copy_ops.py ├── README.md ├── driver_generator ├── utility_functions │ ├── fdg_names.py │ └── fdg_driver_utils.py ├── fdg_driver_gen_pnl.py ├── driver_gen_shapes │ ├── fdg_driver_gen_shapes_pnl.py │ ├── fdg_driver_gen_shapes_remove_op.py │ └── fdg_driver_gen_shapes_op.py ├── driver_gen_camera │ ├── fdg_driver_gen_camera_pnl.py │ └── fdg_driver_gen_camera_op.py ├── fdg_init.py ├── driver_gen_angles │ ├── fdg_driver_gen_angles_pnl.py │ ├── fdg_driver_gen_angles_remove_op.py │ └── fdg_driver_gen_angles_op.py └── driver_functions │ └── fdg_driver_functions.py ├── helper_functions.py ├── fchar_alerts_ops.py ├── chr_shader_tools ├── cst_init.py ├── cst_shading_toolbox │ ├── cst_shading_tools_pnl.py │ └── cst_shading_tools_ops.py └── cst_ShaderController │ ├── cst_create_light_empty_op.py │ ├── cst_copy_controller_data_op.py │ ├── cst_remove_props_op.py │ ├── cst_vector_light_selector_op.py │ ├── cst_shader_controller_pnl.py │ ├── cst_add_eye_driver_op.py │ ├── cst_select_shader_controller_op.py │ └── cst_create_shader_controller_op.py ├── mocap_retarget ├── fmr_init.py ├── fmr_settings_ops.py ├── fmr_scale_list_op.py ├── fmr_files_ops.py ├── BoneMap │ └── IKlegFKarm.bmap ├── fmr_scale_list_io.py ├── fmr_retarget_pnl.py └── fmr_retarget_op.py ├── retarget_tools ├── frt_retarget_tools_pnl.py └── frt_retarget_tools_op.py ├── .gitignore ├── __init__.py ├── fchar_settings.py └── updater_ui.py /tools/fct_init.py: -------------------------------------------------------------------------------- 1 | from . import fchar_tools_op, fchar_tools_pnl 2 | 3 | def register(): 4 | fchar_tools_pnl.register() 5 | fchar_tools_op.register() 6 | 7 | def unregister(): 8 | fchar_tools_op.unregister() 9 | fchar_tools_pnl.unregister() -------------------------------------------------------------------------------- /property_bake/FPB_init.py: -------------------------------------------------------------------------------- 1 | from . import FPB_property_bake_op, FPB_property_bake_pnl 2 | 3 | def register(): 4 | FPB_property_bake_op.register() 5 | FPB_property_bake_pnl.register() 6 | 7 | def unregister(): 8 | FPB_property_bake_pnl.unregister() 9 | FPB_property_bake_op.unregister() -------------------------------------------------------------------------------- /cam_rig_auto_visibility/cav_init.py: -------------------------------------------------------------------------------- 1 | from . import cav_frame_handler_op, cav_frame_handler_pnl 2 | def register(): 3 | cav_frame_handler_op.register() 4 | cav_frame_handler_pnl.register() 5 | def unregister(): 6 | cav_frame_handler_pnl.unregister() 7 | cav_frame_handler_op.unregister() 8 | -------------------------------------------------------------------------------- /rig_tools/frt_init.py: -------------------------------------------------------------------------------- 1 | from .rig_update import frt_rig_updater_op 2 | from .rig_update import frt_rig_updater_pnl 3 | 4 | def register(): 5 | frt_rig_updater_pnl.register() 6 | frt_rig_updater_op.register() 7 | 8 | 9 | def unregister(): 10 | 11 | frt_rig_updater_op.unregister() 12 | frt_rig_updater_pnl.unregister() -------------------------------------------------------------------------------- /animation_mover/anm_init.py: -------------------------------------------------------------------------------- 1 | from . import anm_mover_pnl 2 | from . import anm_mover_op 3 | from . import anm_copy_ops 4 | 5 | def register(): 6 | anm_mover_pnl.register() 7 | anm_mover_op.register() 8 | anm_copy_ops.register() 9 | 10 | def unregister(): 11 | anm_copy_ops.unregister() 12 | anm_mover_op.unregister() 13 | anm_mover_pnl.unregister() 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fritzi-characters 2 | A Blender addon which contains a suite of production tools for character rigging. 3 | 4 | # Installation 5 | - Download the .zip file of a release from the Releases Section: https://github.com/elias-schwarze/fritzi-characters/releases 6 | - In Blender User Preferences, go to Addons and Install the .zip file 7 | - IMPORTANT: If updating from an older version of fritzi-characters, uninstall old version first, then restart Blender, then install new version 8 | -------------------------------------------------------------------------------- /driver_generator/utility_functions/fdg_names.py: -------------------------------------------------------------------------------- 1 | empty_cam = "outlines_cam_empty" 2 | empty_head = "Empty.head" 3 | 4 | hidden_controller_bone = "Driven_Props" 5 | visible_controller_bone = "Driver_Controller" 6 | 7 | prop_bias = "bias" 8 | prop_blend = "blend" 9 | prop_blendUpDown = "blendUpDown" 10 | prop_driverSwitch = "driverSwitch" 11 | 12 | prop_angle_x = "angle_x" 13 | prop_angle_z = "angle_z" 14 | prop_shapes_left = "shapesLeft" 15 | prop_shapes_right = "shapesRight" 16 | prop_up_down_normalize = "upDownNormalize" -------------------------------------------------------------------------------- /driver_generator/fdg_driver_gen_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class FDG_PT_DriverGenerator_pnl(bpy.types.Panel): 4 | bl_label = "Driver Generator" 5 | bl_category = "FCHAR" 6 | bl_space_type = "VIEW_3D" 7 | bl_region_type = "UI" 8 | bl_options = {"DEFAULT_CLOSED"} 9 | 10 | def draw(self, context): 11 | 12 | 13 | pass 14 | 15 | 16 | def register(): 17 | bpy.utils.register_class(FDG_PT_DriverGenerator_pnl) 18 | 19 | def unregister(): 20 | bpy.utils.unregister_class(FDG_PT_DriverGenerator_pnl) -------------------------------------------------------------------------------- /tools/fchar_tools_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Panel 3 | 4 | class FCT_PT_Tools_PNL(Panel): 5 | bl_label = "Tools" 6 | bl_category = "FCHAR" 7 | bl_space_type = "VIEW_3D" 8 | bl_region_type = "UI" 9 | bl_options = {"DEFAULT_CLOSED"} 10 | 11 | def draw(self, context): 12 | layout = self.layout 13 | 14 | layout.operator("fct.delete_nla") 15 | 16 | def register(): 17 | bpy.utils.register_class(FCT_PT_Tools_PNL) 18 | 19 | def unregister(): 20 | bpy.utils.unregister_class(FCT_PT_Tools_PNL) 21 | -------------------------------------------------------------------------------- /rig_tools/fchar_rig_tools_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Panel 3 | 4 | class FCHAR_PT_RigTools_pnl(bpy.types.Panel): 5 | bl_label = "Rig Tools" 6 | bl_category = "FCHAR" 7 | bl_space_type = "VIEW_3D" 8 | bl_region_type = "UI" 9 | bl_options = {"DEFAULT_CLOSED"} 10 | 11 | def draw(self, context): 12 | layout = self.layout 13 | col = layout.column() 14 | 15 | col.operator("armature.create_spine_master") 16 | 17 | 18 | 19 | def register(): 20 | bpy.utils.register_class(FCHAR_PT_RigTools_pnl) 21 | 22 | def unregister(): 23 | bpy.utils.unregister_class(FCHAR_PT_RigTools_pnl) -------------------------------------------------------------------------------- /property_bake/FPB_property_bake_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Panel 3 | class FPB_PT_PropertyBake_pnl(Panel): 4 | 5 | bl_label = "Property Baking" 6 | bl_category = "FCHAR" 7 | bl_space_type = "VIEW_3D" 8 | bl_region_type = "UI" 9 | bl_options = {"DEFAULT_CLOSED"} 10 | 11 | def draw(self, context): 12 | 13 | wm = context.window_manager 14 | layout = self.layout 15 | layout.prop_search(wm, "character_rig", bpy.data, "objects", text="Character Rig") 16 | layout.operator("fpb.bake_properties") 17 | 18 | def register(): 19 | bpy.utils.register_class(FPB_PT_PropertyBake_pnl) 20 | 21 | def unregister(): 22 | bpy.utils.unregister_class(FPB_PT_PropertyBake_pnl) -------------------------------------------------------------------------------- /tools/fchar_tools_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | class FCT_OT_DeleteNla_OP(Operator): 6 | bl_idname = "fct.delete_nla" 7 | bl_label = "Delete Selected Strips" 8 | bl_description = "Deletes all selected NLA Strips on only the ACTIVE object." 9 | bl_options = {'REGISTER', 'UNDO'} 10 | 11 | 12 | def execute(self, context): 13 | 14 | obj = context.active_object 15 | for track in obj.animation_data.nla_tracks: 16 | for strip in track.strips: 17 | if strip.select: 18 | track.strips.remove(strip) 19 | 20 | return {'FINISHED'} 21 | 22 | def register(): 23 | bpy.utils.register_class(FCT_OT_DeleteNla_OP) 24 | 25 | def unregister(): 26 | bpy.utils.unregister_class(FCT_OT_DeleteNla_OP) -------------------------------------------------------------------------------- /driver_generator/driver_gen_shapes/fdg_driver_gen_shapes_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.props import PointerProperty 4 | 5 | 6 | class FDG_PT_GenerateShapeDrivers_pnl(bpy.types.Panel): 7 | bl_label = "Add Drivers to Shapekeys" 8 | bl_category = "FCHAR" 9 | bl_space_type = "VIEW_3D" 10 | bl_region_type = "UI" 11 | bl_options = {"DEFAULT_CLOSED"} 12 | bl_parent_id = "FDG_PT_DriverGenerator_pnl" 13 | 14 | bpy.types.WindowManager.face_collection = PointerProperty( 15 | name="Face Collection", type=bpy.types.Collection) 16 | 17 | def draw(self, context): 18 | wm = context.window_manager 19 | layout = self.layout 20 | 21 | layout.prop_search(wm, "character_rig", context.scene, 22 | "objects", text='Armature') 23 | 24 | layout.prop_search(wm, "face_collection", bpy.data, "collections") 25 | 26 | layout.operator("object.generate_shape_drivers") 27 | 28 | layout.operator("object.remove_shape_drivers") 29 | 30 | 31 | def register(): 32 | bpy.utils.register_class(FDG_PT_GenerateShapeDrivers_pnl) 33 | 34 | 35 | def unregister(): 36 | bpy.utils.unregister_class(FDG_PT_GenerateShapeDrivers_pnl) 37 | -------------------------------------------------------------------------------- /helper_functions.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from typing import Iterable, Any, Tuple 3 | 4 | 5 | def signal_last(it:Iterable[Any]) -> Iterable[Tuple[bool, Any]]: 6 | iterable = iter(it) 7 | ret_var = next(iterable) 8 | for val in iterable: 9 | yield False, ret_var 10 | ret_var = val 11 | yield True, ret_var 12 | 13 | def update_dependencies_all_drivers(): 14 | for obj in bpy.data.objects: 15 | if obj.animation_data: 16 | #print(obj.name) 17 | for FCurve in obj.animation_data.drivers: 18 | driver = FCurve.driver 19 | driver.expression += " " 20 | driver.expression = driver.expression[:-1] 21 | 22 | def remove_override(object: bpy.types.ID, property: bpy.types.IDOverrideLibraryProperty): 23 | """Removes the given OverrideLibraryProperty from the Override Library of the given object and sets 24 | it Value to the Value of the linked Reference Object (The original value of the linked Object)""" 25 | full_path = 'object.' + property.rna_path 26 | full_ref_path = 'object.override_library.reference.' + property.rna_path 27 | exec(full_path + ' = ' + full_ref_path) 28 | object.override_library.properties.remove(property) -------------------------------------------------------------------------------- /rig_tools/rig_update/frt_rig_updater_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import PointerProperty 3 | 4 | def poll_armature(self, object): 5 | if object.type == 'ARMATURE': 6 | return True 7 | return False 8 | 9 | class FRT_PT_RigUpdater_PNL(bpy.types.Panel): 10 | bl_label = "Rig Updater" 11 | bl_category = "FCHAR" 12 | bl_space_type = "VIEW_3D" 13 | bl_region_type = "UI" 14 | bl_options = {"DEFAULT_CLOSED"} 15 | bl_parent_id = "FCHAR_PT_RigTools_pnl" 16 | 17 | bpy.types.WindowManager.update_rig = PointerProperty(name="Armature", type=bpy.types.Object, poll=poll_armature) 18 | 19 | def draw(self, context): 20 | layout = self.layout 21 | wm = context.window_manager 22 | layout.prop_search(wm, "update_rig", bpy.data, "objects") 23 | layout.operator("object.update_fritzi_rig") 24 | layout.separator() 25 | layout.label(text="Utils") 26 | layout.operator("object.rig_inverse") 27 | layout.operator("object.update_dependencies") 28 | layout.operator("object.remove_bad_overrides") 29 | 30 | def register(): 31 | bpy.utils.register_class(FRT_PT_RigUpdater_PNL) 32 | 33 | def unregister(): 34 | bpy.utils.unregister_class(FRT_PT_RigUpdater_PNL) -------------------------------------------------------------------------------- /driver_generator/driver_gen_camera/fdg_driver_gen_camera_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import BoolProperty, IntProperty 3 | 4 | 5 | class FDG_PT_CameraUtilities_pnl(bpy.types.Panel): 6 | bl_label = "Manage Camera" 7 | bl_category = "FCHAR" 8 | bl_space_type = "VIEW_3D" 9 | bl_region_type = "UI" 10 | bl_options = {"DEFAULT_CLOSED"} 11 | bl_parent_id = "FDG_PT_DriverGenOutlines_pnl" 12 | 13 | 14 | 15 | def draw(self, context): 16 | wm = context.window_manager 17 | layout = self.layout 18 | 19 | box = layout.box() 20 | 21 | box.prop_search(wm, "camera", context.scene, 22 | "objects", text="Camera") 23 | 24 | box.operator("object.link_camera") 25 | 26 | layout.separator(factor=1.5) 27 | 28 | # box = layout.box() 29 | 30 | # box.label(text="Auto link active camera:") 31 | # 32 | # row = box.row() 33 | # 34 | # if bpy.context.scene.auto_link_toggle: 35 | # row.operator("object.activate_auto_link_camera", depress=True) 36 | # row.operator("object.deactivate_auto_link_camera", depress=False) 37 | # 38 | # else: 39 | # row.operator("object.activate_auto_link_camera", depress=False) 40 | # row.operator("object.deactivate_auto_link_camera", depress=True) 41 | 42 | 43 | def register(): 44 | bpy.utils.register_class(FDG_PT_CameraUtilities_pnl) 45 | 46 | 47 | def unregister(): 48 | bpy.utils.unregister_class(FDG_PT_CameraUtilities_pnl) 49 | -------------------------------------------------------------------------------- /fchar_alerts_ops.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.app.handlers import persistent 3 | 4 | class FCHAR_OT_Alert_Lineart_InFront_Op(bpy.types.Operator): 5 | 6 | bl_idname = "utils.alert_lineart_infront" 7 | bl_label = "Lineart InFront Alert" 8 | bl_description = "Alerts user if Fritzi lineart is not in front" 9 | bl_options = {'INTERNAL'} 10 | 11 | def invoke(self, context, event): 12 | self.report({'WARNING'}, "Fritzi Lineart is NOT in Front!") 13 | return context.window_manager.invoke_props_dialog(self, width=600) 14 | 15 | def draw(self, context): 16 | layout = self.layout 17 | col = layout.column() 18 | col.label(text="Fritzi Lineart is NOT in front!") 19 | col.label(text="Certain lineart features might not be working as expected!") 20 | 21 | def execute(self, context): 22 | return {'FINISHED'} 23 | 24 | @persistent 25 | def alert_lineart_infront_postLoad_handler(dummy): 26 | gp_ob = bpy.context.scene.gp_defaults.gp_object 27 | if not gp_ob: 28 | return 29 | 30 | if not gp_ob.show_in_front: 31 | bpy.ops.utils.alert_lineart_infront('INVOKE_DEFAULT') 32 | 33 | return {'FINISHED'} 34 | 35 | def register(): 36 | bpy.utils.register_class(FCHAR_OT_Alert_Lineart_InFront_Op) 37 | bpy.app.handlers.load_post.append(alert_lineart_infront_postLoad_handler) 38 | 39 | def unregister(): 40 | bpy.app.handlers.load_post.remove(alert_lineart_infront_postLoad_handler) 41 | bpy.utils.unregister_class(FCHAR_OT_Alert_Lineart_InFront_Op) 42 | -------------------------------------------------------------------------------- /cam_rig_auto_visibility/cav_frame_handler_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.props import BoolProperty, PointerProperty 4 | 5 | class CAV_PT_CamVisibility_pnl(bpy.types.Panel): 6 | bl_label = "Auto Cam Visibility" 7 | bl_category = "FCHAR" 8 | bl_space_type = "VIEW_3D" 9 | bl_region_type = "UI" 10 | bl_options = {"DEFAULT_CLOSED"} 11 | 12 | bpy.types.Scene.auto_visibility_toggle = BoolProperty(default=False) 13 | bpy.types.Scene.cameras_collection = PointerProperty(name="Cam Collection", type=bpy.types.Collection) 14 | bpy.types.WindowManager.last_active_camera = PointerProperty(name="Camera", type=bpy.types.Object) 15 | def draw(self, context): 16 | wm = context.window_manager 17 | layout = self.layout 18 | 19 | box = layout.box() 20 | box.label(text="Auto Toggle Camera Rig Visibility:") 21 | row = box.row() 22 | 23 | if bpy.context.scene.auto_visibility_toggle: 24 | row.operator("object.activate_auto_toggle_cam_visibility", depress=True) 25 | row.operator("object.deactivate_auto_toggle_cam_visibility", depress=False) 26 | 27 | else: 28 | row.operator("object.activate_auto_toggle_cam_visibility", depress=False) 29 | row.operator("object.deactivate_auto_toggle_cam_visibility", depress=True) 30 | 31 | box.prop_search(context.scene, "cameras_collection", bpy.data, "collections", text="Cam Collection") 32 | 33 | 34 | def register(): 35 | bpy.utils.register_class(CAV_PT_CamVisibility_pnl) 36 | def unregister(): 37 | bpy.utils.unregister_class(CAV_PT_CamVisibility_pnl) -------------------------------------------------------------------------------- /chr_shader_tools/cst_init.py: -------------------------------------------------------------------------------- 1 | from .cst_ShaderController import cst_create_light_empty_op 2 | from .cst_ShaderController import cst_vector_light_selector_op 3 | 4 | from .cst_ShaderController import cst_add_eye_driver_op 5 | from .cst_ShaderController import cst_create_shader_controller_op 6 | from .cst_ShaderController import cst_select_shader_controller_op 7 | from .cst_ShaderController import cst_remove_props_op 8 | from .cst_ShaderController import cst_shader_controller_pnl 9 | from .cst_ShaderController import cst_copy_controller_data_op 10 | 11 | from .cst_shading_toolbox import cst_shading_tools_pnl 12 | from .cst_shading_toolbox import cst_shading_tools_ops 13 | 14 | def register(): 15 | cst_copy_controller_data_op.register() 16 | cst_add_eye_driver_op.register() 17 | cst_create_shader_controller_op.register() 18 | cst_select_shader_controller_op.register() 19 | cst_remove_props_op.register() 20 | cst_shader_controller_pnl.register() 21 | cst_create_light_empty_op.register() 22 | cst_vector_light_selector_op.register() 23 | cst_shading_tools_ops.register() 24 | cst_shading_tools_pnl.register() 25 | 26 | def unregister(): 27 | cst_shading_tools_pnl.unregister() 28 | cst_shading_tools_ops.unregister() 29 | cst_vector_light_selector_op.unregister() 30 | cst_create_light_empty_op.unregister() 31 | cst_shader_controller_pnl.unregister() 32 | cst_remove_props_op.unregister() 33 | cst_select_shader_controller_op.unregister() 34 | cst_create_shader_controller_op.unregister() 35 | cst_add_eye_driver_op.unregister() 36 | cst_copy_controller_data_op.unregister() 37 | -------------------------------------------------------------------------------- /chr_shader_tools/cst_shading_toolbox/cst_shading_tools_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.props import FloatVectorProperty 4 | class CST_PT_ShadingTools_pnl(bpy.types.Panel): 5 | 6 | bl_label = "Shading Tools" 7 | bl_category = "FCHAR" 8 | bl_space_type = "NODE_EDITOR" 9 | bl_region_type = "UI" 10 | bl_options = {"DEFAULT_CLOSED"} 11 | 12 | bpy.types.WindowManager.shader_color = FloatVectorProperty(name= "Shader Color", subtype= 'COLOR', size=4, default=(0.5, 0.5, 0.5, 1.0)) 13 | 14 | def draw(self, context): 15 | wm = context.window_manager 16 | layout = self.layout 17 | 18 | 19 | layout.operator("settings.choose_shader_path") 20 | layout.operator("settings.choose_eye_shader_path") 21 | layout.operator("cst.link_character_shader") 22 | layout.operator("cst.link_eye_shader") 23 | layout.separator() 24 | #layout.label(text="Skin Shaders") 25 | #layout.operator("cst.create_h1_shader") 26 | #layout.operator("cst.create_h2_shader") 27 | #layout.operator("cst.create_h3_shader") 28 | #layout.operator("cst.create_h4_shader") 29 | #layout.separator() 30 | layout.operator("cst.create_mouthinside_shader") 31 | layout.operator("cst.create_tongue_shader") 32 | layout.separator() 33 | layout.label(text="Cloth Shader") 34 | layout.prop(wm, "shader_color") 35 | layout.operator("cst.create_cloth_shader") 36 | 37 | 38 | def register(): 39 | bpy.utils.register_class(CST_PT_ShadingTools_pnl) 40 | 41 | def unregister(): 42 | bpy.utils.unregister_class(CST_PT_ShadingTools_pnl) -------------------------------------------------------------------------------- /chr_shader_tools/cst_ShaderController/cst_create_light_empty_op.py: -------------------------------------------------------------------------------- 1 | from math import degrees 2 | import bpy 3 | 4 | from bpy.types import Operator 5 | 6 | class CST_OT_CreateLightEmpty_OP(Operator): 7 | bl_idname = "object.create_light_empty" 8 | bl_label = "Create Light Empty" 9 | bl_description = "Creates an empty which can be rotated to set the direction of the light on a character" 10 | bl_options = {"REGISTER", "UNDO"} 11 | 12 | # should only work in object mode 13 | @classmethod 14 | def poll(cls, context): 15 | obj = bpy.context.object 16 | 17 | if obj: 18 | if obj.mode == "OBJECT": 19 | return True 20 | 21 | return False 22 | 23 | def execute(self, context): 24 | 25 | wm = bpy.context.window_manager 26 | 27 | characterName = wm.character_name 28 | 29 | if not characterName or characterName == "": 30 | self.report({'WARNING'}, "Please give a Character Name!") 31 | return {'CANCELLED'} 32 | 33 | lightEmpty = bpy.data.objects.new(characterName + "_lgt_empty", None) 34 | 35 | lightEmpty.empty_display_size = 1 36 | lightEmpty.empty_display_type = 'SINGLE_ARROW' 37 | 38 | 39 | 40 | lightEmpty.rotation_euler = (0.0, -2.3, -1.3) 41 | lightEmpty.location = (0.0, 0.0, 2.0) 42 | 43 | bpy.context.scene.collection.objects.link(lightEmpty) 44 | 45 | wm.light_empty = lightEmpty 46 | 47 | self.report({'INFO'}, "Added Light Empty!") 48 | return {'FINISHED'} 49 | 50 | 51 | def register(): 52 | bpy.utils.register_class(CST_OT_CreateLightEmpty_OP) 53 | 54 | def unregister(): 55 | bpy.utils.unregister_class(CST_OT_CreateLightEmpty_OP) -------------------------------------------------------------------------------- /mocap_retarget/fmr_init.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from . import fmr_retarget_pnl 4 | from . import fmr_retarget_op 5 | from . import fmr_scale_list_op 6 | from . import fmr_files_ops 7 | from . import fmr_settings_ops 8 | 9 | from bpy.props import BoolProperty, PointerProperty, CollectionProperty, IntProperty, StringProperty 10 | from .fmr_retarget_pnl import ScaleList, update_source_rig, update_target_rig 11 | 12 | 13 | def register(): 14 | 15 | fmr_retarget_op.register() 16 | fmr_retarget_pnl.register() 17 | fmr_scale_list_op.register() 18 | fmr_files_ops.register() 19 | fmr_settings_ops.register() 20 | bpy.types.WindowManager.auto_scale_check = BoolProperty(name = "Auto Scale", default = True) 21 | bpy.types.WindowManager.crowd_check = BoolProperty(name = "Crowd Animation", default = False) 22 | bpy.types.WindowManager.source_rig_pointer = PointerProperty(name = "Source Armature", type = bpy.types.Object, update = update_source_rig) 23 | bpy.types.WindowManager.target_rig_pointer = PointerProperty(name = "Target Armature", type = bpy.types.Object, update = update_target_rig) 24 | bpy.types.WindowManager.scale_list = CollectionProperty(name = "Scale List", type=ScaleList) 25 | bpy.types.WindowManager.scale_list_index = IntProperty() 26 | bpy.types.WindowManager.bvh_files = CollectionProperty(name = "File Paths", type = fmr_files_ops.FileList) 27 | bpy.types.WindowManager.bvh_files_index = IntProperty() 28 | bpy.types.WindowManager.char_file_path = StringProperty(subtype='FILE_PATH') 29 | 30 | 31 | def unregister(): 32 | fmr_settings_ops.unregister() 33 | fmr_files_ops.unregister() 34 | fmr_scale_list_op.unregister() 35 | fmr_retarget_pnl.unregister() 36 | fmr_retarget_op.unregister() 37 | 38 | -------------------------------------------------------------------------------- /chr_shader_tools/cst_ShaderController/cst_copy_controller_data_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Operator 3 | 4 | class CST_OT_CopyControllerData_OP(Operator): 5 | bl_idname = "object.copy_controller_data" 6 | bl_label = "Copy Settings to selected" 7 | bl_description = "Copies all Controller settings from the active controller to all selected controllers."# 8 | bl_options = {"REGISTER", "UNDO"} 9 | 10 | props = ['vector_diffuse_mix', 'highlight_amount', 'highlight_color', 'rimlight_amount', 11 | 'rimlight_color', 'advanced_mix_switch', 'advanced_mix_light_amount', 12 | 'advanced_mix_light_color', 'advanced_mix_shadow_amount', 'advanced_mix_shadow_tint', 13 | 'main_color_tint', 'main_color_tint_amount', 'shadow_color_tint', 'shadow_color_tint_amount'] 14 | 15 | @classmethod 16 | def poll(cls, context): 17 | 18 | if not bpy.context.active_object: 19 | return False 20 | 21 | for prop in cls.props: 22 | if prop not in bpy.context.active_object: 23 | return False 24 | 25 | for controller in context.selected_objects: 26 | for prop in cls.props: 27 | if prop not in controller: 28 | 29 | return False 30 | 31 | return True 32 | 33 | def execute(self, context): 34 | 35 | active_controller = context.active_object 36 | selected_controllers = context.selected_objects 37 | 38 | for controller in selected_controllers: 39 | if controller == active_controller: 40 | continue 41 | 42 | for prop in self.props: 43 | controller[prop] = active_controller[prop] 44 | 45 | return {'FINISHED'} 46 | 47 | def register(): 48 | bpy.utils.register_class(CST_OT_CopyControllerData_OP) 49 | 50 | def unregister(): 51 | bpy.utils.unregister_class(CST_OT_CopyControllerData_OP) 52 | -------------------------------------------------------------------------------- /retarget_tools/frt_retarget_tools_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Armature, Panel 4 | from bpy.props import PointerProperty, IntProperty 5 | 6 | 7 | class FDG_PT_Retarget_Panel_pnl(Panel): 8 | bl_label = "Retarget Tools" 9 | bl_category = "FCHAR" 10 | bl_space_type = "VIEW_3D" 11 | bl_region_type = "UI" 12 | bl_options = {"DEFAULT_CLOSED"} 13 | 14 | bpy.types.WindowManager.retargetArmature = PointerProperty( 15 | name="Target Armature", type=Armature) 16 | 17 | bpy.types.WindowManager.mocapSourceArmature = PointerProperty( 18 | name="Mocap Source Armature", type=Armature) 19 | 20 | bpy.types.WindowManager.frame_start = IntProperty( 21 | name='Start:', default=0, min=0, step=1) 22 | 23 | bpy.types.WindowManager.frame_end = IntProperty( 24 | name='End:', default=500, min=0, step=1) 25 | 26 | #bpy.types.Scene.num7 = IntProperty(name='Frame Start', default= 0, min= 0, step=1) 27 | #bpy.types.Scene.num8 = IntProperty(name='Frame End', default= 500, min= 0, step=1) 28 | 29 | def draw(self, context): 30 | layout = self.layout 31 | 32 | box = layout.box() 33 | box.prop_search(data=bpy.context.window_manager, property="retargetArmature", search_data=bpy.data, 34 | search_property="armatures", text="Character Target Armature") 35 | 36 | box = layout.box() 37 | box.prop_search(data=bpy.context.window_manager, property="mocapSourceArmature", search_data=bpy.data, 38 | search_property="armatures", text="Mocap Source Armature") 39 | 40 | row = layout.row() 41 | row.prop(bpy.context.window_manager, "frame_start") 42 | row.prop(bpy.context.window_manager, "frame_end") 43 | 44 | col = layout.column() 45 | col.operator("object.retarget") 46 | 47 | 48 | def register(): 49 | bpy.utils.register_class(FDG_PT_Retarget_Panel_pnl) 50 | 51 | 52 | def unregister(): 53 | bpy.utils.unregister_class(FDG_PT_Retarget_Panel_pnl) 54 | -------------------------------------------------------------------------------- /driver_generator/fdg_init.py: -------------------------------------------------------------------------------- 1 | from .driver_functions import fdg_driver_functions 2 | 3 | from .driver_gen_angles import fdg_driver_gen_angles_op 4 | from .driver_gen_angles import fdg_driver_gen_angles_pnl 5 | from .driver_gen_angles import fdg_driver_gen_angles_remove_op 6 | 7 | from .driver_gen_shapes import fdg_driver_gen_shapes_op 8 | from .driver_gen_shapes import fdg_driver_gen_shapes_pnl 9 | from .driver_gen_shapes import fdg_driver_gen_shapes_remove_op 10 | 11 | from .driver_gen_camera import fdg_driver_gen_camera_op 12 | from .driver_gen_camera import fdg_driver_gen_camera_pnl 13 | from . import fdg_driver_gen_pnl 14 | 15 | from .driver_gen_outlines import fdg_driver_gen_outlines_ops 16 | from .driver_gen_outlines import fdg_driver_gen_preview_outlines_ops 17 | from .driver_gen_outlines import fdg_driver_gen_outlines_pnl 18 | 19 | def register(): 20 | fdg_driver_gen_pnl.register() 21 | 22 | fdg_driver_functions.register() 23 | 24 | fdg_driver_gen_angles_op.register() 25 | fdg_driver_gen_angles_remove_op.register() 26 | fdg_driver_gen_angles_pnl.register() 27 | 28 | fdg_driver_gen_shapes_op.register() 29 | fdg_driver_gen_shapes_remove_op.register() 30 | fdg_driver_gen_shapes_pnl.register() 31 | 32 | fdg_driver_gen_outlines_ops.register() 33 | fdg_driver_gen_preview_outlines_ops.register() 34 | fdg_driver_gen_outlines_pnl.register() 35 | 36 | fdg_driver_gen_camera_op.register() 37 | fdg_driver_gen_camera_pnl.register() 38 | 39 | def unregister(): 40 | fdg_driver_gen_camera_pnl.unregister() 41 | fdg_driver_gen_camera_op.unregister() 42 | 43 | fdg_driver_gen_outlines_pnl.unregister() 44 | fdg_driver_gen_preview_outlines_ops.unregister() 45 | fdg_driver_gen_outlines_ops.unregister() 46 | 47 | fdg_driver_gen_shapes_pnl.unregister() 48 | fdg_driver_gen_shapes_remove_op.unregister() 49 | fdg_driver_gen_shapes_op.unregister() 50 | 51 | fdg_driver_gen_angles_pnl.unregister() 52 | fdg_driver_gen_angles_remove_op.unregister() 53 | fdg_driver_gen_angles_op.unregister() 54 | 55 | fdg_driver_functions.unregister() 56 | 57 | fdg_driver_gen_pnl.unregister() -------------------------------------------------------------------------------- /driver_generator/driver_gen_shapes/fdg_driver_gen_shapes_remove_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Operator 3 | 4 | class FDG_OT_RemoveShapeDrivers_Op(Operator): 5 | bl_idname = "object.remove_shape_drivers" 6 | bl_label = "Remove Drivers from Shapekeys" 7 | bl_description = "Remove all drivers from all Shapekeys ending with .L or .R on Meshes or Lattices in the given collection or child collections" 8 | bl_options = {'REGISTER', 'UNDO'} 9 | 10 | def execute(self, context): 11 | 12 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 13 | 14 | wm = bpy.context.window_manager 15 | 16 | collection = wm.face_collection 17 | 18 | arma = wm.character_rig 19 | 20 | if arma is None: 21 | self.report({'WARNING'}, "Please select the Armature!") 22 | 23 | if collection is None: 24 | self.report({'WARNING'}, "Please select the Collection!") 25 | 26 | self.remove_shape_drivers(collection, arma) 27 | 28 | self.report({'INFO'}, "Removed Shapekey Drivers!") 29 | return {'FINISHED'} 30 | 31 | 32 | def remove_shape_drivers(self, collection, armature): 33 | """Removes all Drivers from Shapekeys ending with .L or .R on all Meshes or Lattices in the given collection, and recursively in all child collections""" 34 | for child in collection.children: 35 | self.remove_shape_drivers(child, armature) 36 | 37 | for obj in collection.objects: 38 | if obj.type == 'MESH' or obj.type == 'LATTICE': 39 | 40 | if obj.data.shape_keys is None: 41 | continue 42 | for key in obj.data.shape_keys.key_blocks: 43 | 44 | #print(key.name) 45 | 46 | if key.name.endswith(".L") or key.name.endswith(".R"): 47 | key.driver_remove("value") 48 | 49 | for area in bpy.context.screen.areas: 50 | area.tag_redraw() 51 | 52 | 53 | def register(): 54 | bpy.utils.register_class(FDG_OT_RemoveShapeDrivers_Op) 55 | 56 | 57 | def unregister(): 58 | bpy.utils.unregister_class(FDG_OT_RemoveShapeDrivers_Op) -------------------------------------------------------------------------------- /driver_generator/driver_gen_angles/fdg_driver_gen_angles_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.props import BoolProperty, PointerProperty 4 | from bpy.props import StringProperty 5 | 6 | 7 | class FDG_PT_GenerateAngleDrivers_pnl(bpy.types.Panel): 8 | bl_label = "Angle Driver Generator" 9 | bl_category = "FCHAR" 10 | bl_space_type = "VIEW_3D" 11 | bl_region_type = "UI" 12 | bl_options = {"DEFAULT_CLOSED"} 13 | bl_parent_id = "FDG_PT_DriverGenerator_pnl" 14 | 15 | bpy.types.WindowManager.character_prefix = StringProperty(name="Character Prefix") 16 | 17 | bpy.types.WindowManager.character_rig = PointerProperty( 18 | name="Armature", type=bpy.types.Object) 19 | bpy.types.WindowManager.character_head_bone = StringProperty(name="Head Bone") 20 | bpy.types.WindowManager.camera = PointerProperty( 21 | name="Camera", type=bpy.types.Object) 22 | 23 | 24 | def draw(self, context): 25 | wm = context.window_manager 26 | layout = self.layout 27 | 28 | # Create a Text Field to enter the current Characters prefix, so the empties can be named with them. 29 | layout.prop(wm, "character_prefix") 30 | 31 | # Create Search field for first Object (Head) and if this object is an armature create search field for its 32 | # bones 33 | box = layout.box() 34 | box.prop_search(wm, "character_rig", context.scene, 35 | "objects", text="Armature") 36 | ob = wm.character_rig 37 | if ob is not None: 38 | if ob.type == 'ARMATURE': 39 | box.prop_search(wm, "character_head_bone", ob.data, "bones") 40 | 41 | # Create Search field for second Object (Camera) 42 | box = layout.box() 43 | box.prop_search(wm, "camera", context.scene, 44 | "objects", text="Camera") 45 | ob = wm.camera 46 | 47 | row = layout.row() 48 | row.operator("object.generate_drivers") 49 | row.operator("object.remove_drivers") 50 | 51 | 52 | def register(): 53 | bpy.utils.register_class(FDG_PT_GenerateAngleDrivers_pnl) 54 | 55 | 56 | def unregister(): 57 | bpy.utils.unregister_class(FDG_PT_GenerateAngleDrivers_pnl) 58 | -------------------------------------------------------------------------------- /mocap_retarget/fmr_settings_ops.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Operator 3 | from bpy_extras.io_utils import ImportHelper 4 | from ..fchar_settings import Settings 5 | import os 6 | 7 | 8 | class FMR_OT_ChoosePerforcePath_OP(Operator, ImportHelper): 9 | bl_idname = "settings.choose_perforce_path" 10 | bl_label = "Select Perforce DIR" 11 | bl_description = "Select the Main Directory of the Fritzi Perforce on your Computer" 12 | 13 | def execute(self, context): 14 | settings = Settings() 15 | settings.set_setting("perforce_path", self.filepath) 16 | if not (os.path.isdir(self.filepath) and os.path.isdir(os.path.join(self.filepath, "080_scenes")) and os.path.isdir(os.path.join(self.filepath, "075_capture"))): 17 | self.report({'WARNING'}, "NO VALID PERFORCE DIRECTORY FOUND!") 18 | bpy.ops.settings.choose_perforce_path('INVOKE_DEFAULT') 19 | else: 20 | self.report({'INFO'}, "Perforce Directory set!") 21 | 22 | 23 | return {'FINISHED'} 24 | 25 | class FMR_OT_Test_OP(Operator): 26 | bl_idname = "settings.test" 27 | bl_label = "Test Choosing" 28 | 29 | def execute(self,context): 30 | settings = Settings() 31 | filepath = settings.get_setting("perforce_path") 32 | 33 | if not (os.path.isdir(filepath) and os.path.isdir(os.path.join(filepath, "080_scenes")) and os.path.isdir(os.path.join(filepath, "075_capture"))): 34 | 35 | 36 | bpy.ops.settings.choose_perforce_path('INVOKE_DEFAULT') 37 | self.report({'WARNING'}, "Set Perforce Path first!") 38 | return {'FINISHED'} 39 | else: 40 | #Select BVHS and then create filestructure and retarget 41 | 42 | #check if bvhs are already loaded otherwise exec selection and at the end of loading exec this operator again 43 | bpy.ops.retarget.select_bvhs('INVOKE_DEFAULT') 44 | #after each retargeted bvh delete it from the bvh list (Maybe show bvh list in UI) 45 | # 46 | 47 | 48 | 49 | return {'FINISHED'} 50 | 51 | 52 | def register(): 53 | bpy.utils.register_class(FMR_OT_ChoosePerforcePath_OP) 54 | bpy.utils.register_class(FMR_OT_Test_OP) 55 | 56 | def unregister(): 57 | bpy.utils.unregister_class(FMR_OT_Test_OP) 58 | bpy.utils.unregister_class(FMR_OT_ChoosePerforcePath_OP) 59 | -------------------------------------------------------------------------------- /chr_shader_tools/cst_ShaderController/cst_remove_props_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | from ...driver_generator.utility_functions.fdg_driver_utils import remove_property 6 | 7 | class CST_OT_removeProps_OP(Operator): 8 | bl_idname = "object.remove_props" 9 | bl_label = "Remove Properties" 10 | bl_description = "Removes Drivers and Properties from all Objects in the given collection including old obsolete ones." 11 | bl_options = {"REGISTER", "UNDO"} 12 | 13 | propNames = ["highlight_col", "rimlight_col", "AdvMixLightCol", "AdvMixShadowTint", "mix_values", 14 | "highlight_rimlight_mix_values", "AdvMixFactor", "advMixLightAmount", "AdvMixShadowTint", "highlight_amount", 15 | "rimlight_amount", "vector_diffuse_mix", "highlight", "rimlight", "adv_mix_light", "adv_mix_shadow", "main_tint", "light_rotation", "shadow_tint"] 16 | 17 | # should only work in object mode 18 | @classmethod 19 | def poll(cls, context): 20 | obj = bpy.context.object 21 | 22 | if obj: 23 | if obj.mode == "OBJECT": 24 | return True 25 | 26 | return False 27 | 28 | def execute(self, context): 29 | 30 | wm = bpy.context.window_manager 31 | 32 | characterCollection = wm.character_collection 33 | 34 | if characterCollection is None: 35 | self.report({'WARNING'}, "Please select a collection with the Character!") 36 | return {'CANCELLED'} 37 | 38 | self.remove_props_recursive(characterCollection) 39 | 40 | for area in bpy.context.screen.areas: 41 | area.tag_redraw() 42 | 43 | return {'FINISHED'} 44 | 45 | 46 | def remove_props_recursive(self, collection): 47 | for child in collection.children: 48 | self.remove_props_recursive(child) 49 | 50 | for object in collection.objects: 51 | if object.type =='MESH': 52 | for name in self.propNames: 53 | object.driver_remove('["' + name + '"]', 0) 54 | object.driver_remove('["' + name + '"]', 1) 55 | object.driver_remove('["' + name + '"]', 2) 56 | object.driver_remove('["' + name + '"]', 3) 57 | remove_property(object, name) 58 | 59 | def register(): 60 | bpy.utils.register_class(CST_OT_removeProps_OP) 61 | 62 | def unregister(): 63 | bpy.utils.unregister_class(CST_OT_removeProps_OP) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | /fritzi-toolbox-updater/ 132 | fritzi-toolbox_updater/ 133 | fritzi-characters_updater/fritzi-characters_updater_status.json 134 | .VSCodeCounter/ 135 | *.json 136 | -------------------------------------------------------------------------------- /driver_generator/driver_gen_camera/fdg_driver_gen_camera_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | from bpy.app.handlers import persistent 6 | from ..utility_functions.fdg_driver_utils import append_function_unique 7 | from ..utility_functions.fdg_driver_utils import remove_function 8 | from ..utility_functions.fdg_driver_utils import parent_objects, link_camera, add_auto_link_handler, remove_auto_link_handler 9 | from ..utility_functions import fdg_names 10 | 11 | 12 | class FDG_OT_LinkCamera_Op(Operator): 13 | bl_idname = "object.link_camera" 14 | bl_label = "Link this Camera" 15 | bl_description = "Link all Generated Drivers to this Camera" 16 | bl_options = {"REGISTER", "UNDO"} 17 | 18 | def execute(self, context): 19 | 20 | 21 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 22 | 23 | wm = bpy.context.window_manager 24 | 25 | cam = wm.camera 26 | 27 | if cam is None: 28 | self.report({'WARNING'}, "Please select the Camera!") 29 | return {'CANCELLED'} 30 | 31 | if cam.type != 'CAMERA': 32 | self.report({'WARNING'}, "Please select a Camera!") 33 | 34 | link_camera(cam) 35 | 36 | self.report({'INFO'}, "Camera Linked!") 37 | return {'FINISHED'} 38 | 39 | class FDG_OT_ActivateAutoLinkCamera_Op(Operator): 40 | bl_idname = "object.activate_auto_link_camera" 41 | bl_label = "Auto Link On" 42 | bl_description = "Auto link camera toggle on" 43 | bl_options = {'REGISTER', 'UNDO'} 44 | 45 | def execute(self, context): 46 | 47 | add_auto_link_handler() 48 | 49 | return {'FINISHED'} 50 | 51 | class FDG_OT_DeactivateAutoLinkCamera_Op(Operator): 52 | bl_idname = "object.deactivate_auto_link_camera" 53 | bl_label = "Auto Link Off" 54 | bl_description = "Auto link camera toggle off" 55 | bl_options = {'REGISTER', 'UNDO'} 56 | 57 | def execute(self, context): 58 | 59 | remove_auto_link_handler() 60 | 61 | return {'FINISHED'} 62 | 63 | 64 | @persistent 65 | def frame_change_handler(dummy): 66 | """Handler which gathers the active camera and links the camera empty to it""" 67 | camera = bpy.context.scene.camera 68 | link_camera(camera) 69 | 70 | 71 | def register(): 72 | bpy.utils.register_class(FDG_OT_LinkCamera_Op) 73 | bpy.utils.register_class(FDG_OT_ActivateAutoLinkCamera_Op) 74 | bpy.utils.register_class(FDG_OT_DeactivateAutoLinkCamera_Op) 75 | 76 | def unregister(): 77 | bpy.utils.unregister_class(FDG_OT_DeactivateAutoLinkCamera_Op) 78 | bpy.utils.unregister_class(FDG_OT_ActivateAutoLinkCamera_Op) 79 | bpy.utils.unregister_class(FDG_OT_LinkCamera_Op) -------------------------------------------------------------------------------- /driver_generator/driver_gen_shapes/fdg_driver_gen_shapes_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | from ..utility_functions.fdg_driver_utils import add_var 6 | from..utility_functions import fdg_names 7 | 8 | 9 | class FDG_OT_GenerateShapeDrivers_Op(Operator): 10 | bl_idname = "object.generate_shape_drivers" 11 | bl_label = "Add Drivers to Shapekeys" 12 | bl_description = "Adds drivers to all shapekeys with either suffix .L or .R on objects in the given collection" 13 | bl_options = {"REGISTER", "UNDO"} 14 | 15 | def execute(self, context): 16 | 17 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 18 | 19 | wm = bpy.context.window_manager 20 | 21 | collection = wm.face_collection 22 | 23 | arma = wm.character_rig 24 | 25 | if arma is None: 26 | self.report({'WARNING'}, "Please select the Armature!") 27 | 28 | if collection is None: 29 | self.report({'WARNING'}, "Please select the Collection!") 30 | 31 | self.add_shape_drivers(collection, arma) 32 | 33 | self.report({'INFO'}, "Created Shapekey Drivers!") 34 | return {'FINISHED'} 35 | 36 | def add_shape_drivers(self, collection, armature): 37 | """Adds Drivers to all shapekeys which end with .L or .R on all Meshes and Lattices in the given collection, and recursively in all child collections""" 38 | for child in collection.children: 39 | self.add_shape_drivers(child, armature) 40 | 41 | for obj in collection.objects: 42 | if obj.type == 'MESH' or obj.type == 'LATTICE': 43 | 44 | if obj.data.shape_keys is None: 45 | continue 46 | 47 | for key in obj.data.shape_keys.key_blocks: 48 | 49 | if key.name.endswith(".L"): 50 | 51 | driver = key.driver_add("value").driver 52 | add_var(driver, armature, "shapesLeft", type='SINGLE_PROP', 53 | rna_data_path='pose.bones["' + fdg_names.hidden_controller_bone + '"]["' + fdg_names.prop_shapes_left + '"]') 54 | driver.expression = "shapesLeft" 55 | 56 | if key.name.endswith(".R"): 57 | 58 | driver = key.driver_add("value").driver 59 | add_var(driver, armature, "shapesRight", type='SINGLE_PROP', 60 | rna_data_path='pose.bones["' + fdg_names.hidden_controller_bone + '"]["' + fdg_names.prop_shapes_right + '"]') 61 | driver.expression = "shapesRight" 62 | 63 | 64 | def register(): 65 | bpy.utils.register_class(FDG_OT_GenerateShapeDrivers_Op) 66 | 67 | 68 | def unregister(): 69 | bpy.utils.unregister_class(FDG_OT_GenerateShapeDrivers_Op) 70 | -------------------------------------------------------------------------------- /rig_tools/rig_update/frt_bone_list_io.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import bpy 4 | from . import bone_list 5 | 6 | class BoneList(object): 7 | 8 | _bone_list_path = os.path.join(os.path.dirname(__file__), "frt_bone_list.json") 9 | _bone_list = {} 10 | _default_bone_list = bone_list.bone_list 11 | 12 | 13 | def __init__(self): 14 | self.load_list(self._bone_list_path) 15 | 16 | def load_list(self, path): 17 | try: 18 | if os.path.isfile(path): 19 | f = open(path, 'r') 20 | self._bone_list = json.load(f) 21 | f.close() 22 | if not self._bone_list: 23 | self._bone_list = self._default_bone_list 24 | self.write_list(self._bone_list_path) 25 | else: 26 | self._bone_list = self._default_bone_list 27 | self.write_list(self._bone_list_path) 28 | 29 | except: 30 | self._bone_list = self._default_bone_list 31 | self.write_list(self._bone_list_path) 32 | 33 | return 34 | 35 | def write_list(self, path): 36 | try: 37 | with open(path, 'w') as f: 38 | data_out = json.dump(self._bone_list, f, indent=4, sort_keys=False) 39 | 40 | except Exception as e: 41 | print("exception") 42 | print(e) 43 | pass 44 | 45 | return 46 | 47 | def get_bone_list(self): 48 | if "bone_list" in self._bone_list: 49 | return self._bone_list["bone_list"] 50 | 51 | else: 52 | self._bone_list["bone_list"] = self._default_bone_list["bone_list"] 53 | self.write_list(self._bone_list_path) 54 | return self._bone_list["bone_list"] 55 | 56 | def get_constraint_update_list(self): 57 | if "constraint_update_list" in self._bone_list: 58 | return self._bone_list["constraint_update_list"] 59 | 60 | else: 61 | self._bone_list["constraint_update_list"] = self._default_bone_list["constraint_update_list"] 62 | self.write_list(self._bone_list_path) 63 | return self._bone_list["constraint_update_list"] 64 | 65 | def get_constraint_add_list(self): 66 | if "constraint_add_list" in self._bone_list: 67 | return self._bone_list["constraint_add_list"] 68 | 69 | else: 70 | self._bone_list["constraint_add_list"] = self._default_bone_list["constraint_add_list"] 71 | self.write_list(self._bone_list_path) 72 | return self._bone_list["constraint_add_list"] 73 | 74 | def get_bone_visualization_list(self): 75 | if "bone_visualization_list" in self._bone_list: 76 | return self._bone_list["bone_visualization_list"] 77 | 78 | else: 79 | self._bone_list["bone_visualization_list"] = self._default_bone_list["bone_visualization_list"] 80 | self.write_list(self._bone_list_path) 81 | return self._bone_list["bone_visualization_list"] -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # This program is free software; you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation; either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, but 7 | # WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 9 | # General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program. If not, see . 13 | 14 | import bpy 15 | 16 | from . import addon_updater_ops 17 | from . import updater_ui 18 | 19 | from .driver_generator import fdg_init 20 | from .retarget_tools import frt_retarget_tools_op, frt_retarget_tools_pnl 21 | from .chr_shader_tools import cst_init 22 | from .mocap_retarget import fmr_init 23 | from .rig_tools import fchar_rig_tools_op, fchar_rig_tools_pnl 24 | from .cam_rig_auto_visibility import cav_init 25 | from .animation_mover import anm_init 26 | from .rig_tools import frt_init 27 | from .property_bake import FPB_init 28 | from .tools import fct_init 29 | from .driver_generator.utility_functions.fdg_driver_utils import add_auto_link_handler, add_load_handler, remove_load_handler 30 | from . import fchar_alerts_ops 31 | 32 | bl_info = { 33 | "name": "fritziCharacters", 34 | "author": "Michael Schieber, Sophie Fink, Elias Schwarze", 35 | "description": "A suite of tools for character rigging in production", 36 | "blender": (3, 3, 0), 37 | "version": (1, 7, 3), 38 | "location": "3D Viewport > Properties panel (N) > FCHAR Tabs", 39 | "category": "Rigging" 40 | } 41 | 42 | classes = ( 43 | updater_ui.DemoPreferences, 44 | ) 45 | 46 | 47 | def register(): 48 | addon_updater_ops.register(bl_info) 49 | 50 | for cls in classes: 51 | addon_updater_ops.make_annotations(cls) # Avoid blender 2.8 warnings. 52 | bpy.utils.register_class(cls) 53 | 54 | fdg_init.register() 55 | cst_init.register() 56 | fmr_init.register() 57 | frt_retarget_tools_op.register() 58 | frt_retarget_tools_pnl.register() 59 | fchar_rig_tools_op.register() 60 | fchar_rig_tools_pnl.register() 61 | cav_init.register() 62 | anm_init.register() 63 | frt_init.register() 64 | FPB_init.register() 65 | fct_init.register() 66 | fchar_alerts_ops.register() 67 | add_auto_link_handler() 68 | add_load_handler() 69 | 70 | 71 | 72 | 73 | def unregister(): 74 | remove_load_handler() 75 | fchar_alerts_ops.unregister() 76 | fct_init.unregister() 77 | FPB_init.unregister() 78 | frt_init.unregister() 79 | anm_init.unregister() 80 | cav_init.unregister() 81 | fchar_rig_tools_pnl.unregister() 82 | fchar_rig_tools_op.unregister() 83 | frt_retarget_tools_pnl.unregister() 84 | frt_retarget_tools_op.unregister() 85 | fmr_init.unregister() 86 | cst_init.unregister() 87 | fdg_init.unregister() 88 | 89 | for cls in reversed(classes): 90 | bpy.utils.unregister_class(cls) 91 | 92 | addon_updater_ops.unregister() 93 | -------------------------------------------------------------------------------- /cam_rig_auto_visibility/cav_frame_handler_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | from bpy.app.handlers import persistent 6 | 7 | from ..driver_generator.utility_functions.fdg_driver_utils import append_function_unique, remove_function 8 | 9 | class CAV_OT_ActivateAutoToggleCamVisibility_OP(Operator): 10 | bl_idname = "object.activate_auto_toggle_cam_visibility" 11 | bl_label = "On" 12 | bl_description = "Enable Auto Toggle Camera Rig Visibilities on Active Camera change." 13 | bl_options = {'REGISTER', 'UNDO'} 14 | 15 | def execute(self, context): 16 | bpy.context.scene.auto_visibility_toggle = True 17 | cam_collection = bpy.context.scene.cameras_collection 18 | if cam_collection is None: 19 | bpy.context.scene.cameras_collection = bpy.data.collections.get("CAMERAS") 20 | append_function_unique(bpy.app.handlers.frame_change_pre, on_frame_change_visibility_handler) 21 | 22 | return {'FINISHED'} 23 | 24 | class CAV_OT_DeactivateAutoToggleCamVisibility_OP(Operator): 25 | bl_idname = "object.deactivate_auto_toggle_cam_visibility" 26 | bl_label = "Off" 27 | bl_description = "Disable Auto Toggle Camera Rig Visibilities on Active Camera change." 28 | bl_options = {'REGISTER', 'UNDO'} 29 | 30 | 31 | def execute(self, context): 32 | bpy.context.scene.auto_visibility_toggle = False 33 | remove_function(bpy.app.handlers.frame_change_pre, on_frame_change_visibility_handler) 34 | 35 | cam_collections = bpy.data.collections 36 | 37 | if bpy.context.scene.cameras_collection is not None: 38 | cam_collections = bpy.context.scene.cameras_collection.children 39 | 40 | for c in cam_collections: 41 | if c.name.startswith('CAM.') or c.name.startswith('CAM_'): 42 | c.hide_viewport = False 43 | 44 | return {'FINISHED'} 45 | 46 | @persistent 47 | def on_frame_change_visibility_handler(dummy): 48 | """Handler which disables all camera collections in the viewport, which do not correspond to the active camera""" 49 | #print("Yay") 50 | last_active_camera = bpy.context.window_manager.last_active_camera 51 | active_camera = bpy.context.scene.camera 52 | #if last_active_camera is None: 53 | # bpy.context.window_manager.last_active_camera = active_camera 54 | 55 | if active_camera is last_active_camera: 56 | return 57 | bpy.context.window_manager.last_active_camera = active_camera 58 | #print(active_camera.name) 59 | cam_collections = bpy.data.collections 60 | 61 | if bpy.context.scene.cameras_collection is not None: 62 | cam_collections = bpy.context.scene.cameras_collection.children 63 | 64 | for c in cam_collections: 65 | if c.name.startswith('CAM.') or c.name.startswith('CAM_'): 66 | 67 | if c.name.endswith(active_camera.name[-3:]): 68 | c.hide_viewport = False 69 | else: 70 | c.hide_viewport = True 71 | #print(c.name) 72 | 73 | 74 | def register(): 75 | bpy.utils.register_class(CAV_OT_ActivateAutoToggleCamVisibility_OP) 76 | bpy.utils.register_class(CAV_OT_DeactivateAutoToggleCamVisibility_OP) 77 | 78 | 79 | def unregister(): 80 | bpy.utils.unregister_class(CAV_OT_DeactivateAutoToggleCamVisibility_OP) 81 | bpy.utils.unregister_class(CAV_OT_ActivateAutoToggleCamVisibility_OP) 82 | -------------------------------------------------------------------------------- /driver_generator/driver_gen_angles/fdg_driver_gen_angles_remove_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | from ..utility_functions import fdg_names 6 | 7 | 8 | class FDG_OT_RemoveDrivers_Op(Operator): 9 | bl_idname = "object.remove_drivers" 10 | bl_label = "Remove Drivers" 11 | bl_description = "Remove all drivers, controllers and empties which where created for the 2D Fakes, with the excetion of the shapekey drivers. To remove them use the remove option in the shapekey Panel." 12 | bl_options = {'REGISTER', 'UNDO'} 13 | 14 | def execute(self, context): 15 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 16 | 17 | wm = bpy.context.window_manager 18 | 19 | prefix = wm.character_prefix 20 | arma = wm.character_rig 21 | head_bone_name = wm.character_head_bone 22 | cam = wm.camera 23 | 24 | if prefix == "": 25 | self.report({'WARNING'}, "Please enter a Character Prefix!") 26 | return {'CANCELLED'} 27 | 28 | if arma is None: 29 | self.report({'WARNING'}, "Please select the Armature!") 30 | return {'CANCELLED'} 31 | 32 | if head_bone_name == "": 33 | self.report({'WARNING'}, "Please select the Head Bone!") 34 | return {'CANCELLED'} 35 | 36 | if cam is None: 37 | self.report({'WARNING'}, "Please select the Camera!") 38 | return {'CANCELLED'} 39 | 40 | hidden_controller_pose_bone = arma.pose.bones.get(fdg_names.hidden_controller_bone) 41 | 42 | if hidden_controller_pose_bone is not None: 43 | 44 | hidden_controller_pose_bone.driver_remove('["' + fdg_names.prop_angle_x + '"]') 45 | hidden_controller_pose_bone.driver_remove('["' + fdg_names.prop_angle_z + '"]') 46 | hidden_controller_pose_bone.driver_remove('["' + fdg_names.prop_shapes_left + '"]') 47 | hidden_controller_pose_bone.driver_remove('["' + fdg_names.prop_shapes_right + '"]') 48 | hidden_controller_pose_bone.driver_remove('["' + fdg_names.prop_up_down_normalize + '"]') 49 | 50 | bpy.ops.object.mode_set(mode='EDIT', toggle=False) 51 | 52 | hidden_controller_edit_bone = arma.data.edit_bones.get(fdg_names.hidden_controller_bone) 53 | 54 | if hidden_controller_edit_bone is not None: 55 | arma.data.edit_bones.remove(hidden_controller_edit_bone) 56 | 57 | visible_controller_edit_bone = arma.data.edit_bones.get(fdg_names.visible_controller_bone) 58 | 59 | if visible_controller_edit_bone is not None: 60 | arma.data.edit_bones.remove(visible_controller_edit_bone) 61 | 62 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 63 | 64 | empty_cam = bpy.data.objects.get(fdg_names.empty_cam) 65 | 66 | if empty_cam is not None: 67 | bpy.data.objects.remove(empty_cam, do_unlink=True) 68 | 69 | empty_head = bpy.data.objects.get(prefix + "." + fdg_names.empty_head) 70 | 71 | if empty_head is not None: 72 | bpy.data.objects.remove(empty_head, do_unlink=True) 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | self.report({'INFO'}, "Removed Drivers!") 81 | return {'FINISHED'} 82 | 83 | 84 | def register(): 85 | bpy.utils.register_class(FDG_OT_RemoveDrivers_Op) 86 | 87 | 88 | def unregister(): 89 | bpy.utils.unregister_class(FDG_OT_RemoveDrivers_Op) -------------------------------------------------------------------------------- /fchar_settings.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | 5 | class Settings(object): 6 | """A Singleton class which stores all the custom settings for the FCHAR AddOn and keeps them synced with a json.""" 7 | _instance = None 8 | _settings_path = os.path.join( 9 | os.path.dirname(__file__), 10 | "fchar_settings.json") 11 | _settings = {} 12 | _default_settings = { 13 | "bone_map_path" : os.path.join(os.path.join(os.path.join(os.path.dirname(__file__),"mocap_retarget"), "BoneMap"),"IKlegFKarm.bmap"), 14 | "perforce_path" : "", 15 | "shader_path" : "", 16 | "line_settings" : 17 | {"L0" : {"line_key" : "L0", 18 | "dist_close" : 0.75, "thick_close" : 20.0, "clamp_close" : False, 19 | "dist_far" : 2.8, "thick_far" : 34.0, "clamp_far" : True, 20 | "crv_amount" : 15.0, "crv_mode" : True, 21 | "crv_max_dist" : 0.0, "crv_off_dist" : -1.0 22 | }, 23 | 24 | "L1" : 25 | {"line_key" : "L1", 26 | "dist_close" : 0.75, "thick_close" : 14.0, "clamp_close" : False, 27 | "dist_far" : 2.8, "thick_far" : 20.0, "clamp_far" : True, 28 | "crv_amount" : 1.0, "crv_mode" : True, 29 | "crv_max_dist" : 0.0, "crv_off_dist" : -1.0 30 | }, 31 | 32 | "L2" : 33 | {"line_key" : "L2", 34 | "dist_close" : 0.75, "thick_close" : 7.0, "clamp_close" : False, 35 | "dist_far" : 2.8, "thick_far" : 10.0, "clamp_far" : True, 36 | "crv_amount" : 1.0, "crv_mode" : True, 37 | "crv_max_dist" : 0.0, "crv_off_dist" : -1.0 38 | } 39 | } 40 | 41 | } 42 | 43 | def __new__(cls): 44 | """Singleton initialization""" 45 | if cls._instance is None: 46 | #print('Creating the object') 47 | cls._instance = super(Settings, cls).__new__(cls) 48 | cls._instance._load_settings() 49 | return cls._instance 50 | 51 | def _load_settings(self): 52 | """Loads the Settings. If no settings JSON is found, uses the default settings and writes them to disk as JSON.""" 53 | try: 54 | if os.path.isfile(self._settings_path): 55 | f = open(self._settings_path, 'r') 56 | self._settings = json.load(f) 57 | f.close() 58 | else: 59 | 60 | self._settings = self._default_settings 61 | self._write_settings() 62 | 63 | except Exception: 64 | pass 65 | 66 | 67 | 68 | def _write_settings(self): 69 | """Writes the SettingsDict to disk as JSON""" 70 | 71 | try: 72 | with open(self._settings_path, 'w') as f: 73 | data_out = json.dump(self._settings, f, indent=4, sort_keys=True) 74 | 75 | except Exception: 76 | pass 77 | 78 | def get_setting(self, key): 79 | """Gets a Setting from the Dict. If the Setting is not in the Dict, it tries to find it in the default settings. 80 | If it is not in either retuns None.""" 81 | if key in self._settings: 82 | return self._settings[key] 83 | else: 84 | if key in self._default_settings: 85 | self._settings[key] = self._default_settings[key] 86 | self._write_settings() 87 | return self._settings[key] 88 | else: 89 | return None 90 | 91 | def set_setting(self, key, value): 92 | """Sets a Setting in the dict and updates the Json afterwards.""" 93 | self._settings[key] = value 94 | self._write_settings() -------------------------------------------------------------------------------- /updater_ui.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | # Updater ops import, all setup in this file. 4 | from . import addon_updater_ops 5 | 6 | 7 | class DemoUpdaterPanel(bpy.types.Panel): 8 | """Panel to demo popup notice and ignoring functionality""" 9 | bl_label = "Updater Demo Panel" 10 | bl_idname = "OBJECT_PT_DemoUpdaterPanel_hello" 11 | bl_space_type = 'VIEW_3D' 12 | bl_region_type = 'TOOLS' if bpy.app.version < (2, 80) else 'UI' 13 | bl_context = "objectmode" 14 | bl_category = "Tools" 15 | 16 | def draw(self, context): 17 | layout = self.layout 18 | 19 | # Call to check for update in background. 20 | # Note: built-in checks ensure it runs at most once, and will run in 21 | # the background thread, not blocking or hanging blender. 22 | # Internally also checks to see if auto-check enabled and if the time 23 | # interval has passed. 24 | addon_updater_ops.check_for_update_background() 25 | 26 | layout.label(text="Demo Updater Addon") 27 | layout.label(text="") 28 | 29 | col = layout.column() 30 | col.scale_y = 0.7 31 | col.label(text="If an update is ready,") 32 | col.label(text="popup triggered by opening") 33 | col.label(text="this panel, plus a box ui") 34 | 35 | # Could also use your own custom drawing based on shared variables. 36 | if addon_updater_ops.updater.update_ready: 37 | layout.label(text="Custom update message", icon="INFO") 38 | layout.label(text="") 39 | 40 | # Call built-in function with draw code/checks. 41 | addon_updater_ops.update_notice_box_ui(self, context) 42 | 43 | 44 | @addon_updater_ops.make_annotations 45 | class DemoPreferences(bpy.types.AddonPreferences): 46 | """Demo bare-bones preferences""" 47 | bl_idname = __package__ 48 | 49 | # Addon updater preferences. 50 | 51 | auto_check_update = bpy.props.BoolProperty( 52 | name="Auto-check for Update", 53 | description="If enabled, auto-check for updates using an interval", 54 | default=False) 55 | 56 | updater_interval_months = bpy.props.IntProperty( 57 | name='Months', 58 | description="Number of months between checking for updates", 59 | default=0, 60 | min=0) 61 | 62 | updater_interval_days = bpy.props.IntProperty( 63 | name='Days', 64 | description="Number of days between checking for updates", 65 | default=7, 66 | min=0, 67 | max=31) 68 | 69 | updater_interval_hours = bpy.props.IntProperty( 70 | name='Hours', 71 | description="Number of hours between checking for updates", 72 | default=0, 73 | min=0, 74 | max=23) 75 | 76 | updater_interval_minutes = bpy.props.IntProperty( 77 | name='Minutes', 78 | description="Number of minutes between checking for updates", 79 | default=0, 80 | min=0, 81 | max=59) 82 | 83 | def draw(self, context): 84 | layout = self.layout 85 | 86 | # Works best if a column, or even just self.layout. 87 | mainrow = layout.row() 88 | col = mainrow.column() 89 | 90 | # Updater draw function, could also pass in col as third arg. 91 | addon_updater_ops.update_settings_ui(self, context) 92 | 93 | # Alternate draw function, which is more condensed and can be 94 | # placed within an existing draw function. Only contains: 95 | # 1) check for update/update now buttons 96 | # 2) toggle for auto-check (interval will be equal to what is set above) 97 | # addon_updater_ops.update_settings_ui_condensed(self, context, col) 98 | 99 | # Adding another column to help show the above condensed ui as one column 100 | # col = mainrow.column() 101 | # col.scale_y = 2 102 | # ops = col.operator("wm.url_open","Open webpage ") 103 | # ops.url=addon_updater_ops.updater.website 104 | -------------------------------------------------------------------------------- /mocap_retarget/fmr_scale_list_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Operator 3 | from bpy_extras.io_utils import ImportHelper, ExportHelper 4 | from bpy.props import StringProperty, FloatProperty 5 | from .fmr_scale_list_io import ScaleListDict 6 | 7 | class FMR_OT_ImportScaleList_OP(Operator, ImportHelper): 8 | bl_idname = "file.import_scale_list" 9 | bl_label = "Import a Scale List" 10 | bl_description = "Import a JSON File with custom character scalings" 11 | bl_options = {"REGISTER", "UNDO"} 12 | 13 | filter_glob: StringProperty(default='*.json', options={'HIDDEN'}) 14 | filename_ext: StringProperty(default=".json") 15 | 16 | def execute(self, context): 17 | wm = context.window_manager 18 | item = wm.scale_list.add() 19 | item.name = "Fritzi" 20 | item.scale = 0.4 21 | 22 | scale_list_io = ScaleListDict() 23 | 24 | scale_list_io.load_scale_list(self.filepath) 25 | 26 | for area in bpy.context.screen.areas: 27 | area.tag_redraw() 28 | 29 | #print(self.filepath) 30 | 31 | return {'FINISHED'} 32 | 33 | 34 | class FMR_OT_ExportScaleList_OP(Operator, ExportHelper): 35 | bl_idname = "file.export_scale_list" 36 | bl_label = "Export a Scale List" 37 | bl_description = "Export a JSON File with custom character scalings" 38 | bl_option = {"REGISTER", "UNDO"} 39 | 40 | filter_glob: StringProperty(default='*.json', options={'HIDDEN'}) 41 | filename_ext: StringProperty(default=".json") 42 | 43 | def execute(self, context): 44 | wm = context.window_manager 45 | scale_list_io = ScaleListDict() 46 | 47 | scale_list_io.write_scale_list(self.filepath) 48 | #print(self.filepath) 49 | return {'FINISHED'} 50 | 51 | class FMR_OT_AddCharacterScale_OP(Operator): 52 | bl_idname = "object.add_character_scale" 53 | bl_label = "+" 54 | bl_description = "Add a character and its scaling to the scale list" 55 | bl_option = {"REGISTER", "UNDO"} 56 | 57 | char_name: StringProperty(name="Name", description="Name of the character which schould be scaled") 58 | char_scale: FloatProperty(name="Scale", description="scale which the character should be scaled to on import", default=1.0, min=0.0, max=2.0) 59 | 60 | def invoke(self, context, event): 61 | wm = context.window_manager 62 | return wm.invoke_props_dialog(self) 63 | 64 | def draw(self, context): 65 | layout = self.layout 66 | layout.label(text="Character Name") 67 | layout.prop(self, "char_name") 68 | layout.label(text="Character Scale") 69 | layout.prop(self,"char_scale") 70 | 71 | def execute(self, context): 72 | scale_list = ScaleListDict() 73 | scale_list.set_scale(self.char_name, self.char_scale) 74 | return {'FINISHED'} 75 | 76 | class FMR_OT_RemoveCharacterScale_OP(Operator): 77 | bl_idname = "object.remove_character_scale" 78 | bl_label = "-" 79 | bl_description = "Remove a character and its scaling from the scale list" 80 | bl_option = {"REGISTER", "UNDO"} 81 | 82 | @classmethod 83 | def poll(cls, context): 84 | return context.window_manager.scale_list 85 | 86 | def execute(self, context): 87 | wm = context.window_manager 88 | scale_list_UI = wm.scale_list 89 | scale_list = ScaleListDict() 90 | index = wm.scale_list_index 91 | name = scale_list_UI[index].character 92 | scale_list.remove_scale(key=name) 93 | wm.scale_list_index = min(max(0, index -1), len(scale_list_UI) - 1) 94 | 95 | return {'FINISHED'} 96 | 97 | 98 | def register(): 99 | bpy.utils.register_class(FMR_OT_ImportScaleList_OP) 100 | bpy.utils.register_class(FMR_OT_ExportScaleList_OP) 101 | bpy.utils.register_class(FMR_OT_AddCharacterScale_OP) 102 | bpy.utils.register_class(FMR_OT_RemoveCharacterScale_OP) 103 | 104 | def unregister(): 105 | bpy.utils.unregister_class(FMR_OT_RemoveCharacterScale_OP) 106 | bpy.utils.unregister_class(FMR_OT_AddCharacterScale_OP) 107 | bpy.utils.unregister_class(FMR_OT_ExportScaleList_OP) 108 | bpy.utils.unregister_class(FMR_OT_ImportScaleList_OP) -------------------------------------------------------------------------------- /mocap_retarget/fmr_files_ops.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import os 3 | 4 | from bpy.types import Operator, OperatorFileListElement, PropertyGroup 5 | from bpy.props import CollectionProperty, StringProperty 6 | 7 | from ..fchar_settings import Settings 8 | 9 | from bpy_extras.io_utils import ImportHelper 10 | 11 | class FMR_OT_SelectBVHs_OP(Operator, ImportHelper): 12 | """Operator which opens a file dialog to select multiple bvhs to be retargeted.""" 13 | bl_idname = "retarget.select_bvhs" 14 | bl_label = "Select BVHs" 15 | bl_description = "Select multiple bvhs to be retargeted" 16 | files: CollectionProperty(name="File Path", type=OperatorFileListElement) 17 | directory: StringProperty(subtype='DIR_PATH') 18 | 19 | filename_ext = "" 20 | 21 | def execute(self, context): 22 | wm = context.window_manager 23 | 24 | 25 | wm.bvh_files.clear() 26 | 27 | directory = self.directory 28 | for file_elem in self.files: 29 | file = wm.bvh_files.add() 30 | file.name = file_elem.name 31 | filepath = os.path.join(directory, file_elem.name) 32 | file.filepath = filepath 33 | #print(filepath) 34 | return {'FINISHED'} 35 | 36 | class FileList(PropertyGroup): 37 | """A Propertygroup which can hold the Name of a bvh file and the Filepath to it.""" 38 | name : StringProperty() 39 | filepath : StringProperty() 40 | 41 | class FMR_OT_AddBvh_OP(Operator, ImportHelper): 42 | bl_idname = "object.add_bvh" 43 | bl_label = "+" 44 | bl_description = "Add a bvh file to the list" 45 | bl_option = {"REGISTER", "UNDO"} 46 | 47 | files: CollectionProperty(name="File Path", type= OperatorFileListElement) 48 | directory: StringProperty(subtype='DIR_PATH') 49 | 50 | filename_ext = "" 51 | 52 | def execute(self, context): 53 | wm = context.window_manager 54 | 55 | directory = self.directory 56 | for file_elem in self.files: 57 | file = wm.bvh_files.add() 58 | file.name = file_elem.name 59 | filepath = os.path.join(directory, file_elem.name) 60 | file.filepath = filepath 61 | 62 | return {'FINISHED'} 63 | 64 | class FMR_OT_RemoveBvh_OP(Operator): 65 | bl_idname = "retarget.remove_bvh" 66 | bl_label = "-" 67 | bl_description = "Remove a BVH from the list" 68 | bl_option = {"REGISTER", "UNDO"} 69 | 70 | @classmethod 71 | def poll(cls, context): 72 | return context.window_manager.bvh_files 73 | 74 | def execute(self, context): 75 | wm = context.window_manager 76 | bvh_files = wm.bvh_files 77 | index = wm.bvh_files_index 78 | bvh_files.remove(index) 79 | wm.bvh_files_index = min(max(0, index - 1), len(bvh_files) - 1) 80 | 81 | return {'FINISHED'} 82 | 83 | class FMR_OT_SelectCharacter_OP(Operator, ImportHelper): 84 | bl_idname = "retarget.select_char_file" 85 | bl_label = "Load Character" 86 | bl_description = "Select the character Rig File on which to retarget the MoCap files" 87 | bl_option = {"REGISTER", "UNDO"} 88 | 89 | def execute(self, context): 90 | path = self.filepath 91 | file = os.path.basename(path) 92 | root, ext = os.path.splitext(path) 93 | 94 | wm = context.window_manager 95 | 96 | 97 | #print(file) 98 | #print(path) 99 | #print(ext) 100 | 101 | if file and ext == '.blend': 102 | wm.char_file_path = path 103 | bpy.ops.wm.open_mainfile(filepath=path) 104 | 105 | 106 | return {'FINISHED'} 107 | 108 | 109 | def register(): 110 | bpy.utils.register_class(FMR_OT_SelectBVHs_OP) 111 | bpy.utils.register_class(FileList) 112 | bpy.utils.register_class(FMR_OT_AddBvh_OP) 113 | bpy.utils.register_class(FMR_OT_RemoveBvh_OP) 114 | bpy.utils.register_class(FMR_OT_SelectCharacter_OP) 115 | 116 | def unregister(): 117 | bpy.utils.unregister_class(FMR_OT_SelectCharacter_OP) 118 | bpy.utils.unregister_class(FMR_OT_RemoveBvh_OP) 119 | bpy.utils.unregister_class(FMR_OT_AddBvh_OP) 120 | bpy.utils.unregister_class(FileList) 121 | bpy.utils.unregister_class(FMR_OT_SelectBVHs_OP) -------------------------------------------------------------------------------- /chr_shader_tools/cst_ShaderController/cst_vector_light_selector_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | from ...driver_generator.utility_functions.fdg_driver_utils import remove_driver_variables 6 | 7 | class CST_OT_VectorLightSelector_OP(Operator): 8 | bl_idname = "object.select_light_empty" 9 | bl_label = "Link Light to Character" 10 | bl_description = "Adds driver from the Light empty to all Meshes in the selected Collection, so that the Shader knows the light direction" 11 | bl_options = {"REGISTER", "UNDO"} 12 | 13 | # should only work in object mode 14 | @classmethod 15 | def poll(cls, context): 16 | obj = bpy.context.object 17 | 18 | if obj: 19 | if obj.mode == "OBJECT": 20 | return True 21 | 22 | return False 23 | 24 | 25 | def execute(self, context): 26 | 27 | wm = bpy.context.window_manager 28 | 29 | lightEmpty = wm.light_empty 30 | characterCollection = wm.character_collection 31 | characterRig = wm.character_rig 32 | characterRigBone = wm.character_rig_bone 33 | 34 | if lightEmpty is None: 35 | self.report({'WARNING'}, "Please select the Empty!") 36 | return {'CANCELLED'} 37 | 38 | if characterCollection is None: 39 | self.report({'WARNING'}, "Please select a collection with the Character!") 40 | return {'CANCELLED'} 41 | 42 | if characterRig: 43 | constraint = lightEmpty.constraints.new(type='CHILD_OF') 44 | constraint.target = characterRig 45 | if characterRigBone: 46 | constraint.subtarget = characterRigBone 47 | 48 | constraint.use_rotation_x = False 49 | constraint.use_rotation_y = False 50 | constraint.use_rotation_z = False 51 | constraint.use_scale_x = False 52 | constraint.use_scale_y = False 53 | constraint.use_scale_z = False 54 | constraint.set_inverse_pending = True 55 | 56 | 57 | 58 | self.add_light_drivers(characterCollection, lightEmpty) 59 | 60 | # Have to redraw all areas, so that the new properties are visible to the user 61 | for area in bpy.context.screen.areas: 62 | area.tag_redraw() 63 | 64 | self.report({'INFO'}, "Added Light Drivers!") 65 | return {'FINISHED'} 66 | 67 | def add_light_drivers(self, collection, light): 68 | """Recursively searches the collection and all child collections for Meshes and adds a light rotation vector property to them, which is driven by the rotation of the light empty""" 69 | for child in collection.children: 70 | self.add_light_drivers(child, light) 71 | 72 | for obj in collection.objects: 73 | if obj.type == 'MESH': 74 | 75 | obj["light_rotation"] = (0.0, 0.0, 0.0) 76 | ui_data = obj.id_properties_ui("light_rotation") 77 | ui_data.update( 78 | description="Light Rotation", 79 | default=(0.0, 0.0, 0.0), 80 | min=-10000, 81 | max=10000 82 | ) 83 | 84 | driver_rot_x = obj.driver_add('["light_rotation"]', 0).driver 85 | driver_rot_x.type = 'AVERAGE' 86 | 87 | remove_driver_variables(driver_rot_x) 88 | var = driver_rot_x.variables.new() 89 | var.name = "rot_x" 90 | var.type = "TRANSFORMS" 91 | target = var.targets[0] 92 | target.id = light 93 | target.transform_type = 'ROT_X' 94 | target.transform_space = 'WORLD_SPACE' 95 | 96 | driver_rot_y = obj.driver_add('["light_rotation"]', 1).driver 97 | driver_rot_y.type = 'AVERAGE' 98 | remove_driver_variables(driver_rot_y) 99 | var = driver_rot_y.variables.new() 100 | var.name = "rot_y" 101 | var.type = "TRANSFORMS" 102 | target = var.targets[0] 103 | target.id = light 104 | target.transform_type = 'ROT_Y' 105 | target.transform_space = 'WORLD_SPACE' 106 | 107 | driver_rot_z = obj.driver_add('["light_rotation"]', 2).driver 108 | driver_rot_z.type = 'AVERAGE' 109 | remove_driver_variables(driver_rot_z) 110 | var = driver_rot_z.variables.new() 111 | var.name = "rot_z" 112 | var.type = "TRANSFORMS" 113 | target = var.targets[0] 114 | target.id = light 115 | target.transform_type = 'ROT_Z' 116 | target.transform_space = 'WORLD_SPACE' 117 | 118 | 119 | 120 | 121 | 122 | def register(): 123 | bpy.utils.register_class(CST_OT_VectorLightSelector_OP) 124 | 125 | def unregister(): 126 | bpy.utils.unregister_class(CST_OT_VectorLightSelector_OP) -------------------------------------------------------------------------------- /chr_shader_tools/cst_ShaderController/cst_shader_controller_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import PointerProperty, StringProperty 3 | 4 | 5 | class CST_PT_ShaderController_pnl(bpy.types.Panel): 6 | bl_label = "Shader Controller" 7 | bl_category = "FCHAR" 8 | bl_space_type = "VIEW_3D" 9 | bl_region_type = "UI" 10 | bl_idname = "CST_PT_ShaderController_pnl" 11 | bl_options = {"DEFAULT_CLOSED"} 12 | 13 | bpy.types.WindowManager.character_name = StringProperty(name="Character Name") 14 | bpy.types.WindowManager.controller_empty = PointerProperty( 15 | name="Shader Controller", type=bpy.types.Object) 16 | bpy.types.WindowManager.character_collection = PointerProperty( 17 | name="Character Collection", type=bpy.types.Collection) 18 | bpy.types.WindowManager.light_empty = PointerProperty( 19 | name="Light Empty", type=bpy.types.Object) 20 | bpy.types.WindowManager.character_rig = PointerProperty(name = "Character Rig", type=bpy.types.Object) 21 | bpy.types.WindowManager.character_rig_bone = StringProperty(name = "Character Rig Bone") 22 | 23 | def draw(self, context): 24 | 25 | wm = context.window_manager 26 | layout = self.layout 27 | 28 | layout.prop(wm, "character_name") 29 | layout.operator("object.create_shader_controller") 30 | layout.operator("object.create_light_empty") 31 | 32 | layout.separator() 33 | 34 | 35 | 36 | layout.prop_search(wm, "controller_empty", 37 | context.scene, "objects", text="Shader Controller") 38 | layout.prop_search(wm, "light_empty", 39 | context.scene, "objects", text="Light Empty") 40 | layout.prop_search(wm, "character_collection", 41 | bpy.data, "collections", text="Character Collection") 42 | layout.prop_search(wm, "character_rig", bpy.data, "objects", text="Character Rig") 43 | rig = wm.character_rig 44 | if rig: 45 | if rig.type == 'ARMATURE': 46 | layout.prop_search(wm, "character_rig_bone", rig.data, "bones") 47 | if 'c_root_master.x' in rig.data.bones: 48 | wm.character_rig_bone = 'c_root_master.x' 49 | 50 | layout.operator("object.select_shader_controller") 51 | layout.operator("object.select_light_empty") 52 | 53 | class CST_PT_ShaderControllerAdvanced_PNL(bpy.types.Panel): 54 | bl_label = "Advanced" 55 | bl_space_type = "VIEW_3D" 56 | bl_region_type = "UI" 57 | bl_category = "FCHAR" 58 | bl_parent_id = "CST_PT_ShaderController_pnl" 59 | bl_options = {"DEFAULT_CLOSED"} 60 | 61 | def draw(self, context): 62 | 63 | wm = context.window_manager 64 | layout = self.layout 65 | layout.operator("object.copy_controller_data") 66 | layout.operator("object.create_crowd_shader_controller") 67 | layout.operator("object.select_crowd_shader_controller") 68 | layout.separator() 69 | 70 | layout.label(text = 'Cleanup') 71 | layout.operator("object.remove_props") 72 | 73 | layout.separator() 74 | layout.operator("object.update_shader_controller") 75 | 76 | def poll_armature(self, object): 77 | if object.type == 'ARMATURE': 78 | return True 79 | 80 | return False 81 | 82 | class CST_PT_ShaderControllerEyes_pnl(bpy.types.Panel): 83 | bl_label = "Eyes" 84 | bl_space_type = "VIEW_3D" 85 | bl_region_type = "UI" 86 | bl_category = "FCHAR" 87 | bl_parent_id = "CST_PT_ShaderController_pnl" 88 | bl_options = {"DEFAULT_CLOSED"} 89 | 90 | bpy.types.WindowManager.eye_left = PointerProperty(name = "Left Eye", type=bpy.types.Object) 91 | bpy.types.WindowManager.eye_right = PointerProperty(name = "Right Eye", type=bpy.types.Object) 92 | 93 | def draw(self, context): 94 | wm = context.window_manager 95 | layout = self.layout 96 | 97 | layout.prop_search(wm, "character_rig", bpy.data, "objects", text="Character Rig") 98 | layout.prop_search(wm, "eye_left", bpy.data, "objects") 99 | layout.prop_search(wm, "eye_right", bpy.data, "objects") 100 | 101 | if wm.character_rig: 102 | 103 | rig_bones = wm.character_rig.pose.bones 104 | 105 | if ('eye_up.L' in rig_bones and 106 | 'eye_up.R' in rig_bones and 107 | 'eye_down.L' in rig_bones and 108 | 'eye_down.R' in rig_bones): 109 | msg = "Rig should have eye up/down bones" 110 | else: 111 | msg = "Rig should NOT have eye up/down bones" 112 | layout.label(text=msg) 113 | 114 | layout.operator("object.add_eye_drivers") 115 | layout.operator("object.add_eye_drivers_no_up_down") 116 | 117 | 118 | 119 | def register(): 120 | bpy.utils.register_class(CST_PT_ShaderController_pnl) 121 | bpy.utils.register_class(CST_PT_ShaderControllerAdvanced_PNL) 122 | bpy.utils.register_class(CST_PT_ShaderControllerEyes_pnl) 123 | 124 | def unregister(): 125 | bpy.utils.unregister_class(CST_PT_ShaderControllerEyes_pnl) 126 | bpy.utils.unregister_class(CST_PT_ShaderControllerAdvanced_PNL) 127 | bpy.utils.unregister_class(CST_PT_ShaderController_pnl) 128 | -------------------------------------------------------------------------------- /mocap_retarget/BoneMap/IKlegFKarm.bmap: -------------------------------------------------------------------------------- 1 | c_head.x%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 2 | Head 3 | False 4 | False 5 | 6 | c_root_master.x%False%False%0.0,0.0,0.0%0.0,-0.009999999776482582,0.0%1.0 7 | Hips 8 | True 9 | False 10 | 11 | c_arm_fk.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 12 | LeftArm 13 | False 14 | False 15 | 16 | c_foot_ik.l%False%True%0.13962633907794952,0.0,0.0%0.0,0.0,0.0%1.0 17 | LeftFoot 18 | False 19 | True 20 | c_leg_pole.l 21 | c_forearm_fk.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 22 | LeftForeArm 23 | False 24 | False 25 | 26 | c_hand_fk.l%False%False%0.0,0.0,0.0%0.0,-0.029999999329447746,0.0%1.0 27 | LeftHand 28 | False 29 | False 30 | c_arms_pole.l 31 | c_index1.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 32 | LeftHandIndex1 33 | False 34 | False 35 | 36 | c_index2.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 37 | LeftHandIndex2 38 | False 39 | False 40 | 41 | c_index3.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 42 | LeftHandIndex3 43 | False 44 | False 45 | 46 | c_middle1.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 47 | LeftHandMiddle1 48 | False 49 | False 50 | 51 | c_middle2.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 52 | LeftHandMiddle2 53 | False 54 | False 55 | 56 | c_middle3.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 57 | LeftHandMiddle3 58 | False 59 | False 60 | 61 | c_pinky1.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 62 | LeftHandPinky1 63 | False 64 | False 65 | 66 | c_pinky2.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 67 | LeftHandPinky2 68 | False 69 | False 70 | 71 | c_pinky3.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 72 | LeftHandPinky3 73 | False 74 | False 75 | 76 | c_ring1.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 77 | LeftHandRing1 78 | False 79 | False 80 | 81 | c_ring2.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 82 | LeftHandRing2 83 | False 84 | False 85 | 86 | c_ring3.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 87 | LeftHandRing3 88 | False 89 | False 90 | 91 | None%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 92 | LeftHandThumb1 93 | False 94 | False 95 | 96 | None%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 97 | LeftHandThumb2 98 | False 99 | False 100 | 101 | None%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 102 | LeftHandThumb3 103 | False 104 | False 105 | 106 | c_leg_fk.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 107 | LeftLeg 108 | False 109 | False 110 | 111 | c_shoulder.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 112 | LeftShoulder 113 | False 114 | False 115 | 116 | c_toes_ik.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 117 | LeftToeBase 118 | False 119 | False 120 | 121 | c_thigh_fk.l%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 122 | LeftUpLeg 123 | False 124 | False 125 | 126 | c_neck.x%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 127 | Neck 128 | False 129 | False 130 | 131 | c_arm_fk.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 132 | RightArm 133 | False 134 | False 135 | 136 | c_foot_ik.r%False%True%0.12217304110527039,0.0,0.0%0.0,0.0,0.0%1.0 137 | RightFoot 138 | False 139 | True 140 | c_leg_pole.r 141 | c_forearm_fk.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 142 | RightForeArm 143 | False 144 | False 145 | 146 | c_hand_fk.r%False%False%0.0,0.0,0.0%0.0,-0.019999999552965164,0.0%1.0 147 | RightHand 148 | False 149 | False 150 | c_arms_pole.r 151 | c_index1.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 152 | RightHandIndex1 153 | False 154 | False 155 | 156 | c_index2.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 157 | RightHandIndex2 158 | False 159 | False 160 | 161 | c_index3.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 162 | RightHandIndex3 163 | False 164 | False 165 | 166 | c_middle1.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 167 | RightHandMiddle1 168 | False 169 | False 170 | 171 | c_middle2.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 172 | RightHandMiddle2 173 | False 174 | False 175 | 176 | c_middle3.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 177 | RightHandMiddle3 178 | False 179 | False 180 | 181 | c_pinky1.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 182 | RightHandPinky1 183 | False 184 | False 185 | 186 | c_pinky2.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 187 | RightHandPinky2 188 | False 189 | False 190 | 191 | c_pinky3.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 192 | RightHandPinky3 193 | False 194 | False 195 | 196 | c_ring1.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 197 | RightHandRing1 198 | False 199 | False 200 | 201 | c_ring2.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 202 | RightHandRing2 203 | False 204 | False 205 | 206 | c_ring3.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 207 | RightHandRing3 208 | False 209 | False 210 | 211 | None%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 212 | RightHandThumb1 213 | False 214 | False 215 | 216 | None%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 217 | RightHandThumb2 218 | False 219 | False 220 | 221 | None%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 222 | RightHandThumb3 223 | False 224 | False 225 | 226 | c_leg_fk.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 227 | RightLeg 228 | False 229 | False 230 | 231 | c_shoulder.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 232 | RightShoulder 233 | False 234 | False 235 | 236 | c_toes_ik.r%False%False%-0.12217304110527039,0.0,0.0%0.0,0.0,0.0%1.0 237 | RightToeBase 238 | False 239 | False 240 | 241 | c_thigh_fk.r%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 242 | RightUpLeg 243 | False 244 | False 245 | 246 | c_spine_01.x%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 247 | Spine 248 | False 249 | False 250 | 251 | c_spine_02.x%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 252 | Spine1 253 | False 254 | False 255 | 256 | c_spine_03.x%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 257 | Spine2 258 | False 259 | False 260 | 261 | c_spine_04.x%False%False%0.0,0.0,0.0%0.0,0.0,0.0%1.0 262 | Spine3 263 | False 264 | False 265 | 266 | -------------------------------------------------------------------------------- /mocap_retarget/fmr_scale_list_io.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import bpy 4 | 5 | class ScaleListDict(object): 6 | """A Singleton class which stores all the custom scalings for characters and keeps them synced with a json.""" 7 | _instance = None 8 | _scale_list_path = os.path.join(os.path.dirname(__file__), "fmr_scale_list.json") 9 | _scale_list = {} 10 | _default_scale_list = { 11 | "hanno" : 0.52, 12 | "sophie" : 0.86 13 | } 14 | _wm = None 15 | loading = True 16 | 17 | def __new__(cls): 18 | """Singleton initialization""" 19 | if cls._instance is None: 20 | cls._instance = super(ScaleListDict, cls).__new__(cls) 21 | if not cls._instance._wm: 22 | cls._instance._wm = bpy.context.window_manager 23 | #cls._instance._wm = window_manager 24 | #cls._instance.initializin = True 25 | cls._instance.load_scale_list(cls._instance._scale_list_path) 26 | #cls._instance.initializin = False 27 | 28 | if not cls._instance._wm: 29 | cls._instance._wm = bpy.context.window_manager 30 | 31 | return cls._instance 32 | 33 | def load_scale_list(self, path): 34 | """Loads the Scale List JSON and pushes it to the UI. 35 | Sets the loading flag, so UI updates do not trigger infinite recursion. 36 | If no Scale List JSON is found, the default scale list gets used and written to disk.""" 37 | self.loading = True 38 | try: 39 | if os.path.isfile(path): 40 | f = open(path, 'r') 41 | self._scale_list = json.load(f) 42 | f.close() 43 | if not self._scale_list: 44 | self._scale_list = self._default_scale_list 45 | self.write_scale_list(self._scale_list_path) 46 | 47 | else: 48 | self._scale_list = self._default_scale_list 49 | self.write_scale_list(self._scale_list_path) 50 | 51 | 52 | 53 | 54 | except Exception: 55 | self._scale_list = self._default_scale_list 56 | self.write_scale_list(self._scale_list_path) 57 | 58 | finally: 59 | self.push_to_properties() 60 | self.loading = False 61 | 62 | 63 | 64 | def write_scale_list(self, path): 65 | """ Writes the Scale List to disk as JSON.""" 66 | # print("writing") 67 | try: 68 | with open(path, 'w') as f: 69 | data_out = json.dump(self._scale_list, f, indent=4, sort_keys=True) 70 | 71 | # print("written") 72 | except Exception as e: 73 | print("exception") 74 | print(e) 75 | pass 76 | 77 | 78 | 79 | def push_to_properties(self): 80 | """ Pushes all scales and character Names to the UI. Sets the Loading flag while doing it, so UI Updates do not cause infinite recursion.""" 81 | self.loading = True 82 | scale_list_prop = bpy.context.window_manager.scale_list 83 | scale_list_prop.clear() 84 | for key in self._scale_list: 85 | item = scale_list_prop.add() 86 | item.name = key 87 | item.character = key 88 | item.scale = self._scale_list[key] 89 | 90 | for area in bpy.context.screen.areas: 91 | area.tag_redraw() 92 | self.loading = False 93 | 94 | 95 | def get_scale(self, key): 96 | """Returns the scale of a given character. If the Character is not in the scale list dict, searches for it in the default values. 97 | If it is not in there either, returns None""" 98 | 99 | if key in self._scale_list: 100 | return self._scale_list[key] 101 | else: 102 | if key in self._default_scale_list: 103 | self._scale_list[key] = self._default_scale_list[key] 104 | self.write_scale_list(self._scale_list_path) 105 | return self._scale_list[key] 106 | else: 107 | return None 108 | 109 | def set_scale(self, key, value): 110 | """Sets a scale in the scale List Dict.""" 111 | if not self.loading: 112 | key = key.lower() 113 | self._scale_list[key] = value 114 | 115 | self.push_to_properties() 116 | self.write_scale_list(self._scale_list_path) 117 | 118 | def remove_scale(self, key): 119 | """Removes a Character Scaling from the scale list dict if it exists.""" 120 | if key in self._scale_list: 121 | del self._scale_list[key] 122 | 123 | if not self.loading: 124 | self.push_to_properties() 125 | self.write_scale_list(self._scale_list_path) 126 | 127 | 128 | 129 | def fetch_properties(self): 130 | """ Gets the Scale List Properties from the UI and puts them in the scale list dict, if the Loading flag is not set. 131 | Whis prevents infinite recursion, since the update methods in the ui also get triggered on API calls.""" 132 | if not self.loading: 133 | self._scale_list.clear() 134 | scale_props = bpy.context.window_manager.scale_list 135 | length = len(scale_props) 136 | if length > 0: 137 | for i in range(length): 138 | self._scale_list[scale_props[i].character.lower()] = scale_props[i].scale 139 | self.write_scale_list(self._scale_list_path) 140 | self.push_to_properties() 141 | -------------------------------------------------------------------------------- /driver_generator/driver_functions/fdg_driver_functions.py: -------------------------------------------------------------------------------- 1 | # This script defines functions to be used directly in driver expressions to 2 | # extend the built-in set of python functions. 3 | # 4 | # This can be executed on manually or set to 'Register' to 5 | # initialize the functions on file load. 6 | 7 | import bpy 8 | import mathutils 9 | import math 10 | from bpy.app.handlers import persistent 11 | from ..utility_functions.fdg_driver_utils import append_function_unique, remove_function 12 | 13 | 14 | # this function returns the vector between two points. The direction is from point1 to point2 15 | def vector_head_cam(head_loc_x, head_loc_y, head_loc_z, cam_loc_x, cam_loc_y, cam_loc_z): 16 | vec_head_cam = mathutils.Vector( 17 | (cam_loc_x - head_loc_x, cam_loc_y - head_loc_y, cam_loc_z - head_loc_z)) 18 | 19 | return vec_head_cam 20 | 21 | 22 | # this function translates the vector from head to camera from world-space into the local space of the head-bone. 23 | def to_local_head(vec_head_cam, head_rot_x, head_rot_y, head_rot_z): 24 | rot = mathutils.Euler((-head_rot_x, -head_rot_y, -head_rot_z)) 25 | 26 | vec_head_cam.rotate(rot) 27 | 28 | return vec_head_cam 29 | 30 | 31 | # this function calculates the angle between the head bone and the camera around the local z axis of the head. (It returns a Positive angle if the camera is right of the head, negative angle if the camera is left of the head) 32 | def angle_z(head_loc_x, head_loc_y, head_loc_z, cam_loc_x, cam_loc_y, cam_loc_z, head_rot_x, head_rot_y, head_rot_z): 33 | head_forward = mathutils.Vector((0, -1)) 34 | 35 | vec_head_cam = vector_head_cam( 36 | head_loc_x, head_loc_y, head_loc_z, cam_loc_x, cam_loc_y, cam_loc_z) 37 | vec_head_cam = to_local_head( 38 | vec_head_cam, head_rot_x, head_rot_y, head_rot_z) 39 | 40 | if vec_head_cam.x == 0.0 and vec_head_cam.y == 0.0 and vec_head_cam.z == 0.0: 41 | return 0.0 42 | 43 | vec_head_cam_xy = mathutils.Vector((vec_head_cam.x, vec_head_cam.y)) 44 | 45 | vec_head_cam_xy = vec_head_cam_xy.normalized() 46 | 47 | dot_product = vec_head_cam_xy.dot(head_forward) 48 | 49 | angle = math.acos(dot_product) 50 | 51 | if (vec_head_cam.x < 0): 52 | angle = angle * -1 53 | 54 | return angle 55 | 56 | 57 | # this function calculates the angle between the head bone and the camera around the local x axis of the head. (It returns a Positive angle if the camera is above the head, negative angle if the camera is below of the head) 58 | def angle_x(head_loc_x, head_loc_y, head_loc_z, cam_loc_x, cam_loc_y, cam_loc_z, head_rot_x, head_rot_y, head_rot_z): 59 | head_forward = mathutils.Vector((1, 0)) 60 | 61 | vec_head_cam = vector_head_cam( 62 | head_loc_x, head_loc_y, head_loc_z, cam_loc_x, cam_loc_y, cam_loc_z) 63 | vec_head_cam = to_local_head( 64 | vec_head_cam, head_rot_x, head_rot_y, head_rot_z) 65 | 66 | length_xy = math.sqrt(pow(vec_head_cam.x, 2) + pow(vec_head_cam.y, 2)) 67 | 68 | if length_xy == 0.0: 69 | if vec_head_cam.z > 0.0: 70 | return math.pi/2 #90 degrees 71 | elif vec_head_cam.z == 0.0: 72 | return 0.0 73 | else: 74 | return -math.pi/2 #-90 degrees 75 | 76 | angle = math.atan(vec_head_cam.z / length_xy) 77 | 78 | return angle 79 | 80 | def lerp_custom_curve(grease_pencil, distance, start, end): 81 | factor = (distance - start) / (end - start) 82 | factor = min(1.0, max(0.0, factor)) 83 | gp = bpy.data.objects["c001_fritzi_GPencil_Main"] 84 | gp.grease_pencil_modifiers["Thickness"].curve.update() 85 | standard_values = [0.425, 0.61875, 1.0, 0.625, 0.425] 86 | 87 | for i in range (0,5): 88 | point = gp.grease_pencil_modifiers["Thickness"].curve.curves[0].points[i] 89 | #grease_pencil.grease_pencil_modifiers["Thickness"].curve.curves[0].points.new(0.25, 0.5) 90 | value = standard_values[i] * (1.0-factor) + 1.0 * factor 91 | #print(value) 92 | point.location[1] = value 93 | #print(point.location[1]) 94 | gp.grease_pencil_modifiers["Thickness"].curve.update() 95 | 96 | #bpy.context.view_layer.update() 97 | return factor 98 | 99 | 100 | 101 | @persistent 102 | def load_handler(dummy): 103 | bpy.app.driver_namespace["angle_x_func"] = angle_x 104 | bpy.app.driver_namespace["angle_z_func"] = angle_z 105 | bpy.app.driver_namespace["lerp_custom_curve"] = lerp_custom_curve 106 | 107 | 108 | # Add functions defined in this script into the drivers namespace. 109 | def register(): 110 | #append_function_unique(bpy.app.handlers.load_post, load_handler) 111 | #bpy.app.driver_namespace["angle_x_func"] = angle_x 112 | #bpy.app.driver_namespace["angle_z_func"] = angle_z 113 | #bpy.app.driver_namespace["lerp_custom_curve"] = lerp_custom_curve 114 | remove_function(bpy.app.handlers.load_post, load_handler) 115 | 116 | if "angle_x_func" in bpy.app.driver_namespace: 117 | 118 | del bpy.app.driver_namespace["angle_x_func"] 119 | 120 | if "angle_z_func" in bpy.app.driver_namespace: 121 | del bpy.app.driver_namespace["angle_z_func"] 122 | 123 | if "angle_x" in bpy.app.driver_namespace: 124 | del bpy.app.driver_namespace["angle_x"] 125 | 126 | if "angle_z" in bpy.app.driver_namespace: 127 | del bpy.app.driver_namespace["angle_z"] 128 | 129 | if "angle_vectors" in bpy.app.driver_namespace: 130 | del bpy.app.driver_namespace["angle_vectors"] 131 | 132 | pass 133 | 134 | 135 | def unregister(): 136 | remove_function(bpy.app.handlers.load_post, load_handler) 137 | 138 | if "angle_x_func" in bpy.app.driver_namespace: 139 | print("it is") 140 | del bpy.app.driver_namespace["angle_x_func"] 141 | 142 | bpy.app.driver_namespace["angle_z_func"] = None 143 | bpy.app.driver_namespace["lerp_custom_curve"] = None 144 | -------------------------------------------------------------------------------- /animation_mover/anm_mover_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import EnumProperty, IntProperty, BoolProperty 3 | 4 | 5 | class ANM_PT_KeyframeMover_pnl(bpy.types.Panel): 6 | bl_label = "Animation Mover" 7 | bl_category = "FCHAR" 8 | bl_space_type = "VIEW_3D" 9 | bl_region_type = "UI" 10 | bl_options = {"DEFAULT_CLOSED"} 11 | 12 | bpy.types.WindowManager.move_type = EnumProperty(name='move_type', 13 | description='Shows which Type of Frame Range should be used for the animation move (Before, After, Between', 14 | items={('BEFORE', 'before', 'Move all before'), 15 | ('AFTER', 'after', 'Move all after'), 16 | ('BETWEEN', 'between', 'move all between')}, 17 | default = 'AFTER') 18 | 19 | bpy.types.WindowManager.move_direction = EnumProperty(name='move_direction', 20 | description='Which direction to move the keyframes', 21 | items={('FORWARD', 'forward', 'Move Keyframes forwards'), 22 | ('BACKWARD', 'backwards', 'Move Keyframes backwards')}, 23 | default= 'FORWARD') 24 | bpy.types.WindowManager.frame_in = IntProperty(name= 'Frame In') 25 | bpy.types.WindowManager.frame_out = IntProperty(name= 'Frame Out') 26 | bpy.types.WindowManager.frame_amount = IntProperty(name= 'Frames', min = 0, soft_min = 0) 27 | 28 | bpy.types.WindowManager.move_keys = BoolProperty(name='Move Keyframes', description='Should the Move Operator Move Keyframes', default=True) 29 | bpy.types.WindowManager.move_NLA = BoolProperty(name='Move NLA Strips', description='Should the Move Operator Move NLA Strips', default=True) 30 | bpy.types.WindowManager.move_marker = BoolProperty(name='Move Camera Marker', description='Should the Move Operator Move Camera Markers', default=True) 31 | bpy.types.WindowManager.move_sounds = BoolProperty(name= 'Move Sounds', description= 'Should the Move Operator move Sound Strips', default=True) 32 | def draw(self, context): 33 | wm = context.window_manager 34 | layout = self.layout 35 | layout.label(text="Move all Keyframes") 36 | row = layout.row(align=True) 37 | 38 | 39 | if wm.move_type == 'BEFORE': 40 | row.operator("object.move_animation_before", depress=True) 41 | row.operator("object.move_animation_after", depress=False) 42 | row.operator("object.move_animation_between", depress=False) 43 | layout.label(text= 'Frame') 44 | layout.prop(wm, "frame_in", text="") 45 | elif wm.move_type == 'AFTER': 46 | 47 | row.operator("object.move_animation_before", depress=False) 48 | row.operator("object.move_animation_after", depress=True) 49 | row.operator("object.move_animation_between", depress=False) 50 | layout.label(text= 'Frame') 51 | layout.prop(wm, "frame_in", text="") 52 | elif wm.move_type == 'BETWEEN': 53 | row.operator("object.move_animation_before", depress=False) 54 | row.operator("object.move_animation_after", depress=False) 55 | row.operator("object.move_animation_between", depress=True) 56 | layout.label(text= 'Frames') 57 | row = layout.row() 58 | row.prop(wm, "frame_in",text="") 59 | row.alignment = 'CENTER' 60 | row.label(text="and") 61 | row.alignment = 'EXPAND' 62 | row.prop(wm, "frame_out",text="") 63 | 64 | layout.prop(wm, 'frame_amount') 65 | row = layout.row(align=True) 66 | 67 | if wm.move_direction == 'FORWARD': 68 | row.operator("object.move_animation_backward", depress=False) 69 | row.operator("object.move_animation_forward", depress=True) 70 | elif wm.move_direction == 'BACKWARD': 71 | row.operator("object.move_animation_backward", depress=True) 72 | row.operator("object.move_animation_forward", depress=False) 73 | 74 | layout.operator("object.move_animation") 75 | 76 | layout.separator() 77 | layout.operator("animation.move_test") 78 | layout.separator() 79 | layout.operator("animation.split_nlas") 80 | 81 | class ANM_PT_MoveOptions_pnl(bpy.types.Panel): 82 | bl_label = "Options" 83 | bl_category = "FCHAR" 84 | bl_space_type = "VIEW_3D" 85 | bl_region_type = "UI" 86 | bl_parent_id = "ANM_PT_KeyframeMover_pnl" 87 | 88 | def draw(self, context): 89 | 90 | wm = context.window_manager 91 | layout = self.layout 92 | 93 | layout.prop(wm, 'move_keys') 94 | layout.prop(wm, 'move_NLA') 95 | layout.prop(wm, 'move_marker') 96 | layout.prop(wm, 'move_sounds') 97 | 98 | class ANM_PT_SyncLength_pnl(bpy.types.Panel): 99 | bl_label = "Sync Length" 100 | bl_category = "FCHAR" 101 | bl_space_type = "VIEW_3D" 102 | bl_region_type = "UI" 103 | bl_parent_id = "ANM_PT_KeyframeMover_pnl" 104 | 105 | def draw(self, context): 106 | wm = context.window_manager 107 | layout = self.layout 108 | 109 | layout.label(text="For all NLA Strips:") 110 | 111 | row = layout.row(align=True) 112 | row.operator("object.sync_length_on") 113 | row.operator("object.sync_length_off") 114 | 115 | def draw_copy_animation_menu(self, context): 116 | 117 | layout = self.layout 118 | layout.separator() 119 | layout.operator("anm.copy_anim_data") 120 | 121 | def register(): 122 | bpy.types.VIEW3D_MT_make_links.append(draw_copy_animation_menu) 123 | bpy.utils.register_class(ANM_PT_KeyframeMover_pnl) 124 | bpy.utils.register_class(ANM_PT_MoveOptions_pnl) 125 | bpy.utils.register_class(ANM_PT_SyncLength_pnl) 126 | 127 | def unregister(): 128 | bpy.utils.unregister_class(ANM_PT_SyncLength_pnl) 129 | bpy.utils.unregister_class(ANM_PT_MoveOptions_pnl) 130 | bpy.utils.unregister_class(ANM_PT_KeyframeMover_pnl) 131 | bpy.types.VIEW3D_MT_make_links.remove(draw_copy_animation_menu) -------------------------------------------------------------------------------- /property_bake/FPB_property_bake_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Operator 3 | from bpy.props import IntProperty 4 | 5 | class FPB_OT_PropertyBake_OP(Operator): 6 | bl_idname = "fpb.bake_properties" 7 | bl_label = "Bake Ik/Fk Properties" 8 | bl_description = "Bakes all the Ik/Fk switches on a character into the current action" 9 | bl_options = {"REGISTER", "UNDO"} 10 | 11 | frame_start: IntProperty(name= 'Start') 12 | frame_end: IntProperty(name= 'End') 13 | 14 | @classmethod 15 | def poll(cls, context): 16 | wm = context.window_manager 17 | if not wm.character_rig: 18 | return False 19 | return True 20 | 21 | def invoke(self, context, event): 22 | self.frame_start = context.scene.frame_start 23 | self.frame_end = context.scene.frame_end 24 | return context.window_manager.invoke_props_dialog(self, width = 400) 25 | 26 | def draw(self, context): 27 | layout = self.layout 28 | row = layout.row() 29 | row.prop(self, "frame_start") 30 | row.prop(self, "frame_end") 31 | 32 | def execute(self, context): 33 | self.bake_easy_but_slow() 34 | 35 | return {'FINISHED'} 36 | 37 | def bake_easy_but_slow(self): 38 | wm = bpy.context.window_manager 39 | rig = wm.character_rig 40 | anim_data = rig.animation_data 41 | action = rig.animation_data.action 42 | influence = anim_data.action_influence 43 | anim_data.action_influence = 0.0 44 | bone_hand_l = rig.pose.bones["c_hand_ik.l"] 45 | hand_l_switch_curve = action.fcurves.find('pose.bones["c_hand_ik.l"]["ik_fk_switch"]') 46 | if not hand_l_switch_curve: 47 | hand_l_switch_curve = action.fcurves.new('pose.bones["c_hand_ik.l"]["ik_fk_switch"]') 48 | 49 | bone_hand_r = rig.pose.bones["c_hand_ik.r"] 50 | hand_r_switch_curve = action.fcurves.find('pose.bones["c_hand_ik.r"]["ik_fk_switch"]') 51 | if not hand_r_switch_curve: 52 | hand_r_switch_curve = action.fcurves.new('pose.bones["c_hand_ik.r"]["ik_fk_switch"]') 53 | 54 | bone_foot_l = rig.pose.bones["c_foot_ik.l"] 55 | foot_l_switch_curve = action.fcurves.find('pose.bones["c_foot_ik.l"]["ik_fk_switch"]') 56 | if not foot_l_switch_curve: 57 | foot_l_switch_curve = action.fcurves.new('pose.bones["c_foot_ik.l"]["ik_fk_switch"]') 58 | 59 | bone_foot_r = rig.pose.bones["c_foot_ik.r"] 60 | foot_r_switch_curve = action.fcurves.find('pose.bones["c_foot_ik.r"]["ik_fk_switch"]') 61 | if not foot_r_switch_curve: 62 | foot_r_switch_curve = action.fcurves.new('pose.bones["c_foot_ik.r"]["ik_fk_switch"]') 63 | 64 | for i in range(self.frame_start, self.frame_end): 65 | bpy.context.scene.frame_set(i) 66 | #print(bone_hand_l["ik_fk_switch"]) 67 | #print(rig.pose.bones["c_hand_ik.l"]["ik_fk_switch"]) 68 | hand_l_switch_curve.keyframe_points.insert(i, bone_hand_l["ik_fk_switch"], options={'FAST'}) 69 | hand_r_switch_curve.keyframe_points.insert(i, bone_hand_r["ik_fk_switch"], options={'FAST'}) 70 | foot_l_switch_curve.keyframe_points.insert(i, bone_foot_l["ik_fk_switch"], options={'FAST'}) 71 | foot_r_switch_curve.keyframe_points.insert(i, bone_foot_r["ik_fk_switch"], options={'FAST'}) 72 | 73 | anim_data.action_influence = influence 74 | 75 | 76 | def bake_complicated_but_fast(self): 77 | wm = bpy.context.window_manager 78 | rig = wm.character_rig 79 | 80 | #I cant think of a better name, I am sorry 81 | #This list holds Fcurve_Datas with unique data_paths and array_indices 82 | datas_list = Fcurve_Datas_List() 83 | 84 | for track in rig.animation_data.nla_tracks: 85 | for strip in track.strips: 86 | for fcurve in strip.action.fcurves: 87 | if 'ik_fk_switch' in fcurve.data_path: 88 | data = Fcurve_Data(track, strip, fcurve) 89 | datas_list.add_with_unique_path(data) 90 | 91 | for datas in datas_list.datas_list: 92 | print("----------------") 93 | print("Unique Path List") 94 | print("Path: " + datas.data_path + ": " + str(datas.array_index)) 95 | for i in range(160, 310): 96 | print(datas.evaluate(i)) 97 | for data in datas.Fcurve_List: 98 | print("----------------") 99 | print("Data:") 100 | print("Path: " + data.data_path) 101 | print("Index: " + str(data.array_index)) 102 | print("fcurve: " + str(data.fcurve)) 103 | print("Blending: " + str(data.blend_mode)) 104 | print("Extrapolation: " + str(data.hold_mode)) 105 | print("Active: " + str(data.active)) 106 | 107 | class Fcurve_Data: 108 | 109 | def __init__(self, track, strip, fcurve): 110 | self.track = track 111 | self.strip = strip 112 | self.data_path = fcurve.data_path 113 | self.array_index = fcurve.array_index 114 | self.fcurve = fcurve 115 | self.postition = 0 116 | self.blend_mode = strip.blend_mode 117 | self.hold_mode = strip.extrapolation 118 | self.active = strip.active 119 | 120 | class Fcurve_Datas: 121 | 122 | def __init__(self, data_path, array_index): 123 | self.data_path = data_path 124 | self.array_index = array_index 125 | self.Fcurve_List = [] 126 | 127 | def add_fcurve_data(self, fcurve_data: Fcurve_Data): 128 | if fcurve_data.data_path == self.data_path and fcurve_data.array_index == self.array_index: 129 | self.Fcurve_List.append(fcurve_data) 130 | 131 | def evaluate(self, frame): 132 | for data in self.Fcurve_List: 133 | pass 134 | 135 | class Fcurve_Datas_List: 136 | 137 | def __init__(self): 138 | self.datas_list = [] 139 | 140 | def add_with_unique_path(self, fcurve_data: Fcurve_Data): 141 | if len(self.datas_list) == 0: 142 | datas = Fcurve_Datas(fcurve_data.data_path, fcurve_data.array_index) 143 | self.datas_list.append(datas) 144 | datas.add_fcurve_data(fcurve_data) 145 | else: 146 | for datas in self.datas_list: 147 | if fcurve_data.data_path == datas.data_path and fcurve_data.array_index == datas.array_index: 148 | datas.add_fcurve_data(fcurve_data) 149 | return 150 | datas = Fcurve_Datas(fcurve_data.data_path, fcurve_data.array_index) 151 | self.datas_list.append(datas) 152 | datas.add_fcurve_data(fcurve_data) 153 | 154 | 155 | 156 | 157 | 158 | def register(): 159 | bpy.utils.register_class(FPB_OT_PropertyBake_OP) 160 | 161 | def unregister(): 162 | bpy.utils.unregister_class(FPB_OT_PropertyBake_OP) -------------------------------------------------------------------------------- /retarget_tools/frt_retarget_tools_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from math import radians 3 | 4 | from bpy.types import Operator 5 | 6 | 7 | class FRT_OT_Retarget_Op(Operator): 8 | 9 | bl_idname = "object.retarget" 10 | bl_label = "Retarget Thumbs" 11 | bl_description = "Retarget the thumbs from Mocap to Character Rig." 12 | bl_options = {"REGISTER", "UNDO"} 13 | 14 | # should only work in object mode and if both armatures are not null 15 | @classmethod 16 | def poll(cls, context): 17 | 18 | if (bpy.context.window_manager.retargetArmature is None): 19 | return False 20 | 21 | if (bpy.context.window_manager.mocapSourceArmature is None): 22 | return False 23 | 24 | if (bpy.context.window_manager.mocapSourceArmature == bpy.context.window_manager.retargetArmature): 25 | return False 26 | 27 | obj = context.object 28 | if not obj: 29 | return True 30 | if obj: 31 | if obj.mode == "OBJECT": 32 | return True 33 | return False 34 | 35 | def execute(self, context): 36 | 37 | frame_sta = bpy.context.scene.frame_start 38 | frame_ende = bpy.context.scene.frame_end 39 | 40 | #frame_sta = bpy.context.scene.num7 41 | #frame_ende = bpy.context.scene.num8 42 | 43 | rig_input = bpy.context.window_manager.retargetArmature.name 44 | rig_mocap = bpy.context.window_manager.mocapSourceArmature.name 45 | 46 | # Empty names 47 | 48 | m_thumb1_l = 'LeftHandThumb1' 49 | m_thumb2_l = 'LeftHandThumb2' 50 | m_thumb3_l = 'LeftHandThumb3' 51 | 52 | m_thumb1_r = 'RightHandThumb1' 53 | m_thumb2_r = 'RightHandThumb2' 54 | m_thumb3_r = 'RightHandThumb3' 55 | 56 | thumb1_l = 'c_thumb1.l' 57 | thumb2_l = 'c_thumb2.l' 58 | thumb3_l = 'c_thumb3.l' 59 | 60 | thumb1_r = 'c_thumb1.r' 61 | thumb2_r = 'c_thumb2.r' 62 | thumb3_r = 'c_thumb3.r' 63 | 64 | thumb1_l_empty = 'thumb1_l_empty' 65 | thumb2_l_empty = 'thumb2_l_empty' 66 | thumb3_l_empty = 'thumb3_l_empty' 67 | 68 | thumb1_r_empty = 'thumb1_r_empty' 69 | thumb2_r_empty = 'thumb2_r_empty' 70 | thumb3_r_empty = 'thumb3_r_empty' 71 | 72 | def addCopyRotLocThumb(rig, bone): 73 | obj = bpy.context.active_object 74 | constraint = obj.constraints.new(type='COPY_ROTATION') 75 | target_obj = bpy.data.objects[rig] 76 | target_bone = bone 77 | constraint.target = target_obj 78 | constraint.subtarget = target_bone 79 | obj.constraints["Copy Rotation"].mix_mode = 'OFFSET' 80 | 81 | constraint = obj.constraints.new(type='COPY_LOCATION') 82 | constraint.target = target_obj 83 | constraint.subtarget = target_bone 84 | 85 | def EnterPoseMode(rig): 86 | bpy.ops.object.select_all(action='DESELECT') 87 | 88 | ob = bpy.data.objects[rig] 89 | bpy.context.view_layer.objects.active = ob 90 | ob.select_set(True) 91 | bpy.ops.object.mode_set(mode='POSE') 92 | 93 | def createEmpty(name, type, radi, loc, rot): 94 | bpy.ops.object.empty_add( 95 | type=type, radius=radi, location=loc, rotation=rot) 96 | bpy.context.active_object.name = name 97 | 98 | def copyRotBone(rig, bone, empty): 99 | obj = bpy.data.objects[rig].pose.bones[bone] 100 | constraint = obj.constraints.new(type='COPY_ROTATION') 101 | target_obj = bpy.data.objects[empty] 102 | constraint.target = target_obj 103 | 104 | # selects Bones without deselecting already selected 105 | def boneMakeActive(bone): 106 | boneToSelect = bpy.data.objects[rig_input].pose.bones[bone].bone 107 | bpy.context.object.data.bones.active = boneToSelect 108 | #Select in viewport 109 | boneToSelect.select = True 110 | 111 | def deleteEmpties(empty): 112 | 113 | object_to_delete = bpy.data.objects[empty] 114 | bpy.data.objects.remove(object_to_delete, do_unlink=True) 115 | 116 | def createThumbEmpties(): 117 | 118 | createEmpty(thumb1_l_empty, 'PLAIN_AXES', 0.05, 119 | (0, 0, 0), (0, radians(-90), 0)) 120 | addCopyRotLocThumb(rig_mocap, m_thumb1_l) 121 | createEmpty(thumb2_l_empty, 'PLAIN_AXES', 0.05, 122 | (0, 0, 0), (0, radians(-90), 0)) 123 | addCopyRotLocThumb(rig_mocap, m_thumb2_l) 124 | createEmpty(thumb3_l_empty, 'PLAIN_AXES', 0.05, 125 | (0, 0, 0), (0, radians(-90), 0)) 126 | addCopyRotLocThumb(rig_mocap, m_thumb3_l) 127 | 128 | createEmpty(thumb1_r_empty, 'PLAIN_AXES', 0.05, 129 | (0, 0, 0), (0, radians(90), 0)) 130 | addCopyRotLocThumb(rig_mocap, m_thumb1_r) 131 | createEmpty(thumb2_r_empty, 'PLAIN_AXES', 0.05, 132 | (0, 0, 0), (0, radians(90), 0)) 133 | addCopyRotLocThumb(rig_mocap, m_thumb2_r) 134 | createEmpty(thumb3_r_empty, 'PLAIN_AXES', 0.05, 135 | (0, 0, 0), (0, radians(90), 0)) 136 | addCopyRotLocThumb(rig_mocap, m_thumb3_r) 137 | 138 | def thumbBoneConstraints(): 139 | copyRotBone(rig_input, thumb1_l, thumb1_l_empty) 140 | copyRotBone(rig_input, thumb2_l, thumb2_l_empty) 141 | copyRotBone(rig_input, thumb3_l, thumb3_l_empty) 142 | 143 | copyRotBone(rig_input, thumb1_r, thumb1_r_empty) 144 | copyRotBone(rig_input, thumb2_r, thumb2_r_empty) 145 | copyRotBone(rig_input, thumb3_r, thumb3_r_empty) 146 | 147 | def bakeThumbs(): 148 | bpy.ops.pose.select_all(action='DESELECT') 149 | 150 | boneMakeActive(thumb1_l) 151 | boneMakeActive(thumb2_l) 152 | boneMakeActive(thumb3_l) 153 | 154 | boneMakeActive(thumb1_r) 155 | boneMakeActive(thumb2_r) 156 | boneMakeActive(thumb3_r) 157 | 158 | bpy.ops.nla.bake(frame_start=frame_sta, frame_end=frame_ende, 159 | visual_keying=True, use_current_action=True, bake_types={'POSE'}) 160 | bpy.ops.constraint.delete(constraint="Copy Rotation", owner='BONE') 161 | 162 | bpy.ops.object.mode_set(mode='OBJECT') 163 | 164 | ####transferThumb animation#### 165 | 166 | createThumbEmpties() 167 | thumbBoneConstraints() 168 | 169 | EnterPoseMode(rig_input) 170 | bakeThumbs() 171 | 172 | bpy.ops.object.mode_set(mode='OBJECT') 173 | 174 | # delete Thumb empties 175 | deleteEmpties(thumb1_l_empty) 176 | deleteEmpties(thumb2_l_empty) 177 | deleteEmpties(thumb3_l_empty) 178 | 179 | deleteEmpties(thumb1_r_empty) 180 | deleteEmpties(thumb2_r_empty) 181 | deleteEmpties(thumb3_r_empty) 182 | 183 | return{'FINISHED'} 184 | 185 | 186 | def register(): 187 | bpy.utils.register_class(FRT_OT_Retarget_Op) 188 | 189 | 190 | def unregister(): 191 | bpy.utils.unregister_class(FRT_OT_Retarget_Op) 192 | -------------------------------------------------------------------------------- /animation_mover/anm_copy_ops.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import copy 3 | 4 | class ANM_OT_CopyAnimData_OP(bpy.types.Operator): 5 | bl_idname = "anm.copy_anim_data" 6 | bl_label = "Copy Animation Data (Keep Drivers)" 7 | bl_description = "Copies Animation Data from the active to all selected objects, with the exception of drivers" 8 | bl_options = {"REGISTER", "UNDO"} 9 | 10 | @classmethod 11 | def poll(cls, context): 12 | return True 13 | 14 | def execute(self, context): 15 | 16 | selectedObjects = context.selected_objects 17 | activeObject = context.active_object 18 | 19 | # Copy Active object to temporarily store drivers from objects to copy animation to 20 | #active_object_copy = activeObject.copy() 21 | #context.scene.collection.objects.link(active_object_copy) 22 | # 23 | #print("From / Before" + str(len(activeObject.animation_data.drivers))) 24 | #for object in selectedObjects: 25 | # if object == activeObject: 26 | # continue 27 | # print("To / Before" + str(len(object.animation_data.drivers))) 28 | # # Remove all drivers from active object copy 29 | # if active_object_copy.animation_data and active_object_copy.animation_data.drivers: 30 | # for driver in active_object_copy.animation_data.drivers: 31 | # active_object_copy.animation_data.drivers.remove(driver) 32 | # # Copy all Drivers from the Target Object on the copy object 33 | # if object.animation_data and object.animation_data.drivers: 34 | # for driver in object.animation_data.drivers: 35 | # if not active_object_copy.animation_data: 36 | # active_object_copy.animation_data.create() 37 | # 38 | # try: 39 | # new_driver = active_object_copy.driver_add(driver.data_path, driver.array_index) 40 | # except: 41 | # new_driver = active_object_copy.driver_add(driver.data_path) 42 | # copy_driver(driver, new_driver) 43 | # 44 | # # Select the target and copy object for the animation link operator 45 | # for obj in context.selected_objects: 46 | # obj.select_set(False) 47 | # object.select_set(True) 48 | # bpy.context.view_layer.objects.active = active_object_copy 49 | # bpy.ops.object.make_links_data(type='ANIMATION') 50 | # 51 | # print("To / After" + str(len(object.animation_data.drivers))) 52 | ## Remove the temporary copy object from the file after all Objects have the copied animation 53 | #bpy.data.objects.remove(active_object_copy, do_unlink=True) 54 | # 55 | ## Reset the selection to the original state (Before the Operator was started) 56 | # 57 | #for object in selectedObjects: 58 | # object.select_set(True) 59 | # 60 | # # Reset the use_nla flag so the animation gets re-evaluated (Otherwise Animation does not update on Linked Objects) 61 | # object.animation_data.use_nla = False 62 | # object.animation_data.use_nla = True 63 | # 64 | #context.view_layer.objects.active = activeObject 65 | 66 | bpy.ops.object.make_links_data(type='ANIMATION') 67 | 68 | for object in selectedObjects: 69 | if object == activeObject: 70 | continue 71 | 72 | for driver in object.animation_data.drivers: 73 | for var in driver.driver.variables: 74 | for target in var.targets: 75 | if target.id.name == activeObject.name: 76 | target.id = object 77 | 78 | object.animation_data.use_nla = False 79 | object.animation_data.use_nla = True 80 | 81 | return {'FINISHED'} 82 | 83 | def has_old_targets(driver, old_obj): 84 | for var in driver.driver.variables: 85 | for target in var.targets: 86 | #print(target.id.name) 87 | #print(old_obj.name) 88 | if not target.id or target.id.name == old_obj.name: 89 | return True 90 | return False 91 | 92 | def copy_driver(old_driver, new_driver): 93 | 94 | new_driver.driver.expression = old_driver.driver.expression 95 | 96 | for var in old_driver.driver.variables: 97 | copy_variable(var, new_driver.driver) 98 | return new_driver 99 | 100 | def copy_variable(old_var, target_driver): 101 | new_var = target_driver.variables.new() 102 | new_var.type = old_var.type 103 | new_var.name = old_var.name 104 | 105 | index = 0 106 | for target in old_var.targets: 107 | copy_target(target, new_var, index) 108 | index = index + 1 109 | 110 | def copy_target(old_target, target_variable, index): 111 | target_variable.targets[index].data_path = old_target.data_path 112 | target_variable.targets[index].id = old_target.id 113 | 114 | 115 | def copy_anim_data(from_anim_data, to_anim_data): 116 | to_anim_data.action = from_anim_data.action 117 | to_anim_data.action_blend_type = from_anim_data.action_blend_type 118 | to_anim_data.action_extrapolation = from_anim_data.action_extrapolation 119 | to_anim_data.action_influence = from_anim_data.action_influence 120 | 121 | for track in to_anim_data.nla_tracks: 122 | to_anim_data.nla_tracks.remove(track) 123 | 124 | for track in from_anim_data.nla_tracks: 125 | copy_nla_track_to_anim_data(track, to_anim_data) 126 | 127 | to_anim_data.use_nla = from_anim_data.use_nla 128 | to_anim_data.use_pin = from_anim_data.use_pin 129 | to_anim_data.use_tweak_mode = from_anim_data.use_tweak_mode 130 | 131 | def copy_nla_track_to_anim_data(track, anim_data): 132 | new_track = anim_data.nla_tracks.new() 133 | 134 | new_track.lock = track.lock 135 | new_track.mute = track.mute 136 | new_track.name = track.name 137 | new_track.select = track.select 138 | for strip in track.strips: 139 | copy_nla_strip_to_track(strip, new_track) 140 | 141 | def copy_nla_strip_to_track(strip, track): 142 | new_strip = track.strips.new(strip.name, strip.frame_start, strip.action) 143 | new_strip.action = strip.action 144 | new_strip.action_frame_end = strip.action_frame_end 145 | new_strip.action_frame_start = strip.action_frame_start 146 | #new_strip.active = strip.active 147 | new_strip.blend_in = strip.blend_in 148 | new_strip.blend_out = strip.blend_out 149 | new_strip.blend_type = strip.blend_type 150 | new_strip.extrapolation = strip.extrapolation 151 | #for fcurve in strip.fcurves: 152 | # copy_fcurve_to_strip(fcurve, strip) 153 | new_strip.frame_end = strip.frame_end 154 | #new_strip.frame_end_ui = strip.frame_end_ui 155 | new_strip.frame_start = strip.frame_start 156 | #new_strip.frame_start_ui = strip.frame_start_ui 157 | new_strip.mute = strip.mute 158 | new_strip.name = strip.name 159 | new_strip.repeat = strip.repeat 160 | new_strip.scale = strip.scale 161 | new_strip.select = strip.select 162 | new_strip.strip_time = strip.strip_time 163 | #new_strip.type = strip.type 164 | new_strip.use_animated_influence = strip.use_animated_influence 165 | new_strip.use_animated_time = strip.use_animated_time 166 | new_strip.use_animated_time_cyclic = strip.use_animated_time_cyclic 167 | new_strip.use_auto_blend = strip.use_auto_blend 168 | new_strip.use_reverse = strip.use_reverse 169 | new_strip.use_sync_length = strip.use_sync_length 170 | 171 | 172 | 173 | def copy_fcurve_to_strip(fcurve, strip): 174 | strip.fcurves.append(fcurve) 175 | def register(): 176 | bpy.utils.register_class(ANM_OT_CopyAnimData_OP) 177 | 178 | def unregister(): 179 | bpy.utils.unregister_class(ANM_OT_CopyAnimData_OP) -------------------------------------------------------------------------------- /chr_shader_tools/cst_shading_toolbox/cst_shading_tools_ops.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | from bpy_extras.io_utils import ImportHelper 5 | from ...fchar_settings import Settings 6 | 7 | class CST_OT_ChooseShaderPath_OP(Operator, ImportHelper): 8 | bl_idname = "settings.choose_shader_path" 9 | bl_label = "Select Character Shader" 10 | bl_description = "Select the .blend file with the Fritzi Character Shader" 11 | 12 | def execute(self, context): 13 | settings = Settings() 14 | settings.set_setting("shader_path", self.filepath) 15 | 16 | self.report({'INFO'}, "Path set!") 17 | 18 | return {'FINISHED'} 19 | 20 | class CST_OT_ChooseEyeShaderPath_OP(Operator, ImportHelper): 21 | bl_idname = "settings.choose_eye_shader_path" 22 | bl_label = "Select Character Eye Shader" 23 | bl_description = "Select the .blend file with the Fritzi Character Eye Shaders" 24 | 25 | def execute(self, context): 26 | settings = Settings() 27 | settings.set_setting("eye_shader_path", self.filepath) 28 | 29 | self.report({'INFO'}, "Path set!") 30 | 31 | return {'FINISHED'} 32 | 33 | class CST_OT_LinkCharacterShader_OP(Operator): 34 | bl_idname = "cst.link_character_shader" 35 | bl_label = "Link Character Shader" 36 | bl_description = "Links the Character Shader into the file." 37 | 38 | def execute(self, context): 39 | settings = Settings() 40 | filepath = settings.get_setting("shader_path") 41 | 42 | with bpy.data.libraries.load(filepath, link = True, relative = True) as (data_from, data_to): 43 | data_to.node_groups = ["shader-Fritzi_Characters"] 44 | 45 | return {'FINISHED'} 46 | 47 | class CST_OT_LinkEyeShader_OP(Operator): 48 | bl_idname = "cst.link_eye_shader" 49 | bl_label = "Link Character Eye Shader" 50 | bl_description = "Links the Character Eye Shader into the file." 51 | 52 | def execute(self, context): 53 | settings = Settings() 54 | filepath = settings.get_setting("eye_shader_path") 55 | 56 | with bpy.data.libraries.load(filepath, link = True, relative = True) as (data_from, data_to): 57 | 58 | data_to.node_groups = ["shader-Fritzi_Characters-eye_LEFT_simple", "shader-Fritzi_Characters-eye_RIGHT_simple"] 59 | 60 | 61 | return {'FINISHED'} 62 | 63 | def add_shader(context, rgb: tuple[float, float, float, float], shadowHSV: tuple[float, float, float] = (5.0, 1.0, 0.55), hasHighlights: bool = True): 64 | mat = context.material 65 | tree = mat.node_tree 66 | for node in tree.nodes: 67 | node.select = False 68 | rgb_node = mat.node_tree.nodes.new("ShaderNodeRGB") 69 | hsv_node = mat.node_tree.nodes.new("ShaderNodeHueSaturation") 70 | fritzi_shader_node = mat.node_tree.nodes.new("ShaderNodeGroup") 71 | fritzi_shader_node.node_tree = bpy.data.node_groups.get("shader-Fritzi_Characters") 72 | 73 | # Node linking 74 | 75 | tree.links.new(rgb_node.outputs["Color"], hsv_node.inputs["Color"]) 76 | tree.links.new(hsv_node.outputs["Color"], fritzi_shader_node.inputs["MainColor"]) 77 | 78 | # Position 79 | 80 | rgb_node.location = (-165, 0) 81 | fritzi_shader_node.location = (175, 0) 82 | fritzi_shader_node.width = 200 83 | 84 | # Settings 85 | 86 | rgb_node.outputs[0].default_value = rgb 87 | fritzi_shader_node.inputs["ShadowHue"].default_value = shadowHSV[0] 88 | fritzi_shader_node.inputs["ShadowSaturation"].default_value = shadowHSV[1] 89 | fritzi_shader_node.inputs["ShadowValue"].default_value = shadowHSV[2] 90 | 91 | if not hasHighlights: 92 | fritzi_shader_node.inputs["OverrideCharacterHighlight"].default_value = 1.0 93 | fritzi_shader_node.inputs["OverrideCharacterRimlight"].default_value = 1.0 94 | 95 | 96 | class CST_OT_CreateH1Shader_OP(Operator): 97 | bl_idname = "cst.create_h1_shader" 98 | bl_label = "H1 Skin Shader" 99 | bl_description = "Creates the Skin Shader Setup for the H1 Skin Tone" 100 | 101 | def execute(self, context): 102 | 103 | add_shader(context, (1.000000, 0.791298, 0.637597, 1.000000), (4.6, 1.61, 0.72)) 104 | 105 | return bpy.ops.transform.translate('INVOKE_DEFAULT') 106 | 107 | class CST_OT_CreateH2Shader_OP(Operator): 108 | bl_idname = "cst.create_h2_shader" 109 | bl_label = "H2 Skin Shader" 110 | bl_description = "Creates the Skin Shader Setup for the H2 Skin Tone" 111 | 112 | def execute(self, context): 113 | add_shader(context, (1.000000, 0.701102, 0.558341, 1.000000), (4.75, 1.475, 0.63)) 114 | return bpy.ops.transform.translate('INVOKE_DEFAULT') 115 | 116 | class CST_OT_CreateH3Shader_OP(Operator): 117 | bl_idname = "cst.create_h3_shader" 118 | bl_label = "H3 Skin Shader" 119 | bl_description = "Creates the Skin Shader Setup for the H3 Skin Tone" 120 | 121 | def execute(self, context): 122 | add_shader(context, (0.973445, 0.637597, 0.508881, 1.000000), (4.8, 1.25, 0.6)) 123 | return bpy.ops.transform.translate('INVOKE_DEFAULT') 124 | 125 | class CST_OT_CreateH4Shader_OP(Operator): 126 | bl_idname = "cst.create_h4_shader" 127 | bl_label = "H4 Skin Shader" 128 | bl_description = "Creates the Skin Shader Setup for the H4 Skin Tone" 129 | 130 | def execute(self, context): 131 | add_shader(context, (1.000000, 0.846873, 0.701102, 1.000000), (4.6, 1.61, 0.63)) 132 | return bpy.ops.transform.translate('INVOKE_DEFAULT') 133 | 134 | class CST_OT_CreateClothShader_OP(Operator): 135 | bl_idname = "cst.create_cloth_shader" 136 | bl_label = "Cloth Shader" 137 | bl_description = "Creates a cloth shader setup with the given color" 138 | 139 | def execute(self, context): 140 | add_shader(context, context.window_manager.shader_color) 141 | return bpy.ops.transform.translate('INVOKE_DEFAULT') 142 | 143 | class CST_OT_CreateMouthInsideShader_Op(Operator): 144 | bl_idname = "cst.create_mouthinside_shader" 145 | bl_label = "MouthInside Shader" 146 | bl_description = "Creates the Mouthinside Shader Nodes" 147 | 148 | def execute(self, context): 149 | add_shader(context, (0.09, 0.02, 0.02, 1.000000), (5.0, 1.0, 1.0), False) 150 | return bpy.ops.transform.translate('INVOKE_DEFAULT') 151 | 152 | class CST_OT_CreateTongueShader_Op(Operator): 153 | bl_idname = "cst.create_tongue_shader" 154 | bl_label = "Tongue Shader" 155 | bl_description = "Creates the Tongue Shader Nodes" 156 | 157 | def execute(self, context): 158 | add_shader(context, (0.195647, 0.029978, 0.023284, 1.000000), (5.0, 1.0, 1.0), False) 159 | return bpy.ops.transform.translate('INVOKE_DEFAULT') 160 | 161 | def register(): 162 | bpy.utils.register_class(CST_OT_ChooseShaderPath_OP) 163 | bpy.utils.register_class(CST_OT_ChooseEyeShaderPath_OP) 164 | bpy.utils.register_class(CST_OT_LinkCharacterShader_OP) 165 | bpy.utils.register_class(CST_OT_LinkEyeShader_OP) 166 | bpy.utils.register_class(CST_OT_CreateH1Shader_OP) 167 | bpy.utils.register_class(CST_OT_CreateH2Shader_OP) 168 | bpy.utils.register_class(CST_OT_CreateH3Shader_OP) 169 | bpy.utils.register_class(CST_OT_CreateH4Shader_OP) 170 | bpy.utils.register_class(CST_OT_CreateClothShader_OP) 171 | bpy.utils.register_class(CST_OT_CreateMouthInsideShader_Op) 172 | bpy.utils.register_class(CST_OT_CreateTongueShader_Op) 173 | 174 | def unregister(): 175 | bpy.utils.unregister_class(CST_OT_CreateTongueShader_Op) 176 | bpy.utils.unregister_class(CST_OT_CreateMouthInsideShader_Op) 177 | bpy.utils.unregister_class(CST_OT_CreateClothShader_OP) 178 | bpy.utils.unregister_class(CST_OT_CreateH4Shader_OP) 179 | bpy.utils.unregister_class(CST_OT_CreateH3Shader_OP) 180 | bpy.utils.unregister_class(CST_OT_CreateH2Shader_OP) 181 | bpy.utils.unregister_class(CST_OT_CreateH1Shader_OP) 182 | bpy.utils.unregister_class(CST_OT_LinkEyeShader_OP) 183 | bpy.utils.unregister_class(CST_OT_LinkCharacterShader_OP) 184 | bpy.utils.unregister_class(CST_OT_ChooseEyeShaderPath_OP) 185 | bpy.utils.unregister_class(CST_OT_ChooseShaderPath_OP) -------------------------------------------------------------------------------- /rig_tools/fchar_rig_tools_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Operator 3 | 4 | class FCHAR_OT_CreateSpineMaster_OP(Operator): 5 | bl_idname = "armature.create_spine_master" 6 | bl_label = "Create spine master" 7 | bl_description = "Creates a master bone to control the entire spine" 8 | bl_options = {"REGISTER", "UNDO"} 9 | 10 | # should only work in object mode 11 | @classmethod 12 | def poll(cls, context): 13 | 14 | if not bpy.context.object: 15 | return False 16 | 17 | obj = bpy.context.object 18 | if obj: 19 | if obj.mode == "OBJECT": 20 | return True 21 | 22 | return False 23 | 24 | def execute(self, context): 25 | 26 | # custom shape creation 27 | 28 | verts = [(1.1303999423980713, 0.1547279953956604, -0.13347600400447845), (1.1210072040557861, 0.15472817420959473, -0.2768296003341675), (1.0929802656173706, 0.15472817420959473, -0.41773033142089844), (1.0468016862869263, 0.15472817420959473, -0.5537672638893127), (0.9832625389099121, 0.15384173393249512, -0.6826573610305786), (0.9034484624862671, 0.12809038162231445, -0.8033981323242188), (0.8087260723114014, 0.07397103309631348, -0.914121150970459), (0.7007160782814026, 0.0012869834899902344, -1.0124871730804443), (0.5812661051750183, -0.08016180992126465, -1.0963842868804932), (0.45242050290107727, -0.16057252883911133, -1.1639546155929565), (0.31638363003730774, -0.23014426231384277, -1.2136205434799194), (0.17548291385173798, -0.27907490730285645, -1.2440999746322632), (0.03212952986359596, -0.2975635528564453, -1.2544227838516235), (-0.11122384667396545, -0.27907490730285645, -1.2440999746322632), (-0.252124547958374, -0.23014426231384277, -1.2136204242706299), (-0.38816142082214355, -0.16057252883911133, -1.163954496383667), (-0.5170074105262756, -0.08016180992126465, -1.096384048461914), (-0.636457085609436, 0.0012865066528320312, -1.0124871730804443), (-0.74446702003479, 0.07397103309631348, -0.9141212701797485), (-0.8391894698143005, 0.12809038162231445, -0.8033981323242188), (-0.9190031290054321, 0.15384173393249512, -0.6826575994491577), (-0.9825426936149597, 0.15472817420959473, -0.5537673830986023), (-1.0287212133407593, 0.15472817420959473, -0.4177304208278656), (-1.0567479133605957, 0.15472817420959473, -0.27682948112487793), (-1.0661439895629883, 0.15472817420959473, -0.133476123213768), (-1.0567481517791748, 0.15472817420959473, 0.00987716019153595), (-1.0287212133407593, 0.15472817420959473, 0.15077799558639526), (-0.9825425744056702, 0.15472817420959473, 0.28681468963623047), (-0.9190031290054321, 0.15384173393249512, 0.4156160056591034), (-0.8391894698143005, 0.12809038162231445, 0.5337749719619751), (-0.7444671392440796, 0.07397103309631348, 0.6390721797943115), (-0.6364571452140808, 0.0012869834899902344, 0.7301508188247681), (-0.5170069932937622, -0.08016180992126465, 0.8058822154998779), (-0.3881615400314331, -0.16057252883911133, 0.8653900623321533), (-0.252124547958374, -0.23014426231384277, 0.9080815315246582), (-0.11122409999370575, -0.27907490730285645, 0.9336552619934082), (0.03212953358888626, -0.2975633144378662, 0.9421242475509644), (0.17548315227031708, -0.27907490730285645, 0.9336552619934082), (0.31638363003730774, -0.23014426231384277, 0.9080815315246582), (0.4524206817150116, -0.16057205200195312, 0.8653900623321533), (0.5812661051750183, -0.08016180992126465, 0.8058822154998779), (0.7007163166999817, 0.0012869834899902344, 0.7301508188247681), (0.8087260723114014, 0.07397103309631348, 0.6390721797943115), (0.9034485816955566, 0.12809038162231445, 0.5337749719619751), (0.983262300491333, 0.15384173393249512, 0.41561636328697205), (1.0468019247055054, 0.15472817420959473, 0.28681468963623047), (1.0929802656173706, 0.15472817420959473, 0.1507776379585266), (1.121006965637207, 0.15472817420959473, 0.00987716019153595)] 29 | edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), 30 | (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (31, 32), (32, 33), (33, 34), (34, 35), (35, 36), (36, 37), (37, 38), (38, 39), (39, 40), (40, 41), (41, 42), (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (47, 0)] 31 | mesh = bpy.data.meshes.new(('cs_spine_master')) 32 | mesh.from_pydata(verts, edges, []) 33 | mesh.update() 34 | 35 | spine_ctrl = bpy.data.objects.new('cs_spine_master', mesh) 36 | bpy.context.collection.objects.link(spine_ctrl) 37 | 38 | arm_name = bpy.context.selected_objects[0].name 39 | arm = bpy.context.selected_objects[0] 40 | 41 | bpy.ops.object.editmode_toggle() 42 | 43 | ### create Spine Master Bone 44 | 45 | bpy.ops.armature.select_all(action='DESELECT') 46 | 47 | bone = bpy.data.objects[arm_name].data.edit_bones["c_spine_04.x"] 48 | bpy.data.objects[arm_name].data.edit_bones.active = bone 49 | arm.data.edit_bones.active.select_head = True 50 | arm.data.edit_bones.active.select_tail = True 51 | 52 | bpy.ops.armature.duplicate_move() 53 | bpy.data.objects[arm_name].data.edit_bones["c_spine_04.x.001"].name = "c_spine_fk_bend" 54 | 55 | ###create parent relations for c_spine_fk_bend 56 | 57 | bpy.data.objects[arm_name].data.edit_bones["c_spine_fk_bend"].parent = bpy.data.objects[arm_name].data.edit_bones["c_root_master.x"] 58 | 59 | 60 | ###change to pose mode 61 | bpy.ops.object.posemode_toggle() 62 | 63 | ##c_spine_04.x constraint 64 | 65 | def createTransformConst(bone): 66 | 67 | transformConstraint =bpy.data.objects[arm_name].pose.bones[bone].constraints.new('TRANSFORM') 68 | 69 | transformConstraint.target = arm 70 | transformConstraint.subtarget = "c_spine_fk_bend" 71 | 72 | transformConstraint.target_space = "LOCAL" 73 | transformConstraint.owner_space = "LOCAL" 74 | 75 | transformConstraint.influence = 0.4 76 | 77 | transformConstraint.map_from = "ROTATION" 78 | 79 | transformConstraint.from_min_x_rot = -180 80 | transformConstraint.from_max_x_rot = 180 81 | 82 | transformConstraint.from_min_y_rot = -180 83 | transformConstraint.from_max_y_rot = 180 84 | 85 | transformConstraint.from_min_z_rot = -180 86 | transformConstraint.from_max_z_rot = 180 87 | 88 | 89 | transformConstraint.map_to = "ROTATION" 90 | 91 | transformConstraint.to_min_x_rot = -150 92 | transformConstraint.to_max_x_rot = 150 93 | 94 | transformConstraint.to_min_y_rot = -150 95 | transformConstraint.to_max_y_rot = 150 96 | 97 | transformConstraint.to_min_z_rot = -150 98 | transformConstraint.to_max_z_rot = 150 99 | 100 | createTransformConst("c_spine_04.x") 101 | createTransformConst("c_spine_03.x") 102 | createTransformConst("c_spine_02.x") 103 | createTransformConst("c_spine_01.x") 104 | 105 | #lock location c_spine_fk_bend and custom shape scale 106 | 107 | bpy.context.object.pose.bones["c_spine_fk_bend"].lock_location[0] = True 108 | bpy.context.object.pose.bones["c_spine_fk_bend"].lock_location[1] = True 109 | bpy.context.object.pose.bones["c_spine_fk_bend"].lock_location[2] = True 110 | 111 | bpy.context.object.pose.bones["c_spine_fk_bend"].custom_shape = bpy.data.objects["cs_spine_master"] 112 | 113 | bpy.data.objects[arm_name].pose.bones["c_spine_fk_bend"].custom_shape_scale_xyz[0] = 2.0 114 | bpy.data.objects[arm_name].pose.bones["c_spine_fk_bend"].custom_shape_scale_xyz[1] = 2.0 115 | bpy.data.objects[arm_name].pose.bones["c_spine_fk_bend"].custom_shape_scale_xyz[2] = 1.5 116 | 117 | bpy.context.object.pose.bones["c_spine_fk_bend"].custom_shape_transform = bpy.data.objects[arm_name].pose.bones["spine_04.x"] 118 | 119 | return {'FINISHED'} 120 | 121 | 122 | 123 | 124 | def register(): 125 | bpy.utils.register_class(FCHAR_OT_CreateSpineMaster_OP) 126 | 127 | def unregister(): 128 | bpy.utils.unregister_class(FCHAR_OT_CreateSpineMaster_OP) -------------------------------------------------------------------------------- /mocap_retarget/fmr_retarget_pnl.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.props import FloatProperty, StringProperty, PointerProperty 3 | from bpy.types import PropertyGroup, UIList 4 | from .. import fchar_settings 5 | from . import fmr_scale_list_io 6 | import os 7 | 8 | 9 | class FMR_PT_Retarget_pnl(bpy.types.Panel): 10 | """Main Panel for the retarget Operators. Only is a container for subpanels""" 11 | bl_label = "MoCap Retarget" 12 | bl_category = "FCHAR" 13 | bl_space_type = "VIEW_3D" 14 | bl_region_type = "UI" 15 | bl_options = {"DEFAULT_CLOSED"} 16 | 17 | 18 | 19 | 20 | def draw(self, context): 21 | 22 | pass 23 | 24 | class FMR_PT_SingleRetarget_pnl(bpy.types.Panel): 25 | bl_label = "Single Retarget" 26 | bl_category = "FCHAR" 27 | bl_space_type = "VIEW_3D" 28 | bl_region_type = "UI" 29 | bl_parent_id = "FMR_PT_Retarget_pnl" 30 | 31 | def draw(self, context): 32 | 33 | settings = fchar_settings.Settings() 34 | 35 | perforce_path = settings.get_setting("perforce_path") 36 | 37 | 38 | 39 | scene = context.scene 40 | wm = context.window_manager 41 | layout = self.layout 42 | 43 | scale_list = fmr_scale_list_io.ScaleListDict() 44 | 45 | 46 | 47 | layout.prop_search(wm, "source_rig_pointer", scene, "objects", text="Source Armature") 48 | layout.prop_search(wm, "target_rig_pointer", scene, "objects", text="Target Armature") 49 | layout.prop(wm, "auto_scale_check") 50 | 51 | 52 | layout.operator("object.mocap_retarget") 53 | 54 | 55 | 56 | class FMR_PT_BatchRetarget_pnl(bpy.types.Panel): 57 | bl_label = "Batch Retarget" 58 | bl_category = "FCHAR" 59 | bl_space_type = "VIEW_3D" 60 | bl_region_type = "UI" 61 | bl_parent_id = "FMR_PT_Retarget_pnl" 62 | 63 | def draw(self, context): 64 | wm = context.window_manager 65 | scene = context.scene 66 | layout = self.layout 67 | settings = fchar_settings.Settings() 68 | 69 | perforce_path = settings.get_setting("perforce_path") 70 | 71 | # checks if the perforce directory is correctly set: If it is, the retarget Operators are displayed. 72 | # If not, an Operator to select the Perforce directory is displayed. 73 | if not (os.path.isdir(perforce_path) and os.path.isdir(os.path.join(perforce_path, "080_scenes")) and os.path.isdir(os.path.join(perforce_path, "075_capture"))): 74 | layout.label(text="No vaild Perforce Path set.") 75 | layout.label(text="Please set the Perforce Path") 76 | layout.operator("settings.choose_perforce_path") 77 | else: 78 | op = layout.operator("retarget.select_char_file") 79 | op.filepath = perforce_path 80 | op = layout.operator("retarget.select_bvhs") 81 | op.filepath = perforce_path 82 | 83 | 84 | if wm.bvh_files: 85 | row = layout.row() 86 | row.template_list("FMR_UL_BVHList_items", "", wm, "bvh_files", wm, "bvh_files_index", rows = 3) 87 | 88 | column = row.column(align=True) 89 | column.operator("object.add_bvh", icon='ADD', text="") 90 | column.operator("retarget.remove_bvh", icon='REMOVE', text="") 91 | 92 | layout.prop(wm, "crowd_check") 93 | 94 | layout.prop_search(wm, "target_rig_pointer", scene, "objects", text="Target Armature") 95 | 96 | layout.operator("retarget.mocap_batch_retarget") 97 | 98 | 99 | class FMR_PT_ScaleList_pnl(bpy.types.Panel): 100 | bl_label = "Scale List" 101 | bl_category = "FCHAR" 102 | bl_space_type = "VIEW_3D" 103 | bl_region_type = "UI" 104 | bl_parent_id = "FMR_PT_Retarget_pnl" 105 | 106 | def draw(self, context): 107 | wm = context.window_manager 108 | layout = self.layout 109 | 110 | row = layout.row() 111 | row.template_list("FMR_UL_ScaleList_items", "", wm, "scale_list", wm, "scale_list_index", rows=3) 112 | 113 | # Fetches the newest Scales every update. 114 | scale_list = fmr_scale_list_io.ScaleListDict() 115 | scale_list.push_to_properties() 116 | 117 | column = row.column(align=True) 118 | 119 | 120 | column.operator("object.add_character_scale", icon='ADD', text="") 121 | column.operator("object.remove_character_scale", icon='REMOVE', text="") 122 | 123 | row = layout.row(align=True) 124 | row.operator("file.import_scale_list") 125 | row.operator("file.export_scale_list") 126 | 127 | 128 | 129 | def ScaleUpdate(self, context): 130 | """This Method gets triggered if a Scaleproperty gets updated (Either by the User or through the API. 131 | The new property value gets set in the Scale List Dict.""" 132 | scale_list = fmr_scale_list_io.ScaleListDict() 133 | 134 | #print("scaleupdate") 135 | #print(self.name) 136 | #print(self.scale) 137 | scale_list.set_scale(self.name, self.scale) 138 | 139 | 140 | def NameUpdate(self, context): 141 | """ This Method gets triggered if the Name Property gets updated (Either by the User or through the API. 142 | Triggers the Scale List Dict to fetch all new Properties from the UI""" 143 | scale_list = fmr_scale_list_io.ScaleListDict() 144 | #print("Name") 145 | #print(self.character) 146 | 147 | scale_list.fetch_properties() 148 | 149 | class ScaleList(PropertyGroup): 150 | """ A PropertyGroup which can Store the Name and Scale of characters""" 151 | scale : FloatProperty(default=1.0, update=ScaleUpdate) 152 | character : StringProperty(default="", update=NameUpdate) 153 | 154 | class FMR_UL_ScaleList_items(UIList): 155 | @classmethod 156 | def poll(cls, context): 157 | return True 158 | 159 | def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): 160 | 161 | split = layout.split(factor=1.0) 162 | split.prop(item, "character", text="", emboss=False, translate=False) 163 | split = layout.split(factor=1.0) 164 | split.prop(item, "scale", text="", emboss=False, translate=False) 165 | 166 | 167 | def invoke(self, context, event): 168 | 169 | pass 170 | 171 | 172 | class FMR_UL_BVHList_items(UIList): 173 | @classmethod 174 | def poll(cls, context): 175 | return True 176 | 177 | def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): 178 | row = layout.row() 179 | row.prop(item, "name", text="", emboss=False, translate=False) 180 | row.enabled = False 181 | 182 | 183 | def update_source_rig(self, context): 184 | """Updates the Source Rig Property of ARP if a new Source Rig is selected in FCHAR""" 185 | wm = context.window_manager 186 | scene = context.scene 187 | #print("update") 188 | if (wm.source_rig_pointer): 189 | #print("setRig") 190 | scene.source_rig = wm.source_rig_pointer.name 191 | else: 192 | scene.source_rig = "" 193 | 194 | def update_target_rig(self, context): 195 | """Updates the Target Rig Property of ARP if a new Target Rig is selected in FCHAR""" 196 | wm = context.window_manager 197 | scene = context.scene 198 | 199 | if(wm.target_rig_pointer): 200 | scene.target_rig = wm.target_rig_pointer.name 201 | else: 202 | scene.target_rig = "" 203 | 204 | 205 | 206 | def register(): 207 | 208 | 209 | bpy.utils.register_class(FMR_PT_Retarget_pnl) 210 | bpy.utils.register_class(FMR_PT_SingleRetarget_pnl) 211 | bpy.utils.register_class(FMR_PT_BatchRetarget_pnl) 212 | bpy.utils.register_class(FMR_UL_BVHList_items) 213 | bpy.utils.register_class(FMR_PT_ScaleList_pnl) 214 | bpy.utils.register_class(ScaleList) 215 | bpy.utils.register_class(FMR_UL_ScaleList_items) 216 | 217 | 218 | def unregister(): 219 | bpy.utils.unregister_class(FMR_UL_ScaleList_items) 220 | bpy.utils.unregister_class(ScaleList) 221 | bpy.utils.unregister_class(FMR_PT_ScaleList_pnl) 222 | bpy.utils.unregister_class(FMR_UL_BVHList_items) 223 | bpy.utils.unregister_class(FMR_PT_BatchRetarget_pnl) 224 | bpy.utils.unregister_class(FMR_PT_SingleRetarget_pnl) 225 | bpy.utils.unregister_class(FMR_PT_Retarget_pnl) 226 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /driver_generator/utility_functions/fdg_driver_utils.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . import fdg_names 3 | from bpy.app.handlers import persistent 4 | 5 | def add_var(driver, source, name, transform_type='', transform_space = 'WORLD_SPACE', type='TRANSFORMS', source_bone="", rna_data_path="", id_type=""): 6 | """Adds an input variable to a driver, types can be 'TRANSFORMS' or 'SINGLE_PROP'""" 7 | if source is not None: 8 | var = driver.variables.new() 9 | var.name = name 10 | var.type = type 11 | if type == 'SINGLE_PROP': 12 | target = var.targets[0] 13 | if id_type: 14 | target.id_type = id_type 15 | target.id = source 16 | target.data_path = rna_data_path 17 | else: 18 | target = var.targets[0] 19 | target.id = source 20 | if source.type == 'ARMATURE': 21 | target.bone_target = source_bone 22 | target.transform_type = transform_type 23 | target.transform_space = transform_space 24 | 25 | 26 | def add_custom_property(prop_holder, name, default=0.0, prop_min=0.0, prop_max=1.0, description=''): 27 | """Adds a custom property to an object""" 28 | prop_holder[name] = default 29 | 30 | prop_ui = prop_holder.id_properties_ui(name) 31 | prop_ui.update(min= prop_min) 32 | prop_ui.update(max= prop_max) 33 | prop_ui.update(soft_min= prop_min) 34 | prop_ui.update(soft_max= prop_max) 35 | prop_ui.update(description= description) 36 | 37 | for area in bpy.context.screen.areas: 38 | area.tag_redraw() 39 | 40 | 41 | def parent_objects(parent, child, parent_bone_name=''): 42 | """Parents a child object to a parent object, or to a Bone belonging to the Parent if it is an Armature""" 43 | bpy.ops.object.select_all(action='DESELECT') 44 | child.select_set(True) 45 | parent.select_set(True) 46 | 47 | if parent.type == 'ARMATURE' and parent_bone_name != '': 48 | bpy.context.view_layer.objects.active = parent 49 | bpy.ops.object.mode_set(mode='EDIT') 50 | parent.data.edit_bones.active = parent.data.edit_bones[parent_bone_name] 51 | bpy.ops.object.mode_set(mode='OBJECT') 52 | 53 | bpy.ops.object.select_all(action='DESELECT') 54 | child.select_set(True) 55 | parent.select_set(True) 56 | bpy.context.view_layer.objects.active = parent 57 | 58 | bpy.ops.object.parent_set(type='BONE', keep_transform=True) 59 | 60 | else: 61 | bpy.context.view_layer.objects.active = parent 62 | bpy.ops.object.parent_set(keep_transform=True) 63 | 64 | bpy.ops.object.select_all(action='DESELECT') 65 | 66 | def parent_objects_noops(parent, child): 67 | child.parent = parent 68 | 69 | def append_function_unique(function_list, function): 70 | """Appends a unique copy of a function to a handler""" 71 | remove_function(function_list, function) 72 | function_list.append(function) 73 | 74 | 75 | def remove_function(function_list, function): 76 | """Removes all copies of a function from a function handler""" 77 | fn_name = function.__name__ 78 | fn_module = function.__module__ 79 | 80 | for i in range(len(function_list) - 1, -1, -1): 81 | if function_list[i].__name__ == fn_name and function_list[i].__module__ == fn_module: 82 | del function_list[i] 83 | 84 | def remove_driver_variables(driver): 85 | """Removes all Variables from a driver""" 86 | for var in driver.variables: 87 | driver.variables.remove(var) 88 | 89 | def add_driver_float_simple(driving_object, driving_prop_name, driven_object, driven_prop_name, driven_prop_index=-1): 90 | """Adds a simple driver of type Average from the driving prop to the driven prop""" 91 | if driven_prop_index != -1: 92 | driver = driven_object.driver_add(driven_prop_name, driven_prop_index).driver 93 | else: 94 | driver = driven_object.driver_add(driven_prop_name).driver 95 | 96 | driver.type = 'AVERAGE' 97 | 98 | #Removes all variables from the driver, so there are no duplicate variables 99 | remove_driver_variables(driver) 100 | var = driver.variables.new() 101 | var.name = "Amount" 102 | var.type = 'SINGLE_PROP' 103 | target = var.targets[0] 104 | target.id = driving_object 105 | target.data_path = driving_prop_name 106 | 107 | def add_driver_color_simple(driving_object, driving_prop_name, driven_object, driven_prop_name): 108 | """Adds simple drivers of type Average form the RGB Values of the driving prop to the RGB values of the driven prop""" 109 | 110 | add_driver_float_simple(driving_object, driving_prop_name + "[0]", driven_object, driven_prop_name, 0) 111 | 112 | add_driver_float_simple(driving_object, driving_prop_name + "[1]", driven_object, driven_prop_name, 1) 113 | 114 | add_driver_float_simple(driving_object, driving_prop_name + "[2]", driven_object, driven_prop_name, 2) 115 | 116 | def remove_property(object, prop_name): 117 | """Tries to remove property with the given name from the given object if it exists""" 118 | 119 | prop_value = object.get(prop_name) 120 | if prop_value is not None: 121 | del object[prop_name] 122 | 123 | def link_camera(camera): 124 | if not bpy.context.scene.gp_defaults.gp_object: 125 | return 126 | 127 | if camera is None: 128 | return 129 | 130 | cam_empties = [value for key, value in bpy.context.scene.objects.items() if fdg_names.empty_cam.lower() in key.lower()] 131 | 132 | if len(cam_empties) == 0 or len(cam_empties) > 1: 133 | 134 | for cam_empty in cam_empties: 135 | bpy.data.objects.remove(cam_empty, do_unlink=True) 136 | collection = bpy.context.scene.gp_defaults.outline_collection 137 | cam_empty = bpy.data.objects.new(fdg_names.empty_cam, None) 138 | collection.objects.link(cam_empty) 139 | 140 | 141 | cam_empty.location = (0, 0, 0) 142 | cam_empty.rotation_euler = (0, 0, 0) 143 | 144 | parent_objects_noops(camera, cam_empty) 145 | cam_empty.hide_viewport = True 146 | cam_empty.hide_render = True 147 | else: 148 | cam_empty = cam_empties[0] 149 | if cam_empty.parent is camera: 150 | return cam_empty 151 | 152 | cam_empty.hide_viewport = False 153 | #cam_empty.parent = None 154 | 155 | cam_empty.location = (0, 0, 0) 156 | cam_empty.rotation_euler = (0, 0, 0) 157 | 158 | 159 | parent_objects_noops(camera, cam_empty) 160 | cam_empty.hide_viewport = True 161 | cam_empty.hide_render = True 162 | 163 | return cam_empty 164 | 165 | 166 | 167 | def add_auto_link_handler(): 168 | 169 | append_function_unique(bpy.app.handlers.frame_change_pre, frame_change_handler_link_camera) 170 | 171 | def remove_auto_link_handler(): 172 | 173 | remove_function(bpy.app.handlers.frame_change_pre, frame_change_handler_link_camera) 174 | 175 | fn_name = "frame_change_handler" 176 | fn_module = "Fritzi-Characters.driver_generator.driver_gen_camera.fdg_driver_gen_camera_op" 177 | function_list = bpy.app.handlers.frame_change_pre 178 | for i in range(len(function_list) - 1, -1, -1): 179 | if function_list[i].__name__ == fn_name and function_list[i].__module__ == fn_module: 180 | del function_list[i] 181 | 182 | @persistent 183 | def frame_change_handler_link_camera(dummy): 184 | """Handler which gathers the active camera and links the camera empty to it""" 185 | camera = bpy.context.scene.camera 186 | link_camera(camera) 187 | 188 | 189 | def is_handler_in_file(): 190 | return frame_change_handler_link_camera in bpy.app.handlers.frame_change_pre 191 | 192 | @persistent 193 | def load_post_gp_overscan(dummy): 194 | 195 | gp_settings = bpy.context.scene.gp_defaults 196 | gp_ob = gp_settings.gp_object 197 | 198 | 199 | if not gp_ob: 200 | return 201 | 202 | for mod in gp_ob.grease_pencil_modifiers: 203 | if mod.type == 'GP_LINEART': 204 | mod.overscan = 0.05 205 | mod.use_image_boundary_trimming = True 206 | print("Changed modifier " + mod.name) 207 | 208 | 209 | 210 | def add_load_handler(): 211 | append_function_unique(bpy.app.handlers.load_post, load_post_gp_overscan) 212 | 213 | def remove_load_handler(): 214 | remove_function(bpy.app.handlers.load_post, load_post_gp_overscan) -------------------------------------------------------------------------------- /chr_shader_tools/cst_ShaderController/cst_add_eye_driver_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from bpy.types import Operator 3 | from ...driver_generator.utility_functions.fdg_driver_utils import add_var, remove_driver_variables 4 | 5 | class CST_OT_addEyeDrivers_OP(Operator): 6 | bl_idname = "object.add_eye_drivers" 7 | bl_label = "Link eyes to the rig" 8 | bl_description = "Adds drivers from the Rig to both eyes, so that the shader can access them." 9 | bl_options = {"REGISTER", "UNDO"} 10 | 11 | @classmethod 12 | def poll(cls, context): 13 | wm = context.window_manager 14 | 15 | if not wm.character_rig: 16 | return False 17 | 18 | if not wm.eye_left: 19 | return False 20 | 21 | if not wm.eye_right: 22 | return False 23 | 24 | return True 25 | 26 | def execute(self, context): 27 | 28 | wm = context.window_manager 29 | 30 | rig = wm.character_rig 31 | eye_l = wm.eye_left 32 | eye_r = wm.eye_right 33 | 34 | self.add_properties(eye_l, True) 35 | self.add_properties(eye_r, False) 36 | 37 | self.add_drivers(eye_l, True, wm) 38 | self.add_drivers(eye_r, False, wm) 39 | 40 | return {'FINISHED'} 41 | 42 | 43 | def add_properties(self, eye, is_left_eye: bool): 44 | suffix = ".R" 45 | if is_left_eye: 46 | suffix = ".L" 47 | 48 | eye["position_scale" + suffix] = (0.5, 0.5, 1.0, 1.0) 49 | 50 | eye["rotation" + suffix] = (0.0) 51 | 52 | def add_drivers(self, eye, is_left_eye: bool, wm): 53 | 54 | suffix = ".R" 55 | if is_left_eye: 56 | 57 | suffix = ".L" 58 | 59 | # Location X 60 | eye.driver_remove('["position_scale' + suffix + '"]', 0) 61 | driver = eye.driver_add('["position_scale' + suffix + '"]', 0).driver 62 | remove_driver_variables(driver) 63 | add_var(driver, source=wm.character_rig, name="target", transform_type='LOC_X', transform_space='LOCAL_SPACE', source_bone="ctl_eye" + suffix) 64 | 65 | driver.expression = "target*(50) + 0.5" 66 | 67 | 68 | # Location Y 69 | eye.driver_remove('["position_scale' + suffix + '"]', 1) 70 | driver = eye.driver_add('["position_scale' + suffix + '"]', 1).driver 71 | remove_driver_variables(driver) 72 | add_var(driver, source=wm.character_rig, name="target", transform_type='LOC_Z', transform_space='LOCAL_SPACE', source_bone="ctl_eye" + suffix) 73 | add_var(driver, source=wm.character_rig, name="up", transform_type='LOC_Z', transform_space='LOCAL_SPACE', source_bone="eye_up" + suffix) 74 | add_var(driver, source=wm.character_rig, name="down", transform_type='LOC_Z', transform_space='LOCAL_SPACE', source_bone="eye_down" + suffix) 75 | 76 | driver.expression = "(0.5 + (-50)*target) + (12.1*up) + (2.99*down)" 77 | 78 | # Scale X 79 | eye.driver_remove('["position_scale' + suffix + '"]', 2) 80 | driver = eye.driver_add('["position_scale' + suffix + '"]', 2).driver 81 | remove_driver_variables(driver) 82 | add_var(driver, source=wm.character_rig, name="target", transform_type='SCALE_X', transform_space='LOCAL_SPACE', source_bone="eye_track" + suffix) 83 | 84 | driver.expression = "target" 85 | 86 | # Scale Y 87 | eye.driver_remove('["position_scale' + suffix + '"]', 3) 88 | driver = eye.driver_add('["position_scale' + suffix + '"]', 3).driver 89 | remove_driver_variables(driver) 90 | add_var(driver, source=wm.character_rig, name="target", transform_type='SCALE_Z', transform_space='LOCAL_SPACE', source_bone="eye_track" + suffix) 91 | add_var(driver, source=wm.character_rig, name="up", transform_type='LOC_Z', transform_space='LOCAL_SPACE', source_bone="eye_up" + suffix) 92 | add_var(driver, source=wm.character_rig, name="down", transform_type='LOC_Z', transform_space='LOCAL_SPACE', source_bone="eye_down" + suffix) 93 | 94 | driver.expression = "target * (1 + 40.59*up) * (1 - 13.73*down)" 95 | 96 | # Rotation Z 97 | eye.driver_remove('["rotation' + suffix + '"]') 98 | driver = eye.driver_add('["rotation' + suffix + '"]').driver 99 | remove_driver_variables(driver) 100 | add_var(driver, source=wm.character_rig, name="target", transform_type='ROT_Y', transform_space='LOCAL_SPACE', source_bone="eye_track" + suffix) 101 | 102 | driver.expression = "target" 103 | 104 | class CST_OT_addEyeDriversNoUpDown_OP(Operator): 105 | bl_idname = "object.add_eye_drivers_no_up_down" 106 | bl_label = "Link eyes to a rig w/o up/down" 107 | bl_description = "Adds drivers from Rig which does not have eye_up/eye_down bones to both eyes, so that the shader can access them." 108 | bl_options = {"REGISTER", "UNDO"} 109 | 110 | 111 | @classmethod 112 | def poll(cls, context): 113 | wm = context.window_manager 114 | 115 | if not wm.character_rig: 116 | return False 117 | 118 | if not wm.eye_left: 119 | return False 120 | 121 | if not wm.eye_right: 122 | return False 123 | 124 | return True 125 | 126 | def execute(self, context): 127 | 128 | wm = context.window_manager 129 | 130 | rig = wm.character_rig 131 | eye_l = wm.eye_left 132 | eye_r = wm.eye_right 133 | 134 | self.add_properties(eye_l, True) 135 | self.add_properties(eye_r, False) 136 | 137 | self.add_drivers(eye_l, True, wm) 138 | self.add_drivers(eye_r, False, wm) 139 | 140 | return {'FINISHED'} 141 | 142 | 143 | def add_properties(self, eye, is_left_eye: bool): 144 | suffix = ".R" 145 | if is_left_eye: 146 | suffix = ".L" 147 | 148 | eye["position_scale" + suffix] = (0.5, 0.5, 1.0, 1.0) 149 | 150 | eye["rotation" + suffix] = (0.0) 151 | 152 | def add_drivers(self, eye, is_left_eye: bool, wm): 153 | 154 | suffix = ".R" 155 | if is_left_eye: 156 | 157 | suffix = ".L" 158 | 159 | # Location X 160 | eye.driver_remove('["position_scale' + suffix + '"]', 0) 161 | driver = eye.driver_add('["position_scale' + suffix + '"]', 0).driver 162 | remove_driver_variables(driver) 163 | add_var(driver, source=wm.character_rig, name="target", transform_type='LOC_X', transform_space='LOCAL_SPACE', source_bone="ctl_eye" + suffix) 164 | 165 | driver.expression = "target*(50) + 0.5" 166 | 167 | 168 | # Location Y 169 | eye.driver_remove('["position_scale' + suffix + '"]', 1) 170 | driver = eye.driver_add('["position_scale' + suffix + '"]', 1).driver 171 | remove_driver_variables(driver) 172 | add_var(driver, source=wm.character_rig, name="target", transform_type='LOC_Z', transform_space='LOCAL_SPACE', source_bone="ctl_eye" + suffix) 173 | 174 | 175 | driver.expression = "(0.5 + (-50)*target)" 176 | 177 | # Scale X 178 | eye.driver_remove('["position_scale' + suffix + '"]', 2) 179 | driver = eye.driver_add('["position_scale' + suffix + '"]', 2).driver 180 | remove_driver_variables(driver) 181 | add_var(driver, source=wm.character_rig, name="target", transform_type='SCALE_X', transform_space='LOCAL_SPACE', source_bone="eye_track" + suffix) 182 | 183 | driver.expression = "target" 184 | 185 | # Scale Y 186 | eye.driver_remove('["position_scale' + suffix + '"]', 3) 187 | driver = eye.driver_add('["position_scale' + suffix + '"]', 3).driver 188 | remove_driver_variables(driver) 189 | add_var(driver, source=wm.character_rig, name="target", transform_type='SCALE_Z', transform_space='LOCAL_SPACE', source_bone="eye_track" + suffix) 190 | 191 | driver.expression = "target" 192 | 193 | # Rotation Z 194 | eye.driver_remove('["rotation' + suffix + '"]') 195 | driver = eye.driver_add('["rotation' + suffix + '"]').driver 196 | remove_driver_variables(driver) 197 | add_var(driver, source=wm.character_rig, name="target", transform_type='ROT_Y', transform_space='LOCAL_SPACE', source_bone="eye_track" + suffix) 198 | 199 | driver.expression = "target" 200 | 201 | def register(): 202 | bpy.utils.register_class(CST_OT_addEyeDrivers_OP) 203 | bpy.utils.register_class(CST_OT_addEyeDriversNoUpDown_OP) 204 | 205 | def unregister(): 206 | bpy.utils.unregister_class(CST_OT_addEyeDriversNoUpDown_OP) 207 | bpy.utils.unregister_class(CST_OT_addEyeDrivers_OP) -------------------------------------------------------------------------------- /chr_shader_tools/cst_ShaderController/cst_select_shader_controller_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | from ...driver_generator.utility_functions.fdg_driver_utils import add_driver_color_simple 6 | from ...driver_generator.utility_functions.fdg_driver_utils import add_driver_float_simple 7 | 8 | class CST_OT_selectShaderController_OP(Operator): 9 | bl_idname = "object.select_shader_controller" 10 | bl_label = "Link Controller to Character" 11 | bl_description = "Adds drivers from the Controller Properties to all Meshes in the selected Character Collection, so that the shader can access them." 12 | bl_options = {"REGISTER", "UNDO"} 13 | 14 | # should only work in object mode 15 | @classmethod 16 | def poll(cls, context): 17 | obj = bpy.context.object 18 | 19 | if obj: 20 | if obj.mode == "OBJECT": 21 | return True 22 | 23 | return False 24 | 25 | def execute(self, context): 26 | 27 | wm = bpy.context.window_manager 28 | 29 | controllerEmpty = wm.controller_empty 30 | characterCollection = wm.character_collection 31 | characterRig = wm.character_rig 32 | characterRigBone = wm.character_rig_bone 33 | 34 | if controllerEmpty is None: 35 | self.report({'WARNING'}, "Please select the Controller!") 36 | return {'CANCELLED'} 37 | 38 | if characterCollection is None: 39 | self.report({'WARNING'}, "Please select a collection with the Character!") 40 | return {'CANCELLED'} 41 | 42 | if characterRig: 43 | constraint = controllerEmpty.constraints.new(type='CHILD_OF') 44 | constraint.target = characterRig 45 | if characterRigBone: 46 | constraint.subtarget = characterRigBone 47 | 48 | constraint.use_rotation_x = False 49 | constraint.use_rotation_y = False 50 | constraint.use_rotation_z = False 51 | constraint.use_scale_x = False 52 | constraint.use_scale_y = False 53 | constraint.use_scale_z = False 54 | constraint.set_inverse_pending = True 55 | 56 | 57 | self.search_collection(characterCollection, controllerEmpty) 58 | 59 | 60 | self.report({'INFO'}, "Added Controller Drivers!") 61 | return {'FINISHED'} 62 | 63 | def search_collection(self, collection, controller): 64 | for child in collection.children: 65 | self.search_collection(child, controller) 66 | 67 | for object in collection.objects: 68 | if object.type == 'MESH': 69 | self.add_properties(object) 70 | self.add_drivers(object, controller) 71 | 72 | def add_properties(self, object): 73 | 74 | 75 | object["highlight"] = (1.0, 1.0, 1.0, 0.0) 76 | 77 | ui_data = object.id_properties_ui("highlight") 78 | ui_data.update( 79 | description="Highlight Color and Amount", 80 | default=(1.0, 1.0, 1.0, 0.0) 81 | ) 82 | 83 | 84 | object["rimlight"] = (1.0, 1.0, 1.0, 0.0) 85 | 86 | ui_data = object.id_properties_ui("rimlight") 87 | ui_data.update( 88 | description="Rimlight Color and Amount", 89 | default=(1.0, 1.0, 1.0, 0.0) 90 | ) 91 | 92 | object["adv_mix_light"] = (1.0, 1.0, 1.0, 0.2) 93 | ui_data = object.id_properties_ui("adv_mix_light") 94 | ui_data.update( 95 | description="The color of the added Loght and mix factor", 96 | default=(1.0, 1.0, 1.0, 0.2) 97 | ) 98 | 99 | object["adv_mix_shadow"] = (0.0, 0.0, 0.0, 0.2) 100 | 101 | ui_data = object.id_properties_ui("adv_mix_shadow") 102 | ui_data.update( 103 | description="The color of the multiplied shadow tint and multiplication amount", 104 | default=(0.0, 0.0, 0.0, 0.2) 105 | ) 106 | 107 | object["mix_values"] = (0.0, 0.0) 108 | 109 | ui_data = object.id_properties_ui("mix_values") 110 | ui_data.update( 111 | description="Collection of mix values for advanced mixing and simple mixing", 112 | default=(0.0, 0.0) 113 | ) 114 | 115 | 116 | object["main_tint"] = (1.0, 1.0, 1.0, 0.0) 117 | 118 | ui_data = object.id_properties_ui("main_tint") 119 | ui_data.update( 120 | description="Tint Main Color and Tint amount", 121 | default=(1.0, 1.0, 1.0, 0.0) 122 | ) 123 | 124 | 125 | object["shadow_tint"] = (1.0, 1.0, 1.0, 0.0) 126 | 127 | ui_data = object.id_properties_ui("shadow_tint") 128 | ui_data.update( 129 | description="Tint Shadow Color and Tint amount", 130 | default=(1.0, 1.0, 1.0, 0.0) 131 | ) 132 | 133 | def add_drivers(self, object, controller): 134 | """Adds the drivers from the custom props of the controller to the given objects custom props""" 135 | 136 | add_driver_color_simple(controller, '["highlight_color"]', object, '["highlight"]') 137 | add_driver_float_simple(controller, '["highlight_amount"]', object, '["highlight"]', 3) 138 | 139 | add_driver_color_simple(controller, '["rimlight_color"]', object, '["rimlight"]') 140 | add_driver_float_simple(controller, '["rimlight_amount"]', object, '["rimlight"]', 3) 141 | 142 | add_driver_color_simple(controller, '["advanced_mix_light_color"]', object, '["adv_mix_light"]') 143 | add_driver_float_simple(controller, '["advanced_mix_light_amount"]', object, '["adv_mix_light"]', 3) 144 | 145 | add_driver_color_simple(controller, '["advanced_mix_shadow_tint"]', object, '["adv_mix_shadow"]') 146 | add_driver_float_simple(controller, '["advanced_mix_shadow_amount"]', object, '["adv_mix_shadow"]', 3) 147 | 148 | add_driver_float_simple(controller, '["advanced_mix_switch"]', object, '["mix_values"]', 0) 149 | 150 | add_driver_float_simple(controller, '["vector_diffuse_mix"]', object, '["mix_values"]', 1) 151 | 152 | add_driver_color_simple(controller, '["main_color_tint"]', object, '["main_tint"]') 153 | add_driver_float_simple(controller, '["main_color_tint_amount"]', object, '["main_tint"]', 3) 154 | 155 | add_driver_color_simple(controller, '["shadow_color_tint"]', object, '["shadow_tint"]') 156 | add_driver_float_simple(controller, '["shadow_color_tint_amount"]', object, '["shadow_tint"]', 3) 157 | 158 | class CST_OT_selectCrowdShaderController_OP(Operator): 159 | bl_idname = "object.select_crowd_shader_controller" 160 | bl_label = "Link Crowd Controller to Character" 161 | bl_description = "Adds drivers from the Crowd Controller Properties to all Meshes in the selected Character Collection, so that the shader can access them." 162 | bl_options = {"REGISTER", "UNDO"} 163 | 164 | # should only work in object mode 165 | @classmethod 166 | def poll(cls, context): 167 | obj = bpy.context.object 168 | 169 | if obj: 170 | if obj.mode == "OBJECT": 171 | return True 172 | 173 | return False 174 | 175 | def execute(self, context): 176 | 177 | wm = bpy.context.window_manager 178 | 179 | controllerEmpty = wm.controller_empty 180 | characterCollection = wm.character_collection 181 | characterRig = wm.character_rig 182 | characterRigBone = wm.character_rig_bone 183 | 184 | if controllerEmpty is None: 185 | self.report({'WARNING'}, "Please select the Controller!") 186 | return {'CANCELLED'} 187 | 188 | if characterCollection is None: 189 | self.report({'WARNING'}, "Please select a collection with the Character!") 190 | return {'CANCELLED'} 191 | 192 | if characterRig: 193 | constraint = controllerEmpty.constraints.new(type='CHILD_OF') 194 | constraint.target = characterRig 195 | if characterRigBone: 196 | constraint.subtarget = characterRigBone 197 | 198 | constraint.use_rotation_x = False 199 | constraint.use_rotation_y = False 200 | constraint.use_rotation_z = False 201 | constraint.use_scale_x = False 202 | constraint.use_scale_y = False 203 | constraint.use_scale_z = False 204 | constraint.set_inverse_pending = True 205 | 206 | 207 | self.search_collection(characterCollection, controllerEmpty) 208 | 209 | 210 | self.report({'INFO'}, "Added Controller Drivers!") 211 | return {'FINISHED'} 212 | 213 | def search_collection(self, collection, controller): 214 | for child in collection.children: 215 | self.search_collection(child, controller) 216 | 217 | for object in collection.objects: 218 | if object.type == 'MESH': 219 | self.add_properties(object) 220 | self.add_drivers(object, controller) 221 | 222 | def add_properties(self, object): 223 | 224 | 225 | object["main_tint"] = (1.0, 1.0, 1.0, 0.0) 226 | ui_data = object.id_properties_ui("main_tint") 227 | ui_data.update( 228 | description="Tint Main Color and Tint amount", 229 | default=(1.0, 1.0, 1.0, 0.0) 230 | ) 231 | 232 | 233 | object["shadow_tint"] = (1.0, 1.0, 1.0, 0.0) 234 | ui_data = object.id_properties_ui("shadow_tint") 235 | ui_data.update( 236 | description="Tint Shadow Color and Tint amount", 237 | default=(1.0, 1.0, 1.0, 0.0) 238 | ) 239 | 240 | def add_drivers(self, object, controller): 241 | """Adds the drivers from the custom props of the controller to the given objects custom props""" 242 | 243 | add_driver_color_simple(controller, '["main_color_tint"]', object, '["main_tint"]') 244 | add_driver_float_simple(controller, '["main_color_tint_amount"]', object, '["main_tint"]', 3) 245 | 246 | add_driver_color_simple(controller, '["shadow_color_tint"]', object, '["shadow_tint"]') 247 | add_driver_float_simple(controller, '["shadow_color_tint_amount"]', object, '["shadow_tint"]', 3) 248 | 249 | 250 | 251 | def register(): 252 | bpy.utils.register_class(CST_OT_selectShaderController_OP) 253 | bpy.utils.register_class(CST_OT_selectCrowdShaderController_OP) 254 | 255 | def unregister(): 256 | bpy.utils.unregister_class(CST_OT_selectCrowdShaderController_OP) 257 | bpy.utils.unregister_class(CST_OT_selectShaderController_OP) -------------------------------------------------------------------------------- /mocap_retarget/fmr_retarget_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import os 3 | from .. import fchar_settings 4 | from . import fmr_scale_list_io 5 | from ..helper_functions import signal_last 6 | 7 | import re 8 | 9 | from bpy.types import Operator 10 | 11 | class FMR_OT_MocapBatchRetarget_Op(Operator): 12 | bl_idname = "retarget.mocap_batch_retarget" 13 | bl_label = "Batch Retarget" 14 | bl_description = "Retarget multiple bvh files onto a character. This creates the needed filestructure on the perforce if it dies not already exist." 15 | 16 | @classmethod 17 | def poll(cls, context): 18 | return context.window_manager.bvh_files 19 | 20 | def execute(self, context): 21 | 22 | wm = context.window_manager 23 | settings = fchar_settings.Settings() 24 | scales = fmr_scale_list_io.ScaleListDict() 25 | filepath = settings.get_setting("perforce_path") 26 | 27 | 28 | scene = context.scene 29 | 30 | if not (os.path.isdir(filepath) and os.path.isdir(os.path.join(filepath, "080_scenes")) and os.path.isdir(os.path.join(filepath, "075_capture"))): 31 | # Checks if a valid Path to a Fritzi Perforce Directory is set. If not the Operator to choose a Perforce Path is called. 32 | 33 | bpy.ops.settings.choose_perforce_path('INVOKE_DEFAULT') 34 | 35 | self.report({'WARNING'}, "Set Perforce Path first!") 36 | return {'FINISHED'} 37 | else: 38 | #Select BVHS and then create filestructure and retarget 39 | 40 | #check if bvhs are already loaded otherwise execute the selection operator 41 | if not wm.bvh_files: 42 | bpy.ops.retarget.select_bvhs('INVOKE_DEFAULT', filepath = settings.get_setting("perforce_path")) 43 | else: 44 | 45 | char_file_basename = os.path.basename(bpy.data.filepath) 46 | 47 | for is_last, bvh in signal_last(wm.bvh_files): 48 | 49 | #Create the filestructure where the retargeted File should be stored (based on the bvh name and the character file name) 50 | dir_path = get_layout_dir(filepath, bvh.name) 51 | if not os.path.isdir(dir_path): 52 | os.makedirs(dir_path) 53 | 54 | filename = get_file_name(char_file_basename, bvh.name, is_crowd=wm.crowd_check) 55 | 56 | # Save the File with proper name into the file structure 57 | # Check if file already exists and save with _### suffix (001, 002. etc) if it does 58 | blendpath = os.path.join(dir_path, filename) 59 | suffix = 1 60 | while os.path.isfile(blendpath): 61 | filename = get_file_name(char_file_basename, bvh.name, suffix= '_' + str(suffix), is_crowd=wm.crowd_check) 62 | 63 | blendpath = os.path.join(dir_path, filename) 64 | suffix+=1 65 | 66 | 67 | bpy.ops.wm.save_as_mainfile(filepath=blendpath, compress=True) 68 | 69 | #import the bvh (Scaling has to be fetched from scale List!) 70 | name = char_file_basename.split("_")[2].lower() 71 | scale = scales.get_scale(name) 72 | do_auto_scale = False 73 | if not scale: 74 | scale = 1.0 75 | do_auto_scale = True 76 | 77 | bpy.context.view_layer.active_layer_collection = bpy.context.view_layer.layer_collection 78 | 79 | bpy.ops.import_anim.bvh(filepath=bvh.filepath, global_scale=scale, use_fps_scale=True, update_scene_duration=True) 80 | 81 | """print(name) 82 | print(scale) 83 | print(do_auto_scale)""" 84 | 85 | # set the Properties which are needed by ARP to retarget the bvh 86 | bvh_arma_name = bvh.name.split(".")[0] 87 | scene.source_rig = bvh_arma_name 88 | wm.source_rig_pointer = bpy.data.objects[bvh_arma_name] 89 | scene.target_rig = wm.target_rig_pointer.name 90 | 91 | retarget_mocap(context, do_auto_scale) 92 | 93 | #save after retargeting 94 | bpy.ops.wm.save_mainfile(compress=True) 95 | 96 | 97 | # undo retarget and import before heading to next bvh 98 | # delete actions (are named after bvh and one is named after bvh with _remap suffix) 99 | # delete bvh 100 | # does not happen on last bvh in the list 101 | if not is_last: 102 | action = bpy.data.actions.get(bvh_arma_name) 103 | bpy.data.actions.remove(action, do_unlink=True) 104 | action = bpy.data.actions.get(bvh_arma_name + "_remap") 105 | bpy.data.actions.remove(action, do_unlink=True) 106 | bvh_armature = bpy.data.objects[bvh_arma_name] 107 | bpy.data.objects.remove(bvh_armature, do_unlink=True) 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | return {'FINISHED'} 118 | 119 | class FMR_OT_MocapRetarget_OP(Operator): 120 | bl_idname = "object.mocap_retarget" 121 | bl_label = "Retarget Mocap" 122 | bl_description = "Use Auto-Rig-Pro to retarget Mocap to Control Rigs" 123 | bl_options = {"REGISTER","UNDO"} 124 | 125 | settings = None 126 | json_path = os.path.join( 127 | os.path.dirname(__file__), 128 | "fmr_settings.json") 129 | 130 | # should only work in object mode 131 | @classmethod 132 | def poll(cls, context): 133 | obj = bpy.context.object 134 | 135 | if obj: 136 | if obj.mode == "OBJECT": 137 | return True 138 | 139 | return False 140 | 141 | def execute(self, context): 142 | wm = context.window_manager 143 | 144 | # retarget mocap with user input for auto_scaling (From a checkbox) 145 | retarget_mocap(context, wm.auto_scale_check) 146 | 147 | self.report({'INFO'}, "Finished Retargeting Mocap!") 148 | return {'FINISHED'} 149 | 150 | 151 | 152 | 153 | def load_bone_map(context, bone_map_path, settings): 154 | """Imports the Bone Map through ARP from the given Path. 155 | If the Import fails, tries to import the standard Bone map shipped with this add on. 156 | If both of those fail, returns False, otherwise True""" 157 | try: 158 | bpy.ops.arp.import_config(filepath=bone_map_path) 159 | 160 | except Exception: 161 | bone_map_path = os.path.join(os.path.join(os.path.dirname(__file__),"BoneMap"),"IKlegFKarm.bmap") 162 | settings.set_setting("bone_map_path", bone_map_path) 163 | 164 | try: 165 | bpy.ops.arp.import_config(filepath=bone_map_path) 166 | 167 | except Exception: 168 | return False 169 | 170 | return True 171 | 172 | 173 | 174 | def select_rest_bones(context): 175 | """Selects all Bones from the source Rig, with the exception of the RightFoot and LeftFoot""" 176 | wm = context.window_manager 177 | scene = bpy.context.scene 178 | 179 | for myBone in bpy.data.objects[scene.source_rig].pose.bones: 180 | myBone.bone.select = True 181 | 182 | myBone = bpy.data.objects[scene.source_rig].pose.bones["RightFoot"] 183 | myBone.bone.select = False 184 | myBone = bpy.data.objects[scene.source_rig].pose.bones["LeftFoot"] 185 | myBone.bone.select = False 186 | 187 | def retarget_mocap(context, do_auto_scale=True): 188 | """Auto executes all ARP steps to retarget bvh MoCap data to a Fritzi und Sophie Rig. 189 | Only uses the auto_scale operator if do_auto_scale is true, to accomodate prescaled bvhs""" 190 | settings = fchar_settings.Settings() 191 | 192 | wm = context.window_manager 193 | 194 | # Scale Source Mesh to Target Mesh 195 | if do_auto_scale: 196 | bpy.ops.arp.auto_scale() 197 | 198 | # Build Bone List. This will then be overwritten with custom Bone Map 199 | 200 | bpy.ops.arp.build_bones_list() 201 | 202 | # Import custom Bone List 203 | 204 | load_bone_map(context, settings.get_setting("bone_map_path"), settings) 205 | 206 | # Redefine Rest Pose 207 | 208 | bpy.ops.arp.redefine_rest_pose(preserve=True, rest_pose='REST') 209 | select_rest_bones(context) 210 | 211 | bpy.ops.arp.copy_bone_rest() 212 | bpy.ops.arp.save_pose_rest() 213 | 214 | # Retarget 215 | start, end = context.window_manager.source_rig_pointer.animation_data.action.frame_range 216 | bpy.ops.arp.retarget(frame_start=int(start), frame_end=int(end), interpolation_type='BEZIER', handle_type='AUTO_CLAMPED') 217 | return True 218 | 219 | 220 | def get_layout_dir(perforce_path, bvh_file): 221 | """Returns the path to the layout directory in Perforce in which a given bvh file should be saved""" 222 | dir_path = perforce_path 223 | #print(dir_path) 224 | dir_path = os.path.join(dir_path, "080_scenes") 225 | dir_path = os.path.join(dir_path, "010_layout") 226 | dir_path = os.path.join(dir_path, "s01_e0" + get_episode(bvh_file)) 227 | dir_path = os.path.join(dir_path, "sq" + get_sequence(bvh_file)) 228 | dir_path = os.path.join(dir_path, "Re-Target") 229 | 230 | return dir_path 231 | 232 | 233 | def get_file_name(char_file, bvh_file, suffix = "", is_crowd=False): 234 | """Builds the filename for a retarget blendfile out of the character filename and the bvh File name.""" 235 | char_parts = char_file.split("_") 236 | filename = "" 237 | for i in range(0,3): 238 | filename = filename + char_parts[i] + "_" 239 | 240 | filename = filename + get_sequence(bvh_file) 241 | filename = filename + "_p" + get_part(bvh_file) 242 | filename = filename + "_m" + get_mocap(bvh_file) 243 | filename = filename + "_t" + get_take(bvh_file) 244 | filename = filename + "_" + get_actor(bvh_file) 245 | if is_crowd: 246 | filename = filename + "_crowd" 247 | filename = filename + suffix 248 | filename = filename + ".blend" 249 | 250 | return filename 251 | 252 | def get_episode(bvh_file): 253 | """Returns the episode number from a given bvh File""" 254 | return bvh_file[0] 255 | 256 | def get_sequence(bvh_file): 257 | """Returns the sequence number from a given bvh File""" 258 | parts = bvh_file.split("-") 259 | sequence = parts[0].split("_")[0] 260 | return sequence 261 | # nachfragen wie sequences mit a, b etc behandelt werden sollen insbesondere mit unterschiedlichen Formatierungen. 262 | # es gibt z.b ###a, ###_a, ### a. 263 | 264 | def get_part(bvh_file): 265 | """Returns the Part number from a given bvh Filename. 266 | Searches the String for 'part' and returns the number behind it. 267 | If the String does not contain a 'part' it returns 01.""" 268 | parts = bvh_file.split("-") 269 | for string in parts[0].split("_"): 270 | 271 | if string.startswith('part'): 272 | length = len(string) 273 | part = string[4:length] 274 | if len(part) == 1: 275 | return "0" + part 276 | return part 277 | 278 | return '01' 279 | 280 | def get_mocap(bvh_file): 281 | """Returns the Part number from a given bvh Filename. 282 | Searches the String for 'mc' and returns the number behind it. 283 | If the String does not contain a 'mc' it returns 01.""" 284 | parts = bvh_file.split("-") 285 | for string in parts[0].split("_"): 286 | 287 | if string.startswith('mc'): 288 | length = len(string) 289 | mocap = string[2:length] 290 | if len(mocap) == 1: 291 | return "0" + mocap 292 | if len(mocap) == 2 and re.match('[a-z]', mocap[1]): 293 | return "0" + mocap 294 | return string[2:length] 295 | 296 | return '01' 297 | 298 | def get_take(bvh_file): 299 | """Returns the take number from a given bvh Filename.""" 300 | parts = bvh_file.split("-") 301 | take = parts[1].split("_")[0] 302 | length = len(take) 303 | return take[1:length] 304 | 305 | def get_actor(bvh_file): 306 | """Returns the name of the actor which played the animation in the given bvh Filename""" 307 | parts = bvh_file.split("_") 308 | actor = parts[len(parts) - 1] 309 | 310 | actor = actor.split(".")[0] 311 | return actor 312 | 313 | 314 | def register(): 315 | bpy.utils.register_class(FMR_OT_MocapBatchRetarget_Op) 316 | bpy.utils.register_class(FMR_OT_MocapRetarget_OP) 317 | 318 | def unregister(): 319 | bpy.utils.unregister_class(FMR_OT_MocapRetarget_OP) 320 | bpy.utils.unregister_class(FMR_OT_MocapBatchRetarget_Op) -------------------------------------------------------------------------------- /driver_generator/driver_gen_angles/fdg_driver_gen_angles_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import mathutils 3 | 4 | from bpy.types import Operator 5 | 6 | from ..utility_functions.fdg_driver_utils import add_var 7 | from ..utility_functions.fdg_driver_utils import parent_objects 8 | from ..utility_functions.fdg_driver_utils import add_custom_property 9 | 10 | from ..utility_functions import fdg_names 11 | 12 | class FDG_OT_GenerateDrivers_Op(Operator): 13 | bl_idname = "object.generate_drivers" 14 | bl_label = "Generate Drivers" 15 | bl_description = "Generate all drivers necessary to then control the blendshapes for 2D fakes. Sets up a controller for the Animator as well." 16 | bl_options = {"REGISTER", "UNDO"} 17 | 18 | def execute(self, context): 19 | 20 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 21 | 22 | wm = bpy.context.window_manager 23 | 24 | prefix = wm.character_prefix 25 | arma = wm.character_rig 26 | head_bone_name = wm.character_head_bone 27 | cam = wm.camera 28 | 29 | if prefix == "": 30 | self.report({'WARNING'}, "Please enter a Character Prefix!") 31 | return {'CANCELLED'} 32 | 33 | if arma is None: 34 | self.report({'WARNING'}, "Please select the Armature!") 35 | return {'CANCELLED'} 36 | 37 | if head_bone_name == "": 38 | self.report({'WARNING'}, "Please select the Head Bone!") 39 | return {'CANCELLED'} 40 | 41 | if cam is None: 42 | self.report({'WARNING'}, "Please select the Camera!") 43 | return {'CANCELLED'} 44 | 45 | if cam.type != 'CAMERA': 46 | self.report({'WARNING'}, "Please select a Camera!") 47 | 48 | # Create an Empty at the Base of the Head Bone and parent it to the Head Bone 49 | head_empty = bpy.data.objects.get(prefix + "." + fdg_names.empty_head) 50 | 51 | if head_empty is not None: 52 | bpy.data.objects.remove(head_empty, do_unlink=True) 53 | 54 | head_empty = bpy.data.objects.new(prefix + "." + fdg_names.empty_head, None) 55 | 56 | if arma.type == 'ARMATURE' and head_bone_name != '': 57 | head_empty.location = arma.location + \ 58 | arma.data.bones[head_bone_name].head 59 | else: 60 | head_empty.location = arma.location 61 | 62 | bpy.context.collection.objects.link(head_empty) 63 | 64 | parent_objects(arma, head_empty, head_bone_name) 65 | 66 | head_empty.hide_viewport = True 67 | 68 | # Create an Empty at the Camera and parent it to the Camera 69 | cam_empty = bpy.context.scene.objects.get(fdg_names.empty_cam) 70 | if cam_empty is None: 71 | cam_empty = bpy.data.objects.new(fdg_names.empty_cam, None) 72 | 73 | 74 | cam_empty.location = cam.location 75 | cam_empty.rotation_euler = cam.rotation_euler 76 | 77 | bpy.context.collection.objects.link(cam_empty) 78 | 79 | parent_objects(cam, cam_empty) 80 | 81 | cam_empty.hide_viewport = True 82 | 83 | # Create two Bones as controllers for the Drivers, one hidden for driven Properties and one not hidden with animatable properties for the animator 84 | 85 | hidden_controller_bone_name = fdg_names.hidden_controller_bone 86 | visible_controller_bone_name = fdg_names.visible_controller_bone 87 | 88 | # bpy.ops.object.select_all(action='DESELECT') 89 | bpy.context.view_layer.objects.active = arma 90 | bpy.ops.object.mode_set(mode='EDIT', toggle=False) 91 | 92 | edit_bones = arma.data.edit_bones 93 | b = edit_bones.get(hidden_controller_bone_name) 94 | if b is not None: 95 | edit_bones.remove(b) 96 | 97 | b = edit_bones.new(hidden_controller_bone_name) 98 | 99 | b.head = head_empty.location + mathutils.Vector((1.0, 0.0, 0.0)) 100 | b.tail = head_empty.location + mathutils.Vector((1.0, -1.0, 0.0)) 101 | 102 | b = edit_bones.get(visible_controller_bone_name) 103 | if b is not None: 104 | edit_bones.remove(b) 105 | 106 | b = edit_bones.new(visible_controller_bone_name) 107 | 108 | b.head = head_empty.location + mathutils.Vector((2.0, 0.0, 0.0)) 109 | b.tail = head_empty.location + mathutils.Vector((2.0, -1.0, 0.0)) 110 | 111 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 112 | 113 | # Add custom properties to the control bones 114 | 115 | hidden_controller_pose_bone = arma.pose.bones[hidden_controller_bone_name] 116 | visible_controller_pose_bone = arma.pose.bones[visible_controller_bone_name] 117 | 118 | arma.data.bones[hidden_controller_bone_name].layers[29] = True 119 | arma.data.bones[hidden_controller_bone_name].layers[0] = False 120 | 121 | arma.data.bones[visible_controller_bone_name].layers[30] = True 122 | arma.data.bones[visible_controller_bone_name].layers[0] = False 123 | 124 | 125 | arma.data.layers[30] = True 126 | arma.data.layers[29] = False 127 | 128 | 129 | # Hidden Controller 130 | 131 | add_custom_property(hidden_controller_pose_bone, fdg_names.prop_angle_x, prop_min=-180.0, 132 | prop_max=180.0, description="Angle between head and Camera around x axis") 133 | 134 | add_custom_property(hidden_controller_pose_bone, fdg_names.prop_angle_z, prop_min=-180.0, 135 | prop_max=180.0, description="Angle between head and Camera around x axis") 136 | 137 | add_custom_property(hidden_controller_pose_bone, 138 | fdg_names.prop_up_down_normalize, prop_min=-180.0, prop_max=180.0) 139 | 140 | add_custom_property(hidden_controller_pose_bone, 141 | fdg_names.prop_shapes_left, prop_min=0.0, prop_max=1.0) 142 | 143 | add_custom_property(hidden_controller_pose_bone, 144 | fdg_names.prop_shapes_right, prop_min=0.0, prop_max=1.0) 145 | 146 | # Visible Controller 147 | 148 | add_custom_property(visible_controller_pose_bone, 149 | fdg_names.prop_bias, prop_min=-150.0, prop_max=150.0) 150 | 151 | add_custom_property(visible_controller_pose_bone, 152 | fdg_names.prop_blend, default=10.0, prop_min=1.0, prop_max=89.0) 153 | 154 | add_custom_property(visible_controller_pose_bone, 155 | fdg_names.prop_blendUpDown, default=10.0, prop_min=1.0, prop_max=10.0) 156 | 157 | add_custom_property(visible_controller_pose_bone, 158 | fdg_names.prop_driverSwitch, default=1.0, prop_min=0.0, prop_max=1.0) 159 | 160 | # Add drivers to the hidden control bone 161 | 162 | driver_x = hidden_controller_pose_bone.driver_add( 163 | '["' + fdg_names.prop_angle_x + '"]').driver 164 | driver_z = hidden_controller_pose_bone.driver_add( 165 | '["' + fdg_names.prop_angle_z + '"]').driver 166 | driver_normalize = hidden_controller_pose_bone.driver_add( 167 | '["' + fdg_names.prop_up_down_normalize + '"]').driver 168 | driver_shapes_left = hidden_controller_pose_bone.driver_add( 169 | '["' + fdg_names.prop_shapes_left + '"]').driver 170 | driver_shapes_right = hidden_controller_pose_bone.driver_add( 171 | '["' + fdg_names.prop_shapes_right + '"]').driver 172 | 173 | # Add variables to driver around the x axis 174 | add_var(driver_x, head_empty, "head_loc_x", transform_type='LOC_X') 175 | add_var(driver_x, head_empty, "head_loc_y", transform_type='LOC_Y') 176 | add_var(driver_x, head_empty, "head_loc_z", transform_type='LOC_Z') 177 | 178 | add_var(driver_x, head_empty, "head_rot_x", transform_type='ROT_X') 179 | add_var(driver_x, head_empty, "head_rot_y", transform_type='ROT_Y') 180 | add_var(driver_x, head_empty, "head_rot_z", transform_type='ROT_Z') 181 | 182 | add_var(driver_x, cam_empty, "cam_loc_x", transform_type='LOC_X') 183 | add_var(driver_x, cam_empty, "cam_loc_y", transform_type='LOC_Y') 184 | add_var(driver_x, cam_empty, "cam_loc_z", transform_type='LOC_Z') 185 | 186 | # Add variables to driver around the z axis 187 | add_var(driver_z, head_empty, "head_loc_x", transform_type='LOC_X') 188 | add_var(driver_z, head_empty, "head_loc_y", transform_type='LOC_Y') 189 | add_var(driver_z, head_empty, "head_loc_z", transform_type='LOC_Z') 190 | 191 | add_var(driver_z, head_empty, "head_rot_x", transform_type='ROT_X') 192 | add_var(driver_z, head_empty, "head_rot_y", transform_type='ROT_Y') 193 | add_var(driver_z, head_empty, "head_rot_z", transform_type='ROT_Z') 194 | 195 | add_var(driver_z, cam_empty, "cam_loc_x", transform_type='LOC_X') 196 | add_var(driver_z, cam_empty, "cam_loc_y", transform_type='LOC_Y') 197 | add_var(driver_z, cam_empty, "cam_loc_z", transform_type='LOC_Z') 198 | 199 | # add variables to normalize driver 200 | add_var(driver_normalize, arma, "angle_x", type='SINGLE_PROP', 201 | rna_data_path='pose.bones["' + fdg_names.hidden_controller_bone + '"]["' + fdg_names.prop_angle_x + '"]') 202 | add_var(driver_normalize, arma, "angle_z", type='SINGLE_PROP', 203 | rna_data_path='pose.bones["' + fdg_names.hidden_controller_bone + '"]["' + fdg_names.prop_angle_z + '"]') 204 | add_var(driver_normalize, arma, "bias", type='SINGLE_PROP', 205 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_bias + '"]') 206 | 207 | # add variables to the shapes drivers 208 | # LEFT 209 | add_var(driver_shapes_left, arma, "angle_z", type='SINGLE_PROP', 210 | rna_data_path='pose.bones["' + fdg_names.hidden_controller_bone + '"]["' + fdg_names.prop_angle_z + '"]') 211 | add_var(driver_shapes_left, arma, "bias", type='SINGLE_PROP', 212 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_bias + '"]') 213 | add_var(driver_shapes_left, arma, "blend", type='SINGLE_PROP', 214 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_blend + '"]') 215 | add_var(driver_shapes_left, arma, "blendUpDown", type='SINGLE_PROP', 216 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_blendUpDown + '"]') 217 | add_var(driver_shapes_left, arma, "upDownNormalize", type='SINGLE_PROP', 218 | rna_data_path='pose.bones["' + fdg_names.hidden_controller_bone + '"]["' + fdg_names.prop_up_down_normalize + '"]') 219 | add_var(driver_shapes_left, arma, "driverSwitch", type='SINGLE_PROP', 220 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_driverSwitch + '"]') 221 | 222 | # RIGHT 223 | add_var(driver_shapes_right, arma, "angle_z", type='SINGLE_PROP', 224 | rna_data_path='pose.bones["' + fdg_names.hidden_controller_bone + '"]["' + fdg_names.prop_angle_z + '"]') 225 | add_var(driver_shapes_right, arma, "bias", type='SINGLE_PROP', 226 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_bias + '"]') 227 | add_var(driver_shapes_right, arma, "blend", type='SINGLE_PROP', 228 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_blend + '"]') 229 | add_var(driver_shapes_right, arma, "blendUpDown", type='SINGLE_PROP', 230 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_blendUpDown + '"]') 231 | add_var(driver_shapes_right, arma, "upDownNormalize", type='SINGLE_PROP', 232 | rna_data_path='pose.bones["' + fdg_names.hidden_controller_bone + '"]["' + fdg_names.prop_up_down_normalize + '"]') 233 | add_var(driver_shapes_right, arma, "driverSwitch", type='SINGLE_PROP', 234 | rna_data_path='pose.bones["' + fdg_names.visible_controller_bone + '"]["' + fdg_names.prop_driverSwitch + '"]') 235 | 236 | # Add the expressions to the drivers 237 | driver_x.expression = "degrees(angle_x_func(head_loc_x, head_loc_y, head_loc_z, cam_loc_x, cam_loc_y, cam_loc_z, head_rot_x, head_rot_y, head_rot_z)) " 238 | driver_z.expression = "degrees(angle_z_func(head_loc_x, head_loc_y, head_loc_z, cam_loc_x, cam_loc_y, cam_loc_z, head_rot_x, head_rot_y, head_rot_z)) " 239 | driver_normalize.expression = "2*-angle_x if angle_z>0+bias and angle_x>0 else 2*angle_x if angle_z<0+bias and angle_x >0 else 2*angle_x if angle_z>0+bias and angle_x <0 else 2*-angle_x if angle_x<0 and angle_z<0+bias else 0" 240 | 241 | driver_shapes_left.expression = "(1-((-blend-(angle_z-bias))/(90-blend))-((upDownNormalize/blendUpDown)/15) if angle_z-bias <-blend else -(angle_z-bias)/blend-((upDownNormalize/blendUpDown)/15) if angle_z-bias <=0 and angle_z-bias>=-blend else 0) * driverSwitch" 242 | driver_shapes_right.expression = "(1+((blend-(angle_z-bias))/(90-blend))-((-upDownNormalize/blendUpDown)/15) if angle_z-bias >blend else ((angle_z-bias)/blend)-((-upDownNormalize/blendUpDown)/15) if angle_z-bias >=0 and angle_z-bias<=blend else 0) * driverSwitch" 243 | 244 | self.report({'INFO'}, "Generated Drivers!") 245 | return {'FINISHED'} 246 | 247 | 248 | def register(): 249 | bpy.utils.register_class(FDG_OT_GenerateDrivers_Op) 250 | 251 | 252 | def unregister(): 253 | bpy.utils.unregister_class(FDG_OT_GenerateDrivers_Op) 254 | -------------------------------------------------------------------------------- /chr_shader_tools/cst_ShaderController/cst_create_shader_controller_op.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | from bpy.types import Operator 4 | 5 | class CST_OT_CreateShaderController_OP(Operator): 6 | bl_idname = "object.create_shader_controller" 7 | bl_label = "Create Shader Controller" 8 | bl_descritption = "Creates an Empty with controls for the shaders of one character" 9 | bl_options = {"REGISTER", "UNDO"} 10 | 11 | # should only work in object mode 12 | @classmethod 13 | def poll(cls, context): 14 | obj = bpy.context.object 15 | 16 | if obj: 17 | if obj.mode == "OBJECT": 18 | return True 19 | 20 | return False 21 | 22 | 23 | def execute(self, context): 24 | 25 | wm = bpy.context.window_manager 26 | 27 | characterName = wm.character_name 28 | 29 | if characterName is None or characterName == "": 30 | self.report({'WARNING'}, "Please give a Character Name!") 31 | return {'CANCELLED'} 32 | 33 | emptyController = bpy.data.objects.new(characterName + "_ShaderController", None) 34 | 35 | bpy.context.scene.collection.objects.link(emptyController) 36 | 37 | emptyController.empty_display_size = 2 38 | emptyController.empty_display_type = 'PLAIN_AXES' 39 | 40 | add_properties(emptyController) 41 | 42 | wm.controller_empty = emptyController 43 | 44 | self.report({'INFO'}, "Added Shader Controller!") 45 | return {'FINISHED'} 46 | 47 | class CST_OT_UpdateShaderController_OP(Operator): 48 | bl_idname = "object.update_shader_controller" 49 | bl_label = "Update Shader Controller" 50 | bl_description = "Updates the shader controller (and link to the character) with all new properties that may have been added" 51 | bl_options = {"REGISTER", "UNDO"} 52 | 53 | # should only work in object mode 54 | @classmethod 55 | def poll(cls, context): 56 | obj = bpy.context.object 57 | 58 | if obj: 59 | if obj.mode == "OBJECT": 60 | return True 61 | 62 | return False 63 | 64 | def execute(self, context): 65 | 66 | wm = bpy.context.window_manager 67 | 68 | emptyController = wm.controller_empty 69 | 70 | add_properties(emptyController) 71 | 72 | bpy.ops.object.select_shader_controller() 73 | 74 | return {'FINISHED'} 75 | 76 | class CST_OT_CreateCrowdShaderController_OP(Operator): 77 | bl_idname = "object.create_crowd_shader_controller" 78 | bl_label = "Create Crowd Shader Controller" 79 | bl_descritption = "Creates an Empty with controls for the shaders of one CROWD character" 80 | bl_options = {"REGISTER", "UNDO"} 81 | 82 | # should only work in object mode 83 | @classmethod 84 | def poll(cls, context): 85 | obj = bpy.context.object 86 | 87 | if obj: 88 | if obj.mode == "OBJECT": 89 | return True 90 | 91 | return False 92 | 93 | 94 | def execute(self, context): 95 | 96 | wm = bpy.context.window_manager 97 | 98 | characterName = wm.character_name 99 | 100 | if characterName is None or characterName == "": 101 | self.report({'WARNING'}, "Please give a Character Name!") 102 | return {'CANCELLED'} 103 | 104 | emptyController = bpy.data.objects.new(characterName + "_CrowdShaderController", None) 105 | 106 | bpy.context.scene.collection.objects.link(emptyController) 107 | 108 | emptyController.empty_display_size = 2 109 | emptyController.empty_display_type = 'PLAIN_AXES' 110 | 111 | add_crowd_properties(emptyController) 112 | 113 | wm.controller_empty = emptyController 114 | 115 | self.report({'INFO'}, "Added Shader Controller!") 116 | return {'FINISHED'} 117 | 118 | def add_properties(controller): 119 | 120 | emptyController = controller 121 | 122 | 123 | if 'vector_diffuse_mix' not in emptyController: 124 | emptyController["vector_diffuse_mix"] = 0.0 125 | 126 | ui_data = emptyController.id_properties_ui("vector_diffuse_mix") 127 | ui_data.update(min=0.0) 128 | ui_data.update(max=1.0) 129 | ui_data.update(soft_min=0.0) 130 | ui_data.update(soft_max=1.0) 131 | ui_data.update(description="Mix Amount of Vector shading vs Diffuse shading") 132 | ui_data.update(default=0.0) 133 | 134 | if "highlight_amount" not in emptyController: 135 | emptyController["highlight_amount"] = 0.0 136 | 137 | ui_data = emptyController.id_properties_ui("highlight_amount") 138 | ui_data.update(min=0.0) 139 | ui_data.update(soft_min=0.0) 140 | ui_data.update(description="Brightness of the highlights") 141 | ui_data.update(default=0.0) 142 | 143 | if "highlight_color" not in emptyController: 144 | emptyController["highlight_color"] = (1.0, 1.0, 1.0) 145 | 146 | ui_data = emptyController.id_properties_ui("highlight_color") 147 | ui_data.update(min=0.0) 148 | ui_data.update(max=1.0) 149 | ui_data.update(soft_min=0.0) 150 | ui_data.update(soft_max=1.0) 151 | ui_data.update(description="Highlight Color") 152 | ui_data.update(default=(1.0, 1.0, 1.0)) 153 | ui_data.update(subtype='COLOR') 154 | 155 | if "rimlight_amount" not in emptyController: 156 | emptyController["rimlight_amount"] = 0.0 157 | 158 | ui_data = emptyController.id_properties_ui("rimlight_amount") 159 | ui_data.update(min=0.0) 160 | ui_data.update(soft_min=0.0) 161 | ui_data.update(description="Brightness of the rimlights") 162 | ui_data.update(default=0.0) 163 | 164 | if "rimlight_color" not in emptyController: 165 | emptyController["rimlight_color"] = (1.0, 1.0, 1.0) 166 | 167 | ui_data = emptyController.id_properties_ui("rimlight_color") 168 | ui_data.update(min=0.0) 169 | ui_data.update(max=1.0) 170 | ui_data.update(soft_min=0.0) 171 | ui_data.update(soft_max=1.0) 172 | ui_data.update(description="Rimlight Color") 173 | ui_data.update(default=(1.0, 1.0, 1.0)) 174 | ui_data.update(subtype='COLOR') 175 | 176 | if "advanced_mix_switch" not in emptyController: 177 | emptyController["advanced_mix_switch"] = 0.0 178 | 179 | ui_data = emptyController.id_properties_ui("advanced_mix_switch") 180 | ui_data.update(min=0.0) 181 | ui_data.update(max=1.0) 182 | ui_data.update(soft_min=0.0) 183 | ui_data.update(soft_max=1.0) 184 | ui_data.update(description="Turns Advanced mixing on and off") 185 | ui_data.update(default=0.0) 186 | 187 | if "advanced_mix_light_amount" not in emptyController: 188 | emptyController["advanced_mix_light_amount"] = 0.2 189 | 190 | ui_data = emptyController.id_properties_ui("advanced_mix_light_amount") 191 | ui_data.update(min=0.0) 192 | ui_data.update(max=1.0) 193 | ui_data.update(soft_min=0.0) 194 | ui_data.update(soft_max=1.0) 195 | ui_data.update(description="How much light gets added to the light regions") 196 | ui_data.update(default=0.2) 197 | 198 | if "advanced_mix_light_color" not in emptyController: 199 | emptyController["advanced_mix_light_color"] = (1.0, 1.0, 1.0) 200 | 201 | ui_data = emptyController.id_properties_ui("advanced_mix_light_color") 202 | ui_data.update(min=0.0) 203 | ui_data.update(max=1.0) 204 | ui_data.update(soft_min=0.0) 205 | ui_data.update(soft_max=1.0) 206 | ui_data.update(description="The color of the added Light") 207 | ui_data.update(default=(1.0, 1.0, 1.0)) 208 | ui_data.update(subtype='COLOR') 209 | 210 | if "advanced_mix_shadow_amount" not in emptyController: 211 | emptyController["advanced_mix_shadow_amount"] = 0.2 212 | 213 | ui_data = emptyController.id_properties_ui("advanced_mix_shadow_amount") 214 | ui_data.update(min=0.0) 215 | ui_data.update(max=1.0) 216 | ui_data.update(soft_min=0.0) 217 | ui_data.update(soft_max=1.0) 218 | ui_data.update(description="How much shadow tint gets multiplied to the dark regions") 219 | ui_data.update(default=0.2) 220 | 221 | if "advanced_mix_shadow_tint" not in emptyController: 222 | emptyController["advanced_mix_shadow_tint"] = (0.0, 0.0, 0.0) 223 | 224 | ui_data = emptyController.id_properties_ui("advanced_mix_shadow_tint") 225 | ui_data.update(min=0.0) 226 | ui_data.update(max=1.0) 227 | ui_data.update(soft_min=0.0) 228 | ui_data.update(soft_max=1.0) 229 | ui_data.update(description="The color of the multiplied shadow tint") 230 | ui_data.update(default=(0.0, 0.0, 0.0)) 231 | ui_data.update(subtype='COLOR') 232 | 233 | if "main_color_tint" not in emptyController: 234 | emptyController["main_color_tint"] = (1.0, 1.0, 1.0) 235 | 236 | ui_data = emptyController.id_properties_ui("main_color_tint") 237 | ui_data.update(min=0.0) 238 | ui_data.update(max=2.0) 239 | ui_data.update(soft_min=0.0) 240 | ui_data.update(soft_max=2.0) 241 | ui_data.update(description="Color to tint the main Color of a Character") 242 | ui_data.update(default=(1.0, 1.0, 1.0)) 243 | ui_data.update(subtype='COLOR') 244 | 245 | if "main_color_tint_amount" not in emptyController: 246 | emptyController["main_color_tint_amount"] = (0.0) 247 | 248 | ui_data = emptyController.id_properties_ui("main_color_tint_amount") 249 | ui_data.update(min=0.0) 250 | ui_data.update(max=1.0) 251 | ui_data.update(soft_min=0.0) 252 | ui_data.update(soft_max=1.0) 253 | ui_data.update(description="Amount of tint being applied to the main Color") 254 | ui_data.update(default=0.0) 255 | 256 | if "shadow_color_tint" not in emptyController: 257 | emptyController["shadow_color_tint"] = (1.0, 1.0, 1.0) 258 | 259 | ui_data = emptyController.id_properties_ui("shadow_color_tint") 260 | ui_data.update(min=0.0) 261 | ui_data.update(max=2.0) 262 | ui_data.update(soft_min=0.0) 263 | ui_data.update(soft_max=2.0) 264 | ui_data.update(description="Color ot tint the shadow color of a Character") 265 | ui_data.update(default=(1.0, 1.0, 1.0)) 266 | ui_data.update(subtype='COLOR') 267 | 268 | if "shadow_color_tint_amount" not in emptyController: 269 | emptyController["shadow_color_tint_amount"] = (0.0) 270 | 271 | ui_data = emptyController.id_properties_ui("shadow_color_tint_amount") 272 | ui_data.update(min=0.0) 273 | ui_data.update(max=1.0) 274 | ui_data.update(soft_min=0.0) 275 | ui_data.update(soft_max=1.0) 276 | ui_data.update(description="Amount of tint being applied to the shadow Color") 277 | ui_data.update(default=0.0) 278 | 279 | 280 | emptyController.property_overridable_library_set('["vector_diffuse_mix"]', True) 281 | emptyController.property_overridable_library_set('["highlight_amount"]', True) 282 | emptyController.property_overridable_library_set('["highlight_color"]', True) 283 | emptyController.property_overridable_library_set('["rimlight_amount"]', True) 284 | emptyController.property_overridable_library_set('["rimlight_color"]', True) 285 | emptyController.property_overridable_library_set('["advanced_mix_switch"]', True) 286 | emptyController.property_overridable_library_set('["advanced_mix_light_amount"]', True) 287 | emptyController.property_overridable_library_set('["advanced_mix_light_color"]', True) 288 | emptyController.property_overridable_library_set('["advanced_mix_shadow_amount"]', True) 289 | emptyController.property_overridable_library_set('["advanced_mix_shadow_tint"]', True) 290 | emptyController.property_overridable_library_set('["main_color_tint"]', True) 291 | emptyController.property_overridable_library_set('["main_color_tint_amount"]', True) 292 | emptyController.property_overridable_library_set('["shadow_color_tint"]', True) 293 | emptyController.property_overridable_library_set('["shadow_color_tint_amount"]', True) 294 | 295 | def add_crowd_properties(controller): 296 | 297 | emptyController = controller 298 | 299 | 300 | if "main_color_tint" not in emptyController: 301 | emptyController["main_color_tint"] = (1.0, 1.0, 1.0) 302 | 303 | ui_data = emptyController.id_properties_ui("main_color_tint") 304 | ui_data.update(min=0.0) 305 | ui_data.update(max=2.0) 306 | ui_data.update(soft_min=0.0) 307 | ui_data.update(soft_max=2.0) 308 | ui_data.update(description="Color to tint the main Color of a Character") 309 | ui_data.update(default=(1.0, 1.0, 1.0)) 310 | ui_data.update(subtype='COLOR') 311 | 312 | if "main_color_tint_amount" not in emptyController: 313 | emptyController["main_color_tint_amount"] = (0.0) 314 | 315 | ui_data = emptyController.id_properties_ui("main_color_tint_amount") 316 | ui_data.update(min=0.0) 317 | ui_data.update(max=1.0) 318 | ui_data.update(soft_min=0.0) 319 | ui_data.update(soft_max=1.0) 320 | ui_data.update(description="Amount of tint being applied to the main Color") 321 | ui_data.update(default=0.0) 322 | 323 | if "shadow_color_tint" not in emptyController: 324 | emptyController["shadow_color_tint"] = (1.0, 1.0, 1.0) 325 | 326 | ui_data = emptyController.id_properties_ui("shadow_color_tint") 327 | ui_data.update(min=0.0) 328 | ui_data.update(max=2.0) 329 | ui_data.update(soft_min=0.0) 330 | ui_data.update(soft_max=2.0) 331 | ui_data.update(description="Color ot tint the shadow color of a Character") 332 | ui_data.update(default=(1.0, 1.0, 1.0)) 333 | ui_data.update(subtype='COLOR') 334 | 335 | if "shadow_color_tint_amount" not in emptyController: 336 | emptyController["shadow_color_tint_amount"] = (0.0) 337 | 338 | ui_data = emptyController.id_properties_ui("shadow_color_tint_amount") 339 | ui_data.update(min=0.0) 340 | ui_data.update(max=1.0) 341 | ui_data.update(soft_min=0.0) 342 | ui_data.update(soft_max=1.0) 343 | ui_data.update(description="Amount of tint being applied to the shadow Color") 344 | ui_data.update(default=0.0) 345 | 346 | 347 | emptyController.property_overridable_library_set('["main_color_tint"]', True) 348 | emptyController.property_overridable_library_set('["main_color_tint_amount"]', True) 349 | emptyController.property_overridable_library_set('["shadow_color_tint"]', True) 350 | emptyController.property_overridable_library_set('["shadow_color_tint_amount"]', True) 351 | 352 | def register(): 353 | 354 | bpy.utils.register_class(CST_OT_CreateShaderController_OP) 355 | bpy.utils.register_class(CST_OT_UpdateShaderController_OP) 356 | bpy.utils.register_class(CST_OT_CreateCrowdShaderController_OP) 357 | 358 | def unregister(): 359 | bpy.utils.unregister_class(CST_OT_CreateCrowdShaderController_OP) 360 | bpy.utils.unregister_class(CST_OT_UpdateShaderController_OP) 361 | bpy.utils.unregister_class(CST_OT_CreateShaderController_OP) --------------------------------------------------------------------------------