mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-16 15:35:05 +00:00

Python 3.4.0 deprecated the "imp" module, and replaced it with "importlib". This changes imp.reload() into implib.reload(). Reviewers: campbellbarton Differential Revision: https://developer.blender.org/D1016
438 lines
14 KiB
Python
438 lines
14 KiB
Python
#====================== 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 ========================
|
|
|
|
# <pep8 compliant>
|
|
|
|
import bpy
|
|
from bpy.props import StringProperty
|
|
|
|
from .utils import get_rig_type, MetarigError
|
|
from .utils import write_metarig, write_widget
|
|
from . import rig_lists
|
|
from . import generate
|
|
|
|
|
|
class DATA_PT_rigify_buttons(bpy.types.Panel):
|
|
bl_label = "Rigify Buttons"
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
#bl_options = {'DEFAULT_OPEN'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if not context.armature:
|
|
return False
|
|
#obj = context.object
|
|
#if obj:
|
|
# return (obj.mode in {'POSE', 'OBJECT', 'EDIT'})
|
|
#return False
|
|
return True
|
|
|
|
def draw(self, context):
|
|
C = context
|
|
layout = self.layout
|
|
obj = context.object
|
|
id_store = C.window_manager
|
|
|
|
if obj.mode in {'POSE', 'OBJECT'}:
|
|
layout.operator("pose.rigify_generate", text="Generate")
|
|
elif obj.mode == 'EDIT':
|
|
# Build types list
|
|
collection_name = str(id_store.rigify_collection).replace(" ", "")
|
|
|
|
for i in range(0, len(id_store.rigify_types)):
|
|
id_store.rigify_types.remove(0)
|
|
|
|
for r in rig_lists.rig_list:
|
|
# collection = r.split('.')[0] # UNUSED
|
|
if collection_name == "All":
|
|
a = id_store.rigify_types.add()
|
|
a.name = r
|
|
elif r.startswith(collection_name + '.'):
|
|
a = id_store.rigify_types.add()
|
|
a.name = r
|
|
elif (collection_name == "None") and ("." not in r):
|
|
a = id_store.rigify_types.add()
|
|
a.name = r
|
|
|
|
## Rig collection field
|
|
#row = layout.row()
|
|
#row.prop(id_store, 'rigify_collection', text="Category")
|
|
|
|
# Rig type list
|
|
row = layout.row()
|
|
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
|
|
|
|
|
|
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
|
|
def poll(cls, context):
|
|
if not context.armature:
|
|
return False
|
|
return True
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
obj = context.object
|
|
arm = obj.data
|
|
|
|
# Ensure that the layers exist
|
|
if 0:
|
|
for i in range(1 + len(arm.rigify_layers), 29):
|
|
arm.rigify_layers.add()
|
|
else:
|
|
# Can't add while drawing, just use button
|
|
if len(arm.rigify_layers) < 28:
|
|
layout.operator("pose.rigify_layer_init")
|
|
return
|
|
|
|
# UI
|
|
for i, rigify_layer in enumerate(arm.rigify_layers):
|
|
# note: rigify_layer == arm.rigify_layers[i]
|
|
if (i % 16) == 0:
|
|
col = layout.column()
|
|
if i == 0:
|
|
col.label(text="Top Row:")
|
|
else:
|
|
col.label(text="Bottom Row:")
|
|
if (i % 8) == 0:
|
|
col = layout.column(align=True)
|
|
row = col.row()
|
|
row.prop(arm, "layers", index=i, text="", toggle=True)
|
|
split = row.split(percentage=0.8)
|
|
split.prop(rigify_layer, "name", text="Layer %d" % (i + 1))
|
|
split.prop(rigify_layer, "row", text="")
|
|
|
|
#split.prop(rigify_layer, "column", text="")
|
|
|
|
|
|
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.armature or not context.active_pose_bone:
|
|
return False
|
|
obj = context.object
|
|
if obj:
|
|
return obj.mode == 'POSE'
|
|
return False
|
|
|
|
def draw(self, context):
|
|
C = context
|
|
id_store = C.window_manager
|
|
bone = context.active_pose_bone
|
|
collection_name = str(id_store.rigify_collection).replace(" ", "")
|
|
rig_name = str(context.active_pose_bone.rigify_type).replace(" ", "")
|
|
|
|
layout = self.layout
|
|
|
|
# Build types list
|
|
for i in range(0, len(id_store.rigify_types)):
|
|
id_store.rigify_types.remove(0)
|
|
|
|
for r in rig_lists.rig_list:
|
|
# collection = r.split('.')[0] # UNUSED
|
|
if collection_name == "All":
|
|
a = id_store.rigify_types.add()
|
|
a.name = r
|
|
elif r.startswith(collection_name + '.'):
|
|
a = id_store.rigify_types.add()
|
|
a.name = r
|
|
elif collection_name == "None" and len(r.split('.')) == 1:
|
|
a = id_store.rigify_types.add()
|
|
a.name = r
|
|
|
|
# Rig type field
|
|
row = layout.row()
|
|
row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type:")
|
|
|
|
# Rig type parameters / Rig type non-exist alert
|
|
if rig_name != "":
|
|
try:
|
|
rig = get_rig_type(rig_name)
|
|
rig.Rig
|
|
except (ImportError, AttributeError):
|
|
row = layout.row()
|
|
box = row.box()
|
|
box.label(text="ALERT: type \"%s\" does not exist!" % rig_name)
|
|
else:
|
|
try:
|
|
rig.parameters_ui
|
|
except AttributeError:
|
|
col = layout.column()
|
|
col.label(text="No options")
|
|
else:
|
|
col = layout.column()
|
|
col.label(text="Options:")
|
|
box = layout.box()
|
|
rig.parameters_ui(box, bone.rigify_parameters)
|
|
|
|
|
|
class VIEW3D_PT_tools_rigify_dev(bpy.types.Panel):
|
|
bl_label = "Rigify Dev Tools"
|
|
bl_category = 'Tools'
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'TOOLS'
|
|
|
|
def draw(self, context):
|
|
obj = context.active_object
|
|
if obj is not None:
|
|
if context.mode == 'EDIT_ARMATURE':
|
|
r = self.layout.row()
|
|
r.operator("armature.rigify_encode_metarig", text="Encode Metarig to Python")
|
|
r = self.layout.row()
|
|
r.operator("armature.rigify_encode_metarig_sample", text="Encode Sample to Python")
|
|
|
|
if context.mode == 'EDIT_MESH':
|
|
r = self.layout.row()
|
|
r.operator("mesh.rigify_encode_mesh_widget", text="Encode Mesh Widget to Python")
|
|
|
|
#~ class INFO_MT_armature_metarig_add(bpy.types.Menu):
|
|
#~ bl_idname = "INFO_MT_armature_metarig_add"
|
|
#~ bl_label = "Meta-Rig"
|
|
|
|
#~ def draw(self, context):
|
|
#~ import rigify
|
|
|
|
#~ layout = self.layout
|
|
#~ layout.operator_context = 'INVOKE_REGION_WIN'
|
|
|
|
#~ for submodule_type in rigify.get_submodule_types():
|
|
#~ text = bpy.path.display_name(submodule_type)
|
|
#~ layout.operator("pose.metarig_sample_add", text=text, icon='OUTLINER_OB_ARMATURE').metarig_type = submodule_type
|
|
|
|
|
|
def rigify_report_exception(operator, exception):
|
|
import traceback
|
|
import sys
|
|
import os
|
|
# find the module name where the error happened
|
|
# hint, this is the metarig type!
|
|
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
|
|
fn = traceback.extract_tb(exceptionTraceback)[-1][0]
|
|
fn = os.path.basename(fn)
|
|
fn = os.path.splitext(fn)[0]
|
|
message = []
|
|
if fn.startswith("__"):
|
|
message.append("Incorrect armature...")
|
|
else:
|
|
message.append("Incorrect armature for type '%s'" % fn)
|
|
message.append(exception.message)
|
|
|
|
message.reverse() # XXX - stupid! menu's are upside down!
|
|
|
|
operator.report({'INFO'}, '\n'.join(message))
|
|
|
|
|
|
class LayerInit(bpy.types.Operator):
|
|
"""Initialize armature rigify layers"""
|
|
|
|
bl_idname = "pose.rigify_layer_init"
|
|
bl_label = "Add Rigify Layers"
|
|
bl_options = {'UNDO'}
|
|
|
|
def execute(self, context):
|
|
obj = context.object
|
|
arm = obj.data
|
|
for i in range(1 + len(arm.rigify_layers), 29):
|
|
arm.rigify_layers.add()
|
|
return {'FINISHED'}
|
|
|
|
|
|
class Generate(bpy.types.Operator):
|
|
"""Generates a rig from the active metarig armature"""
|
|
|
|
bl_idname = "pose.rigify_generate"
|
|
bl_label = "Rigify Generate Rig"
|
|
bl_options = {'UNDO'}
|
|
|
|
def execute(self, context):
|
|
import importlib
|
|
importlib.reload(generate)
|
|
|
|
use_global_undo = context.user_preferences.edit.use_global_undo
|
|
context.user_preferences.edit.use_global_undo = False
|
|
try:
|
|
generate.generate_rig(context, context.object)
|
|
except MetarigError as rig_exception:
|
|
rigify_report_exception(self, rig_exception)
|
|
finally:
|
|
context.user_preferences.edit.use_global_undo = use_global_undo
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class Sample(bpy.types.Operator):
|
|
"""Create a sample metarig to be modified before generating """ \
|
|
"""the final rig"""
|
|
|
|
bl_idname = "armature.metarig_sample_add"
|
|
bl_label = "Add a sample metarig for a rig type"
|
|
bl_options = {'UNDO'}
|
|
|
|
metarig_type = StringProperty(
|
|
name="Type",
|
|
description="Name of the rig type to generate a sample of",
|
|
maxlen=128,
|
|
)
|
|
|
|
def execute(self, context):
|
|
if context.mode == 'EDIT_ARMATURE' and self.metarig_type != "":
|
|
use_global_undo = context.user_preferences.edit.use_global_undo
|
|
context.user_preferences.edit.use_global_undo = False
|
|
try:
|
|
rig = get_rig_type(self.metarig_type)
|
|
create_sample = rig.create_sample
|
|
except (ImportError, AttributeError):
|
|
raise Exception("rig type '" + self.metarig_type + "' has no sample.")
|
|
else:
|
|
create_sample(context.active_object)
|
|
finally:
|
|
context.user_preferences.edit.use_global_undo = use_global_undo
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class EncodeMetarig(bpy.types.Operator):
|
|
""" Creates Python code that will generate the selected metarig.
|
|
"""
|
|
bl_idname = "armature.rigify_encode_metarig"
|
|
bl_label = "Rigify Encode Metarig"
|
|
bl_options = {'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(self, context):
|
|
return context.mode == 'EDIT_ARMATURE'
|
|
|
|
def execute(self, context):
|
|
name = "metarig.py"
|
|
|
|
if name in bpy.data.texts:
|
|
text_block = bpy.data.texts[name]
|
|
text_block.clear()
|
|
else:
|
|
text_block = bpy.data.texts.new(name)
|
|
|
|
text = write_metarig(context.active_object, layers=True, func_name="create")
|
|
text_block.write(text)
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class EncodeMetarigSample(bpy.types.Operator):
|
|
""" Creates Python code that will generate the selected metarig
|
|
as a sample.
|
|
"""
|
|
bl_idname = "armature.rigify_encode_metarig_sample"
|
|
bl_label = "Rigify Encode Metarig Sample"
|
|
bl_options = {'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(self, context):
|
|
return context.mode == 'EDIT_ARMATURE'
|
|
|
|
def execute(self, context):
|
|
name = "metarig_sample.py"
|
|
|
|
if name in bpy.data.texts:
|
|
text_block = bpy.data.texts[name]
|
|
text_block.clear()
|
|
else:
|
|
text_block = bpy.data.texts.new(name)
|
|
|
|
text = write_metarig(context.active_object, layers=False, func_name="create_sample")
|
|
text_block.write(text)
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class EncodeWidget(bpy.types.Operator):
|
|
""" Creates Python code that will generate the selected metarig.
|
|
"""
|
|
bl_idname = "mesh.rigify_encode_mesh_widget"
|
|
bl_label = "Rigify Encode Widget"
|
|
bl_options = {'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(self, context):
|
|
return context.mode == 'EDIT_MESH'
|
|
|
|
def execute(self, context):
|
|
name = "widget.py"
|
|
|
|
if name in bpy.data.texts:
|
|
text_block = bpy.data.texts[name]
|
|
text_block.clear()
|
|
else:
|
|
text_block = bpy.data.texts.new(name)
|
|
|
|
text = write_widget(context.active_object)
|
|
text_block.write(text)
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
#menu_func = (lambda self, context: self.layout.menu("INFO_MT_armature_metarig_add", icon='OUTLINER_OB_ARMATURE'))
|
|
|
|
#from bl_ui import space_info # ensure the menu is loaded first
|
|
|
|
def register():
|
|
bpy.utils.register_class(DATA_PT_rigify_layer_names)
|
|
bpy.utils.register_class(DATA_PT_rigify_buttons)
|
|
bpy.utils.register_class(BONE_PT_rigify_buttons)
|
|
bpy.utils.register_class(VIEW3D_PT_tools_rigify_dev)
|
|
bpy.utils.register_class(LayerInit)
|
|
bpy.utils.register_class(Generate)
|
|
bpy.utils.register_class(Sample)
|
|
bpy.utils.register_class(EncodeMetarig)
|
|
bpy.utils.register_class(EncodeMetarigSample)
|
|
bpy.utils.register_class(EncodeWidget)
|
|
#space_info.INFO_MT_armature_add.append(ui.menu_func)
|
|
|
|
|
|
def unregister():
|
|
bpy.utils.unregister_class(DATA_PT_rigify_layer_names)
|
|
bpy.utils.unregister_class(DATA_PT_rigify_buttons)
|
|
bpy.utils.unregister_class(BONE_PT_rigify_buttons)
|
|
bpy.utils.unregister_class(VIEW3D_PT_tools_rigify_dev)
|
|
bpy.utils.unregister_class(LayerInit)
|
|
bpy.utils.unregister_class(Generate)
|
|
bpy.utils.unregister_class(Sample)
|
|
bpy.utils.unregister_class(EncodeMetarig)
|
|
bpy.utils.unregister_class(EncodeMetarigSample)
|
|
bpy.utils.unregister_class(EncodeWidget)
|