This PR adds an option "Affect Gizmo" for custom shape transforms to affect the transform gizmos. If enabled, this will place the gizmo at the location and orientation of the "Override Transform" (i.e. in its local space). For Orientations: The gizmo mode *is* respected. I.e. global mode is aligned to the world etc. There is a special case for "Gimbal" where it will not follow the orientation of the "Override Transform" bone. I think it makes sense to keep it that way since this is about the channels of the bone you are actually manipulating. The other option is "Use as Pivot" with which the bone is actually rotated around the override bone. This can be useful for rigs in which shapekeys and armature deformation is combined Taken over from #136468 For design task #135429 Co-authored-by: Wayde Moss <wbmoss_dev@yahoo.com> Pull Request: https://projects.blender.org/blender/blender/pulls/142847
626 lines
21 KiB
Python
626 lines
21 KiB
Python
# SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import bpy
|
|
from bpy.types import Panel
|
|
import rna_prop_ui
|
|
|
|
from bpy.app.translations import contexts as i18n_contexts
|
|
|
|
|
|
class BoneButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "bone"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return (context.bone or context.edit_bone)
|
|
|
|
|
|
class BONE_PT_context_bone(BoneButtonsPanel, Panel):
|
|
bl_label = ""
|
|
bl_options = {'HIDE_HEADER'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
bone = context.bone
|
|
if not bone:
|
|
bone = context.edit_bone
|
|
|
|
row = layout.row()
|
|
row.label(text="", icon='BONE_DATA')
|
|
row.prop(bone, "name", text="")
|
|
|
|
|
|
class BONE_PT_transform(BoneButtonsPanel, Panel):
|
|
bl_label = "Transform"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if context.edit_bone:
|
|
return True
|
|
|
|
ob = context.object
|
|
return ob and ob.mode == 'POSE' and context.bone
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
ob = context.object
|
|
bone = context.bone
|
|
|
|
col = layout.column()
|
|
|
|
if bone and ob:
|
|
pchan = ob.pose.bones[bone.name]
|
|
col.active = not (bone.parent and bone.use_connect)
|
|
|
|
row = col.row(align=True)
|
|
row.prop(pchan, "location")
|
|
row.use_property_decorate = False
|
|
row.prop(pchan, "lock_location", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
|
|
|
rotation_mode = pchan.rotation_mode
|
|
if rotation_mode == 'QUATERNION':
|
|
col = layout.column()
|
|
row = col.row(align=True)
|
|
row.prop(pchan, "rotation_quaternion", text="Rotation")
|
|
sub = row.column(align=True)
|
|
sub.use_property_decorate = False
|
|
sub.prop(pchan, "lock_rotation_w", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
|
sub.prop(pchan, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
|
elif rotation_mode == 'AXIS_ANGLE':
|
|
col = layout.column()
|
|
row = col.row(align=True)
|
|
row.prop(pchan, "rotation_axis_angle", text="Rotation")
|
|
|
|
sub = row.column(align=True)
|
|
sub.use_property_decorate = False
|
|
sub.prop(pchan, "lock_rotation_w", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
|
sub.prop(pchan, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
|
else:
|
|
col = layout.column()
|
|
row = col.row(align=True)
|
|
row.prop(pchan, "rotation_euler", text="Rotation")
|
|
row.use_property_decorate = False
|
|
row.prop(pchan, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
|
row = layout.row(align=True)
|
|
row.prop(pchan, "rotation_mode", text="Mode")
|
|
row.label(text="", icon='BLANK1')
|
|
|
|
col = layout.column()
|
|
row = col.row(align=True)
|
|
row.prop(pchan, "scale")
|
|
row.use_property_decorate = False
|
|
row.prop(pchan, "lock_scale", text="", emboss=False, icon='DECORATE_UNLOCKED')
|
|
|
|
elif context.edit_bone:
|
|
bone = context.edit_bone
|
|
col = layout.column()
|
|
col.prop(bone, "head")
|
|
col.prop(bone, "tail")
|
|
|
|
col = layout.column()
|
|
col.prop(bone, "roll")
|
|
col.prop(bone, "length")
|
|
col.prop(bone, "lock")
|
|
|
|
|
|
class BONE_PT_curved(BoneButtonsPanel, Panel):
|
|
bl_label = "Bendy Bones"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
def draw(self, context):
|
|
ob = context.object
|
|
bone = context.bone
|
|
arm = context.armature
|
|
bone_list = "bones"
|
|
|
|
if ob and bone:
|
|
bbone = ob.pose.bones[bone.name]
|
|
elif bone is None:
|
|
bone = context.edit_bone
|
|
bbone = bone
|
|
bone_list = "edit_bones"
|
|
else:
|
|
bbone = bone
|
|
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
layout.prop(bone, "bbone_segments", text="Segments")
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(bone, "bbone_x", text="Display Size X")
|
|
col.prop(bone, "bbone_z", text="Z")
|
|
|
|
topcol = layout.column()
|
|
topcol.active = bone.bbone_segments > 1
|
|
|
|
topcol.prop(bone, "bbone_mapping_mode", text="Vertex Mapping")
|
|
|
|
col = topcol.column(align=True)
|
|
col.prop(bbone, "bbone_curveinx", text="Curve In X")
|
|
col.prop(bbone, "bbone_curveinz", text="Z")
|
|
|
|
col = topcol.column(align=True)
|
|
col.prop(bbone, "bbone_curveoutx", text="Curve Out X")
|
|
col.prop(bbone, "bbone_curveoutz", text="Z")
|
|
|
|
col = topcol.column(align=True)
|
|
col.prop(bbone, "bbone_rollin", text="Roll In")
|
|
col.prop(bbone, "bbone_rollout", text="Out", text_ctxt=i18n_contexts.id_armature)
|
|
col.prop(bone, "use_endroll_as_inroll")
|
|
|
|
col = topcol.column(align=True)
|
|
col.prop(bbone, "bbone_scalein", text="Scale In")
|
|
|
|
col = topcol.column(align=True)
|
|
col.prop(bbone, "bbone_scaleout", text="Scale Out")
|
|
|
|
col = topcol.column(align=True)
|
|
col.prop(bbone, "bbone_easein", text="Ease In", text_ctxt=i18n_contexts.id_armature)
|
|
col.prop(bbone, "bbone_easeout", text="Out", text_ctxt=i18n_contexts.id_armature)
|
|
col.prop(bone, "use_scale_easing")
|
|
|
|
col = topcol.column(align=True)
|
|
col.prop(bone, "bbone_handle_type_start", text="Start Handle")
|
|
|
|
col2 = col.column(align=True)
|
|
col2.active = (bone.bbone_handle_type_start != 'AUTO')
|
|
col2.prop_search(bone, "bbone_custom_handle_start", arm, bone_list, text="Custom")
|
|
|
|
row = col.row(align=True)
|
|
row.use_property_split = False
|
|
split = row.split(factor=0.4)
|
|
split.alignment = 'RIGHT'
|
|
split.label(text="Scale")
|
|
split2 = split.split(factor=0.7)
|
|
row2 = split2.row(align=True)
|
|
row2.prop(bone, "bbone_handle_use_scale_start", index=0, text="X", toggle=True)
|
|
row2.prop(bone, "bbone_handle_use_scale_start", index=1, text="Y", toggle=True)
|
|
row2.prop(bone, "bbone_handle_use_scale_start", index=2, text="Z", toggle=True)
|
|
split2.prop(bone, "bbone_handle_use_ease_start", text="Ease", text_ctxt=i18n_contexts.id_armature, toggle=True)
|
|
row.label(icon='BLANK1')
|
|
|
|
col = topcol.column(align=True)
|
|
col.prop(bone, "bbone_handle_type_end", text="End Handle")
|
|
|
|
col2 = col.column(align=True)
|
|
col2.active = (bone.bbone_handle_type_end != 'AUTO')
|
|
col2.prop_search(bone, "bbone_custom_handle_end", arm, bone_list, text="Custom")
|
|
|
|
row = col.row(align=True)
|
|
row.use_property_split = False
|
|
split = row.split(factor=0.4)
|
|
split.alignment = 'RIGHT'
|
|
split.label(text="Scale")
|
|
split2 = split.split(factor=0.7)
|
|
row2 = split2.row(align=True)
|
|
row2.prop(bone, "bbone_handle_use_scale_end", index=0, text="X", toggle=True)
|
|
row2.prop(bone, "bbone_handle_use_scale_end", index=1, text="Y", toggle=True)
|
|
row2.prop(bone, "bbone_handle_use_scale_end", index=2, text="Z", toggle=True)
|
|
split2.prop(bone, "bbone_handle_use_ease_end", text="Ease", text_ctxt=i18n_contexts.id_armature, toggle=True)
|
|
row.label(icon='BLANK1')
|
|
|
|
|
|
class BONE_PT_relations(BoneButtonsPanel, Panel):
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
bl_label = "Relations"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
ob = context.object
|
|
bone = context.bone
|
|
arm = context.armature
|
|
pchan = None
|
|
|
|
if ob and bone:
|
|
pchan = ob.pose.bones[bone.name]
|
|
elif bone is None:
|
|
bone = context.edit_bone
|
|
|
|
col = layout.column()
|
|
|
|
if context.bone:
|
|
col.prop(bone, "parent")
|
|
else:
|
|
col.prop_search(bone, "parent", arm, "edit_bones")
|
|
|
|
if ob and pchan:
|
|
col.prop(bone, "use_relative_parent")
|
|
|
|
sub = col.column()
|
|
sub.active = (bone.parent is not None)
|
|
sub.prop(bone, "use_connect")
|
|
sub = col.column()
|
|
sub.active = (not bone.parent or not bone.use_connect)
|
|
sub.prop(bone, "use_local_location")
|
|
sub = col.column()
|
|
sub.active = (bone.parent is not None)
|
|
sub.prop(bone, "use_inherit_rotation")
|
|
sub.prop(bone, "inherit_scale")
|
|
|
|
|
|
class BONE_PT_collections(BoneButtonsPanel, Panel):
|
|
bl_label = "Bone Collections"
|
|
bl_parent_id = "BONE_PT_relations"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.bone or context.edit_bone
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = False
|
|
|
|
bone = context.bone or context.edit_bone
|
|
object = context.pose_object or context.edit_object or context.object
|
|
if not object:
|
|
layout.active = False
|
|
sub = layout.column(align=True)
|
|
sub.label(text="Cannot figure out which object this bone belongs to.")
|
|
sub.label(text="Please file a bug report.")
|
|
return
|
|
|
|
armature = object.data
|
|
is_solo_active = armature.collections.is_solo_active
|
|
|
|
if not bone.collections:
|
|
layout.active = False
|
|
layout.label(text="Not assigned to any bone collection.")
|
|
return
|
|
|
|
box = layout.box()
|
|
sub = box.column(align=True)
|
|
for bcoll in bone.collections:
|
|
bcoll_row = sub.row()
|
|
bcoll_row.emboss = 'NONE'
|
|
|
|
# Name & visibility of bcoll. Safe things, so aligned together.
|
|
row = bcoll_row.row(align=True)
|
|
row.label(text=bcoll.name)
|
|
|
|
# Sub-layout that's dimmed when the bone collection's own visibility flag doesn't matter.
|
|
sub_visible = row.row(align=True)
|
|
sub_visible.active = (not is_solo_active) and bcoll.is_visible_ancestors
|
|
sub_visible.prop(bcoll, "is_visible", text="", icon='HIDE_OFF' if bcoll.is_visible else 'HIDE_ON')
|
|
|
|
row.prop(bcoll, "is_solo", text="", icon='SOLO_ON' if bcoll.is_solo else 'SOLO_OFF')
|
|
|
|
# Unassign operator, less safe so with a bit of spacing.
|
|
props = bcoll_row.operator("armature.collection_unassign_named", text="", icon='X')
|
|
props.name = bcoll.name
|
|
props.bone_name = bone.name
|
|
|
|
|
|
class BONE_PT_display(BoneButtonsPanel, Panel):
|
|
bl_label = "Viewport Display"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.bone or context.edit_bone
|
|
|
|
def draw(self, context):
|
|
# NOTE: this works ok in edit-mode but isn't
|
|
# all that useful so disabling for now.
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
if context.bone is None:
|
|
self.draw_edit_bone(context, layout)
|
|
else:
|
|
self.draw_bone(context, layout)
|
|
|
|
def draw_bone(self, context, layout):
|
|
bone = context.bone
|
|
|
|
col = layout.column()
|
|
# Figure out the pose bone.
|
|
ob = context.object
|
|
pose_bone = ob and ob.pose.bones[bone.name]
|
|
hide_select_sub = col.column()
|
|
if pose_bone:
|
|
col.prop(pose_bone, "hide", text="Hide", toggle=False)
|
|
hide_select_sub.active = not pose_bone.hide
|
|
hide_select_sub.prop(bone, "hide_select", invert_checkbox=True)
|
|
col.prop(bone, "display_type", text="Display As")
|
|
|
|
if not pose_bone:
|
|
return
|
|
|
|
# Allow the layout to use the space normally occupied by the 'set a key' diamond.
|
|
layout.use_property_decorate = False
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(bone.color, "palette", text="Bone Color")
|
|
props = row.operator("armature.copy_bone_color_to_selected", text="", icon='UV_SYNC_SELECT')
|
|
props.bone_type = 'EDIT'
|
|
self.draw_bone_color_ui(layout, bone.color)
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(pose_bone.color, "palette", text="Pose Bone Color")
|
|
props = row.operator("armature.copy_bone_color_to_selected", text="", icon='UV_SYNC_SELECT')
|
|
props.bone_type = 'POSE'
|
|
self.draw_bone_color_ui(layout, pose_bone.color)
|
|
|
|
def draw_edit_bone(self, context, layout):
|
|
bone = context.edit_bone
|
|
if bone is None:
|
|
return
|
|
|
|
col = layout.column()
|
|
col.prop(bone, "hide", text="Hide", toggle=False)
|
|
hide_select_sub = col.column()
|
|
hide_select_sub.active = not bone.hide
|
|
hide_select_sub.prop(bone, "hide_select", invert_checkbox=True)
|
|
col.prop(bone, "display_type", text="Display As")
|
|
layout.prop(bone.color, "palette", text="Bone Color")
|
|
self.draw_bone_color_ui(layout, bone.color)
|
|
|
|
def draw_bone_color_ui(self, layout, bone_color):
|
|
if not bone_color.is_custom:
|
|
return
|
|
|
|
split = layout.split(factor=0.401)
|
|
|
|
col = split.column()
|
|
row = col.row()
|
|
row.alignment = 'RIGHT'
|
|
row.label(text="Custom Colors")
|
|
|
|
col = split.column(align=True)
|
|
row = col.row(align=True)
|
|
row.use_property_split = False
|
|
row.prop(bone_color.custom, "normal", text="")
|
|
row.prop(bone_color.custom, "select", text="")
|
|
row.prop(bone_color.custom, "active", text="")
|
|
|
|
|
|
class BONE_PT_display_custom_shape(BoneButtonsPanel, Panel):
|
|
bl_label = "Custom Shape"
|
|
bl_parent_id = "BONE_PT_display"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.bone
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
ob = context.object
|
|
bone = context.bone
|
|
pchan = None
|
|
|
|
if ob and bone:
|
|
pchan = ob.pose.bones[bone.name]
|
|
elif bone is None:
|
|
bone = context.edit_bone
|
|
|
|
if bone and pchan:
|
|
col = layout.column()
|
|
col.prop(pchan, "custom_shape")
|
|
|
|
sub = col.column()
|
|
sub.active = bool(pchan and pchan.custom_shape)
|
|
sub.separator()
|
|
|
|
sub.prop(pchan, "custom_shape_translation", text="Translation")
|
|
sub.prop(pchan, "custom_shape_rotation_euler", text="Rotation")
|
|
sub.prop(pchan, "custom_shape_scale_xyz", text="Scale")
|
|
|
|
sub.prop_search(pchan, "custom_shape_transform", ob.pose, "bones", text="Override Transform")
|
|
subsub = sub.column()
|
|
subsub.active = bool(pchan and pchan.custom_shape and pchan.custom_shape_transform)
|
|
subsub.prop(pchan, "use_transform_at_custom_shape")
|
|
subsubsub = subsub.column()
|
|
subsubsub.active = subsub.active and pchan.use_transform_at_custom_shape
|
|
subsubsub.prop(pchan, "use_transform_around_custom_shape")
|
|
sub.prop(pchan, "use_custom_shape_bone_size")
|
|
|
|
sub.separator()
|
|
sub.prop(bone, "show_wire", text="Wireframe")
|
|
sub.prop(pchan, "custom_shape_wire_width")
|
|
|
|
|
|
class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
|
|
bl_label = "Inverse Kinematics"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
return ob and ob.mode == 'POSE' and context.bone
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
ob = context.object
|
|
bone = context.bone
|
|
pchan = ob.pose.bones[bone.name]
|
|
|
|
active = pchan.is_in_ik_chain
|
|
|
|
col = layout.column()
|
|
col.prop(pchan, "ik_stretch", slider=True)
|
|
col.active = active
|
|
|
|
layout.separator()
|
|
|
|
col = layout.column(align=True)
|
|
|
|
col.prop(pchan, "lock_ik_x", text="Lock IK X")
|
|
col.prop(pchan, "lock_ik_y", text="Y")
|
|
col.prop(pchan, "lock_ik_z", text="Z")
|
|
|
|
col = layout.column(align=True)
|
|
|
|
sub = col.column(align=True)
|
|
sub.active = pchan.lock_ik_x is False and active
|
|
sub.prop(pchan, "ik_stiffness_x", text="Stiffness X", slider=True)
|
|
sub = col.column(align=True)
|
|
sub.active = pchan.lock_ik_y is False and active
|
|
sub.prop(pchan, "ik_stiffness_y", text="Y", slider=True)
|
|
sub = col.column(align=True)
|
|
sub.active = pchan.lock_ik_z is False and active
|
|
sub.prop(pchan, "ik_stiffness_z", text="Z", slider=True)
|
|
|
|
col = layout.column(align=True)
|
|
|
|
sub = col.column()
|
|
sub.active = pchan.lock_ik_x is False and active
|
|
sub.prop(pchan, "use_ik_limit_x", text="Limit X")
|
|
|
|
sub = col.column(align=True)
|
|
sub.active = pchan.lock_ik_x is False and pchan.use_ik_limit_x and active
|
|
sub.prop(pchan, "ik_min_x", text="Min")
|
|
sub.prop(pchan, "ik_max_x", text="Max")
|
|
|
|
col.separator()
|
|
|
|
sub = col.column()
|
|
sub.active = pchan.lock_ik_y is False and active
|
|
sub.prop(pchan, "use_ik_limit_y", text="Limit Y")
|
|
|
|
sub = col.column(align=True)
|
|
sub.active = pchan.lock_ik_y is False and pchan.use_ik_limit_y and active
|
|
sub.prop(pchan, "ik_min_y", text="Min")
|
|
sub.prop(pchan, "ik_max_y", text="Max")
|
|
|
|
col.separator()
|
|
|
|
sub = col.column()
|
|
sub.active = pchan.lock_ik_z is False and active
|
|
sub.prop(pchan, "use_ik_limit_z", text="Limit Z")
|
|
|
|
sub = col.column(align=True)
|
|
sub.active = pchan.lock_ik_z is False and pchan.use_ik_limit_z and active
|
|
sub.prop(pchan, "ik_min_z", text="Min")
|
|
sub.prop(pchan, "ik_max_z", text="Max")
|
|
|
|
col.separator()
|
|
|
|
if ob.pose.ik_solver == 'ITASC':
|
|
|
|
col = layout.column()
|
|
col.prop(pchan, "use_ik_rotation_control", text="Control Rotation")
|
|
col.active = active
|
|
|
|
col = layout.column()
|
|
|
|
col.prop(pchan, "ik_rotation_weight", text="IK Rotation Weight", slider=True)
|
|
col.active = active
|
|
# not supported yet
|
|
# row = layout.row()
|
|
# row.prop(pchan, "use_ik_linear_control", text="Joint Size")
|
|
# row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
|
|
|
|
|
|
class BONE_PT_deform(BoneButtonsPanel, Panel):
|
|
bl_label = "Deform"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
def draw_header(self, context):
|
|
bone = context.bone
|
|
|
|
if not bone:
|
|
bone = context.edit_bone
|
|
|
|
self.layout.prop(bone, "use_deform", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
bone = context.bone
|
|
|
|
if not bone:
|
|
bone = context.edit_bone
|
|
|
|
layout.active = bone.use_deform
|
|
|
|
col = layout.column()
|
|
col.prop(bone, "envelope_distance", text="Envelope Distance")
|
|
col.prop(bone, "envelope_weight", text="Envelope Weight")
|
|
col.prop(bone, "use_envelope_multiply", text="Envelope Multiply")
|
|
|
|
col.separator()
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(bone, "head_radius", text="Radius Head")
|
|
col.prop(bone, "tail_radius", text="Tail")
|
|
|
|
|
|
class BONE_PT_custom_props(BoneButtonsPanel, rna_prop_ui.PropertyPanel, Panel):
|
|
_property_type = bpy.types.Bone, bpy.types.EditBone, bpy.types.PoseBone
|
|
|
|
@classmethod
|
|
def _poll(cls, context):
|
|
context_path = cls._get_context_path(context)
|
|
rna_item, _context_member = rna_prop_ui.rna_idprop_context_value(context, context_path, cls._property_type)
|
|
return bool(rna_item)
|
|
|
|
def draw(self, context):
|
|
context_path = self._get_context_path(context)
|
|
rna_prop_ui.draw(self.layout, context, context_path, self._property_type)
|
|
|
|
@classmethod
|
|
def _get_context_path(self, context):
|
|
if context.mode == 'EDIT_ARMATURE':
|
|
# This also accounts for pinned armatures.
|
|
return "edit_bone"
|
|
|
|
obj = context.object
|
|
if not obj:
|
|
# We have to return _something_. If there is some bone by some
|
|
# miracle, just use it.
|
|
return "bone"
|
|
|
|
if obj.mode != 'POSE':
|
|
# Outside of pose mode, active_bone is the one to use. It's either a
|
|
# Bone or an EditBone, depending on the mode.
|
|
return "active_bone"
|
|
|
|
if context.active_pose_bone is not None:
|
|
# There is an active pose bone, so use it.
|
|
return "active_pose_bone"
|
|
|
|
# When the active bone is hidden, `context.active_pose_bone` is None, but
|
|
# `context.bone` still points to it. Use that to still get the pose bone.
|
|
if context.bone is None:
|
|
# If there is no active bone, let the rest of the code refer to the
|
|
# also-None active pose bone, as that's more appropriate given we're
|
|
# currently in pose mode.
|
|
return "active_pose_bone"
|
|
|
|
bone_path = obj.pose.bones[context.bone.name].path_from_id()
|
|
return "object." + bone_path
|
|
|
|
|
|
classes = (
|
|
BONE_PT_context_bone,
|
|
BONE_PT_transform,
|
|
BONE_PT_curved,
|
|
BONE_PT_relations,
|
|
BONE_PT_collections,
|
|
BONE_PT_inverse_kinematics,
|
|
BONE_PT_deform,
|
|
BONE_PT_display,
|
|
BONE_PT_display_custom_shape,
|
|
BONE_PT_custom_props,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|