Newer
Older
#====================== BEGIN GPL LICENSE BLOCK ======================
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#======================= END GPL LICENSE BLOCK ========================
from bpy.props import (
BoolProperty,
IntProperty,
EnumProperty,
StringProperty
)
from mathutils import Color
Alexander Gavrilov
committed
from .utils.errors import MetarigError
from .utils.rig import write_metarig
from .utils.widgets import write_widget
from .utils.naming import unique_name
from .utils.rig import upgradeMetarigTypes, outdated_types
from .rigs.utils import get_limb_generated_names
from .utils.animation import get_keyed_frames_in_range, bones_in_frame, overwrite_prop_animation
from .utils.animation import RIGIFY_OT_get_frame_range
from .utils.animation import register as animation_register
from .utils.animation import unregister as animation_unregister
from . import base_rig
from . import rig_lists
from . import generate
from . import rot_mode
from . import feature_set_list
Alexander Gavrilov
committed
def build_type_list(context, rigify_types):
rigify_types.clear()
for r in sorted(rig_lists.rigs):
if (context.object.data.active_feature_set in ('all', rig_lists.rigs[r]['feature_set'])
or len(feature_set_list.feature_set_items(context.scene, context)) == 2
Alexander Gavrilov
committed
):
a = rigify_types.add()
a.name = r
class DATA_PT_rigify_buttons(bpy.types.Panel):
bl_label = "Rigify Buttons"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
if not context.object:
return False
return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
def draw(self, context):
C = context
layout = self.layout
obj = context.object
Campbell Barton
committed
id_store = C.window_manager
if obj.mode in {'POSE', 'OBJECT'}:
armature_id_store = C.object.data
WARNING = "Warning: Some features may change after generation"
show_warning = False
show_not_updatable = False
check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch']
Alexander Gavrilov
committed
for bone in obj.pose.bones:
if bone.bone.layers[30] and (list(set(bone.keys()) & set(check_props))):
show_warning = True
break
for b in obj.pose.bones:
if b.rigify_type in outdated_types.keys():
if outdated_types[b.rigify_type]:
show_update_metarig = True
else:
show_update_metarig = False
show_not_updatable = True
break
if show_warning:
layout.label(text=WARNING, icon='ERROR')
if show_not_updatable:
layout.label(text="WARNING: This metarig contains deprecated rigify rig-types and cannot be upgraded automatically.", icon='ERROR')
layout.label(text="If you want to use it anyway try enabling the legacy mode before generating again.")
layout.operator("pose.rigify_switch_to_legacy", text="Switch to Legacy")
enable_generate_and_advanced = not (show_not_updatable or show_update_metarig)
if show_update_metarig:
layout.label(text="This metarig contains old rig-types that can be automatically upgraded to benefit of rigify's new features.", icon='ERROR')
layout.label(text="To use it as-is you need to enable legacy mode.",)
layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig")
row = layout.row()
Alexander Gavrilov
committed
# Rig type field
col = layout.column(align=True)
col.active = (not 'rig_id' in C.object.data)
col.separator()
row = col.row()
row.operator("pose.rigify_generate", text="Generate Rig", icon='POSE_HLT')
Alexander Gavrilov
committed
row.enabled = enable_generate_and_advanced
if armature_id_store.rigify_advanced_generation:
Lucio Rossi
committed
icon = 'UNLOCKED'
else:
icon = 'LOCKED'
col = layout.column()
col.enabled = enable_generate_and_advanced
row = col.row()
row.prop(armature_id_store, "rigify_advanced_generation", toggle=True, icon=icon)
if armature_id_store.rigify_advanced_generation:
row = col.row(align=True)
row.prop(armature_id_store, "rigify_generate_mode", expand=True)
Lucio Rossi
committed
main_row = col.row(align=True).split(factor=0.3)
col1 = main_row.column()
col2 = main_row.column()
Lucio Rossi
committed
col1.label(text="Rig Name")
row = col1.row()
row.label(text="Target Rig")
row.enabled = (armature_id_store.rigify_generate_mode == "overwrite")
Lucio Rossi
committed
row = col1.row()
row.label(text="Target UI")
row.enabled = (armature_id_store.rigify_generate_mode == "overwrite")
row = col2.row(align=True)
row.prop(armature_id_store, "rigify_rig_basename", text="", icon="SORTALPHA")
Lucio Rossi
committed
row = col2.row(align=True)
row.prop(armature_id_store, "rigify_target_rig", text="")
row.enabled = (armature_id_store.rigify_generate_mode == "overwrite")
row = col2.row()
row.prop(armature_id_store, "rigify_rig_ui", text="", icon='TEXT')
row.enabled = (armature_id_store.rigify_generate_mode == "overwrite")
row = col.row()
row.prop(armature_id_store, "rigify_force_widget_update")
if armature_id_store.rigify_generate_mode == 'new':
Lucio Rossi
committed
row.enabled = False
elif obj.mode == 'EDIT':
# Build types list
Alexander Gavrilov
committed
build_type_list(context, id_store.rigify_types)
Alexander Gavrilov
committed
if id_store.rigify_active_type > len(id_store.rigify_types):
id_store.rigify_active_type = 0
if len(feature_set_list.feature_set_items(context.scene, context)) > 2:
Alexander Gavrilov
committed
row = layout.row()
row.prop(context.object.data, "active_feature_set")
row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type')
props = layout.operator("armature.metarig_sample_add", text="Add sample")
props.metarig_type = id_store.rigify_types[id_store.rigify_active_type].name
Nathan Vegdahl
committed
class DATA_PT_rigify_layer_names(bpy.types.Panel):
bl_label = "Rigify Layer Names"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
Nathan Vegdahl
committed
def poll(cls, context):
if not context.object:
return False
return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
Nathan Vegdahl
committed
def draw(self, context):
layout = self.layout
obj = context.object
Nathan Vegdahl
committed
# Ensure that the layers exist
Campbell Barton
committed
if 0:
for i in range(1 + len(arm.rigify_layers), 29):
arm.rigify_layers.add()
Campbell Barton
committed
else:
# Can't add while drawing, just use button
if len(arm.rigify_layers) < 29:
Campbell Barton
committed
layout.operator("pose.rigify_layer_init")
return
Nathan Vegdahl
committed
# UI
main_row = layout.row(align=True).split(factor=0.05)
col1 = main_row.column()
col2 = main_row.column()
col1.label()
for i in range(32):
if i == 16 or i == 29:
col1.label()
for i, rigify_layer in enumerate(arm.rigify_layers):
# note: rigify_layer == arm.rigify_layers[i]
Nathan Vegdahl
committed
if (i % 16) == 0:
col = col2.column()
Nathan Vegdahl
committed
if i == 0:
col.label(text="Top Row:")
else:
col.label(text="Bottom Row:")
if (i % 8) == 0:
col = col2.column()
if i != 28:
row = col.row(align=True)
icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
#row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
row.prop(rigify_layer, "name", text="")
row.prop(rigify_layer, "row", text="UI Row")
icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
row.prop(rigify_layer, "group", text="Bone Group")
else:
row = col.row(align=True)
icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
# row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
row1 = row.split(align=True).row(align=True)
row1.prop(rigify_layer, "name", text="")
row1.prop(rigify_layer, "row", text="UI Row")
row1.enabled = False
icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
row.prop(rigify_layer, "group", text="Bone Group")
if rigify_layer.group == 0:
row.label(text='None')
else:
row.label(text=arm.rigify_colors[rigify_layer.group-1].name)
col = col2.column()
col.label(text="Reserved:")
# reserved_names = {28: 'Root', 29: 'DEF', 30: 'MCH', 31: 'ORG'}
reserved_names = {29: 'DEF', 30: 'MCH', 31: 'ORG'}
# for i in range(28, 32):
for i in range(29, 32):
row = col.row(align=True)
icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
row.label(text=reserved_names[i])
class DATA_OT_rigify_add_bone_groups(bpy.types.Operator):
bl_idname = "armature.rigify_add_bone_groups"
bl_label = "Rigify Add Standard Bone Groups"
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
obj = context.object
armature = obj.data
if not hasattr(armature, 'rigify_colors'):
return {'FINISHED'}
groups = ['Root', 'IK', 'Special', 'Tweak', 'FK', 'Extra']
for g in groups:
if g in armature.rigify_colors.keys():
continue
armature.rigify_colors.add()
armature.rigify_colors[-1].name = g
armature.rigify_colors[g].select = Color((0.3140000104904175, 0.7839999794960022, 1.0))
armature.rigify_colors[g].active = Color((0.5490000247955322, 1.0, 1.0))
armature.rigify_colors[g].standard_colors_lock = True
if g == "Root":
armature.rigify_colors[g].normal = Color((0.43529415130615234, 0.18431372940540314, 0.41568630933761597))
if g == "IK":
armature.rigify_colors[g].normal = Color((0.6039215922355652, 0.0, 0.0))
if g== "Special":
armature.rigify_colors[g].normal = Color((0.9568628072738647, 0.7882353663444519, 0.0470588281750679))
if g== "Tweak":
armature.rigify_colors[g].normal = Color((0.03921568766236305, 0.21176472306251526, 0.5803921818733215))
if g== "FK":
armature.rigify_colors[g].normal = Color((0.11764706671237946, 0.5686274766921997, 0.03529411926865578))
if g== "Extra":
armature.rigify_colors[g].normal = Color((0.9686275124549866, 0.250980406999588, 0.0941176563501358))
return {'FINISHED'}
class DATA_OT_rigify_use_standard_colors(bpy.types.Operator):
bl_idname = "armature.rigify_use_standard_colors"
bl_label = "Rigify Get active/select colors from current theme"
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
obj = context.object
armature = obj.data
if not hasattr(armature, 'rigify_colors'):
return {'FINISHED'}
current_theme = bpy.context.preferences.themes.items()[0][0]
theme = bpy.context.preferences.themes[current_theme]
armature.rigify_selection_colors.select = theme.view_3d.bone_pose
armature.rigify_selection_colors.active = theme.view_3d.bone_pose_active
# for col in armature.rigify_colors:
# col.select = theme.view_3d.bone_pose
# col.active = theme.view_3d.bone_pose_active
return {'FINISHED'}
class DATA_OT_rigify_apply_selection_colors(bpy.types.Operator):
bl_idname = "armature.rigify_apply_selection_colors"
bl_label = "Rigify Apply user defined active/select colors"
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
obj = context.object
armature = obj.data
if not hasattr(armature, 'rigify_colors'):
return {'FINISHED'}
#current_theme = bpy.context.preferences.themes.items()[0][0]
#theme = bpy.context.preferences.themes[current_theme]
for col in armature.rigify_colors:
col.select = armature.rigify_selection_colors.select
col.active = armature.rigify_selection_colors.active
return {'FINISHED'}
class DATA_OT_rigify_bone_group_add(bpy.types.Operator):
bl_idname = "armature.rigify_bone_group_add"
bl_label = "Rigify Add Bone Group color set"
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
obj = context.object
armature = obj.data
if hasattr(armature, 'rigify_colors'):
armature.rigify_colors.add()
armature.rigify_colors[-1].name = unique_name(armature.rigify_colors, 'Group')
current_theme = bpy.context.preferences.themes.items()[0][0]
theme = bpy.context.preferences.themes[current_theme]
armature.rigify_colors[-1].normal = theme.view_3d.wire
armature.rigify_colors[-1].normal.hsv = theme.view_3d.wire.hsv
armature.rigify_colors[-1].select = theme.view_3d.bone_pose
armature.rigify_colors[-1].select.hsv = theme.view_3d.bone_pose.hsv
armature.rigify_colors[-1].active = theme.view_3d.bone_pose_active
armature.rigify_colors[-1].active.hsv = theme.view_3d.bone_pose_active.hsv
return {'FINISHED'}
class DATA_OT_rigify_bone_group_add_theme(bpy.types.Operator):
bl_idname = "armature.rigify_bone_group_add_theme"
bl_label = "Rigify Add Bone Group color set from Theme"
bl_options = {"REGISTER", "UNDO"}
theme: EnumProperty(items=(
('THEME01', 'THEME01', ''),
('THEME02', 'THEME02', ''),
('THEME03', 'THEME03', ''),
('THEME04', 'THEME04', ''),
('THEME05', 'THEME05', ''),
('THEME06', 'THEME06', ''),
('THEME07', 'THEME07', ''),
('THEME08', 'THEME08', ''),
('THEME09', 'THEME09', ''),
('THEME10', 'THEME10', ''),
('THEME11', 'THEME11', ''),
('THEME12', 'THEME12', ''),
('THEME13', 'THEME13', ''),
('THEME14', 'THEME14', ''),
('THEME15', 'THEME15', ''),
('THEME16', 'THEME16', ''),
('THEME17', 'THEME17', ''),
('THEME18', 'THEME18', ''),
('THEME19', 'THEME19', ''),
('THEME20', 'THEME20', '')
),
name='Theme')
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
obj = context.object
armature = obj.data
if hasattr(armature, 'rigify_colors'):
if self.theme in armature.rigify_colors.keys():
return {'FINISHED'}
armature.rigify_colors.add()
armature.rigify_colors[-1].name = self.theme
id = int(self.theme[-2:]) - 1
theme_color_set = bpy.context.preferences.themes[0].bone_color_sets[id]
armature.rigify_colors[-1].normal = theme_color_set.normal
armature.rigify_colors[-1].select = theme_color_set.select
armature.rigify_colors[-1].active = theme_color_set.active
return {'FINISHED'}
class DATA_OT_rigify_bone_group_remove(bpy.types.Operator):
bl_idname = "armature.rigify_bone_group_remove"
bl_label = "Rigify Remove Bone Group color set"
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
obj = context.object
obj.data.rigify_colors.remove(self.idx)
# set layers references to 0
for l in obj.data.rigify_layers:
if l.group == self.idx + 1:
l.group = 0
elif l.group > self.idx + 1:
l.group -= 1
return {'FINISHED'}
class DATA_OT_rigify_bone_group_remove_all(bpy.types.Operator):
bl_idname = "armature.rigify_bone_group_remove_all"
bl_label = "Rigify Remove All Bone Groups"
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
obj = context.object
for i, col in enumerate(obj.data.rigify_colors):
obj.data.rigify_colors.remove(0)
# set layers references to 0
for l in obj.data.rigify_layers:
if l.group == i + 1:
l.group = 0
return {'FINISHED'}
class DATA_UL_rigify_bone_groups(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
row = layout.row(align=True)
row.label(text=str(index+1))
row.prop(item, "name", text='', emboss=False)
row = row.row(align=True)
icon = 'LOCKED' if item.standard_colors_lock else 'UNLOCKED'
#row.prop(item, "standard_colors_lock", text='', icon=icon)
row.prop(item, "normal", text='')
row2 = row.row(align=True)
row2.prop(item, "select", text='')
row2.prop(item, "active", text='')
#row2.enabled = not item.standard_colors_lock
row2.enabled = not bpy.context.object.data.rigify_colors_lock
class DATA_MT_rigify_bone_groups_context_menu(bpy.types.Menu):
bl_label = 'Rigify Bone Groups Specials'
def draw(self, context):
layout = self.layout
layout.operator('armature.rigify_bone_group_remove_all')
class DATA_PT_rigify_bone_groups(bpy.types.Panel):
bl_label = "Rigify Bone Groups"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
if not context.object:
return False
return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
def draw(self, context):
obj = context.object
armature = obj.data
color_sets = obj.data.rigify_colors
idx = obj.data.rigify_colors_index
layout = self.layout
row = layout.row()
row.operator("armature.rigify_use_standard_colors", icon='FILE_REFRESH', text='')
row = row.row(align=True)
row.prop(armature.rigify_selection_colors, 'select', text='')
row.prop(armature.rigify_selection_colors, 'active', text='')
row = layout.row(align=True)
icon = 'LOCKED' if armature.rigify_colors_lock else 'UNLOCKED'
row.prop(armature, 'rigify_colors_lock', text = 'Unified select/active colors', icon=icon)
row.operator("armature.rigify_apply_selection_colors", icon='FILE_REFRESH', text='Apply')
row = layout.row()
row.template_list("DATA_UL_rigify_bone_groups", "", obj.data, "rigify_colors", obj.data, "rigify_colors_index")
col = row.column(align=True)
col.operator("armature.rigify_bone_group_add", icon='ZOOM_IN', text="")
col.operator("armature.rigify_bone_group_remove", icon='ZOOM_OUT', text="").idx = obj.data.rigify_colors_index
col.menu("DATA_MT_rigify_bone_groups_context_menu", icon='DOWNARROW_HLT', text="")
row = layout.row()
row.prop(armature, 'rigify_theme_to_add', text = 'Theme')
op = row.operator("armature.rigify_bone_group_add_theme", text="Add From Theme")
op.theme = armature.rigify_theme_to_add
row = layout.row()
row.operator("armature.rigify_add_bone_groups", text="Add Standard")
Nathan Vegdahl
committed
class BONE_PT_rigify_buttons(bpy.types.Panel):
bl_label = "Rigify Type"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "bone"
#bl_options = {'DEFAULT_OPEN'}
@classmethod
def poll(cls, context):
if not context.object:
return False
Loading
Loading full blame...