import bpy from . import fn class AW_PT_walk_cycle_anim_panel(bpy.types.Panel): bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Walk" bl_label = "Auto Walk" def draw(self, context): layout = self.layout prefs = fn.get_addon_prefs() ob = context.object settings = context.scene.anim_cycle_settings tweak = settings.tweak # need to know root orientation forward) ## know direction to evaluate feet moves ## Define Constraint axis (depend on root orientation) # layout.prop(settings, "forward_axis") # plain prop col = layout.column() row = col.row() row.label(text='Forward Axis') row.prop(settings, "forward_axis", text='') row.operator('autowalk.open_addon_prefs', icon='PREFERENCES', text='') col.operator("autowalk.autoset_axis", text='Auto-Set Axis') # maybe check for fcruve cycle at the end of autoset axis ? (like a check) col.operator("autowalk.create_cycles_modifiers", text='Add Cycles Modifiers', icon='GRAPH') pb = None constrained = False if ob and ob.type == 'ARMATURE': pb = ob.pose.bones.get(prefs.tgt_bone) if pb: follow = pb.constraints.get('Follow Path') if follow and follow.target: constrained = True if not settings.path_to_follow and not constrained: layout.operator('autowalk.create_curve_path', text='Create Curve at Root Position', icon='CURVE_BEZCURVE') if (w_co := context.scene.collection.children.get('walk_markers')) and w_co.objects: row=layout.row(align=True) row.label(text=f"1st Point Frame {w_co.objects[0].name.split('_')[-1]}") row.operator('autowalk.remove_a_b_step', text='', icon='X') # Remove layout.operator('autowalk.create_a_b_step', text='Set 2nd Point', icon='CURVE_PATH') else: layout.operator('autowalk.create_a_b_step', text='Set 1st Point', icon='CURVE_PATH') else: layout.operator('autowalk.edit_curve', text='Edit Curve', icon='OUTLINER_DATA_CURVE') # FORCE_CURVE elif ob and ob.type == 'CURVE': if context.mode in ('OBJECT', 'EDIT_CURVE') \ and settings.path_to_follow \ and ob == settings.path_to_follow: layout.operator('autowalk.object_from_curve', text='Back To Object', icon='LOOP_BACK') box = layout.box() expand_icon = 'TRIA_DOWN' if tweak else 'TRIA_RIGHT' box.prop(settings, 'tweak', text='Curve Options', icon=expand_icon) if tweak: #-# path and ground objects box.prop(settings, "tgt_bone", text='Bone') box.prop_search(settings, "path_to_follow", context.scene, "objects") box.prop_search(settings, "gnd", context.scene, "objects") row = box.row() if ob and ob.type == 'ARMATURE': row.operator('autowalk.snap_curve_to_ground', text='Snap Curve To Ground', icon='SNAP_ON') elif ob and ob.type == 'CURVE': row.operator('autowalk.snap_selected_curve', text='Snap Selected Curve To Ground', icon='SNAP_ON') else: row.label(text='Select curve or armature to snap', icon='INFO') row.active = bool(settings.gnd) # Determine if already has a constraint (a bit too much condition in a panel...) if ob: if ob.type == 'ARMATURE': pb = ob.pose.bones.get(prefs.tgt_bone) if pb: follow = pb.constraints.get('Follow Path') if follow and follow.target: row = box.row() row.label(text=f'{pb.name} -> {follow.target.name}', icon='CON_FOLLOWPATH') row.operator('autowalk.remove_follow_path', text='', icon='X') constrained = True ## Put this in a setting popup or submenu # if context.mode == 'POSE': if not constrained: box.operator('autowalk.create_follow_path', text='Add follow path constraint', icon='CON_FOLLOWPATH') box = layout.box() col=box.column() col.label(text='Motion:') row = col.row(align=True) row.prop(settings, "start_frame", text='Start') row.prop(settings, "end_frame", text='End') col.operator('autowalk.animate_path', text='Move On Curve', icon='ANIM') # Animate Forward Motion row=col.row() row.operator('autowalk.adjust_animation_length', text='Adjust Speed', icon='MOD_TIME') # Adjust Forward Speed ## Bake cycle (on selected) box = layout.box() col=box.column() col.label(text='Action:') row=col.row() row.prop(settings, "linear", text='Linear') row.prop(settings, "expand_on_selected_bones") txt = 'Bake keys' if settings.linear else 'Bake keys and step path' col.operator('autowalk.bake_cycle_and_step', text=txt, icon='SHAPEKEY_DATA') # Custom Axis Pinning subox = col.box() subox.prop(settings, "custom_pin", text='Custom Pinning') if settings.custom_pin: row=subox.row(align=True) row.label(text='Location') row.prop(settings, 'pin_loc_x', text='X', toggle=True) row.prop(settings, 'pin_loc_y', text='Y', toggle=True) row.prop(settings, 'pin_loc_z', text='Z', toggle=True) row=subox.row(align=True) row.label(text='Rotation') row.prop(settings, 'pin_rot_x', text='X', toggle=True) row.prop(settings, 'pin_rot_y', text='Y', toggle=True) row.prop(settings, 'pin_rot_z', text='Z', toggle=True) # Pin feet col.operator('autowalk.pin_feets', text='Pin feets', icon='PINNED') ## show a dropdown allowing to go back to unpinned, unbaked version of the animation if ob and ob.type == 'ARMATURE': anim = ob.animation_data if anim and anim.action and not anim.use_tweak_mode: # skipped if in NLA tweak mode because anim.is_property_readonly('action') = True if 'baked' in anim.action.name or 'pinned' in anim.action.name: col=box.column() col.operator('autowalk.step_back_actions', text='Use Previous Actions', icon= 'ACTION') class AW_MT_wrap_animation_help(bpy.types.Menu): # bl_idname = "OBJECT_MT_custom_menu" bl_label = "Wrap Animation Infos" def draw(self, context): layout = self.layout col = layout.column() # col.label(text='Wrap animation:', icon='INFO') col.label(text='Apply offset from ref bone to root bone') col.label(text='on all selected pose bones') col.separator() col.label(text='Example:') col.label(text='Root is the bone to return to. Often root/walk/world bone') col.label(text='In most cases, Ref bone will be the bottom spine bone') col.label(text='Applying with spine selected will align spine with root on X-Y axis') # col.separator() # col.label(text='Note: If resulted animation is broken', icon='ERROR') # col.label(text='try applying with only one selected bone at a time') class AW_OT_bone_eye_dropper(bpy.types.Operator): bl_idname = "auto_walk.bone_eye_dropper" bl_label = "Bone Eye Dropper" bl_description = "Fill the field with active pose bone" bl_options = {'INTERNAL'} field : bpy.props.StringProperty() prop : bpy.props.StringProperty() def execute(self, context): prop = self.prop field = eval(self.field) setattr(field, prop, context.active_pose_bone.name) return {'FINISHED'} class AW_PT_wrap_animation_panel(bpy.types.Panel): bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Walk" bl_label = "Wrap Anim" bl_options = {'DEFAULT_CLOSED'} def draw(self, context): layout = self.layout layout.use_property_split = True col = layout.column() if not context.object or context.object.type != 'ARMATURE': col.label(text='Active object must be of Armature type') return row = col.row() row.label(text='Offset Reference to Root') row.operator("wm.call_menu", text="", icon='QUESTION').name = "AW_MT_wrap_animation_help" settings = context.scene.anim_cycle_settings for prop_name in ("wrap_root_bone", "wrap_ref_bone"): row = col.row() row.prop_search(settings, prop_name, context.object.pose, "bones") eyedrop = row.operator('auto_walk.bone_eye_dropper', text='', icon='EYEDROPPER') eyedrop.field = repr(settings) eyedrop.prop = prop_name col.operator('autowalk.wrap_animation', text='Wrap Animation') # , icon='' class AW_PT_anim_tools_panel(bpy.types.Panel): bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Walk" bl_label = "Pin Tools" def draw(self, context): layout = self.layout ## FIXME: need to fix vertices get # layout.operator('autowalk.contact_to_ground', text='Ground selected feet', icon='SNAP_OFF') row = layout.row() row.operator('pose.world_space_copy', text='Copy Pose', icon='COPYDOWN') # row.operator('autowalk.world_space_paste', text='Paste', icon='PASTEDOWN') # row = layout.row(align=False) ## multi buttons # row.label(text='Paste and jump:') # row = layout.row(align=True) # row.operator('autowalk.world_space_paste_next', text='Prev key', icon='PREV_KEYFRAME').prev = True # Paste Prev key # row.operator('autowalk.world_space_paste_next', text='Next key', icon='NEXT_KEYFRAME').prev = False # Paste Next key # row = layout.row(align=True) # row.operator('autowalk.world_space_paste_next_frame', text='Prev frame', icon='FRAME_PREV').prev = True # Paste Prev frame # row.operator('autowalk.world_space_paste_next_frame', text='Next frame', icon='FRAME_NEXT').prev = False # Paste Next frame row = layout.row(align=True) row.operator('pose.world_space_paste_next_frame', text='', icon='FRAME_PREV').prev = True row.operator('pose.world_space_paste_next', text='', icon='PREV_KEYFRAME').prev = True row.operator('pose.world_space_paste', text='', icon='PASTEDOWN') row.operator('pose.world_space_paste_next', text='', icon='NEXT_KEYFRAME').prev = False row.operator('pose.world_space_paste_next_frame', text='', icon='FRAME_NEXT').prev = False row.scale_x = 2 class AW_PT_nla_tools_panel(bpy.types.Panel): bl_space_type = "NLA_EDITOR" bl_region_type = "UI" bl_category = "Strip" bl_label = "Walk Tools" def draw(self, context): layout = self.layout # layout.label(text='Retime Tools') settings = context.scene.anim_cycle_settings row = layout.row(align=True) row.operator('autowalk.nla_key_speed', text='Set/Update Time Keys', icon='TIME') row.operator('autowalk.nla_remove_key_speed', text='', icon='X') classes=( AW_PT_walk_cycle_anim_panel, AW_PT_anim_tools_panel, AW_PT_nla_tools_panel, AW_MT_wrap_animation_help, AW_OT_bone_eye_dropper, AW_PT_wrap_animation_panel, ) classes_override_category =( AW_PT_walk_cycle_anim_panel, AW_PT_anim_tools_panel, ) ## Addons Preferences Update Panel def update_panel(self, context): for cls in classes_override_category: # classes try: bpy.utils.unregister_class(cls) except: pass cls.bl_category = self.category # fn.get_addon_prefs().category bpy.utils.register_class(cls) def register(): for cls in classes: bpy.utils.register_class(cls) def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls)