diff options
Diffstat (limited to 'rigify/rigs/skin/skin_rigs.py')
-rw-r--r-- | rigify/rigs/skin/skin_rigs.py | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/rigify/rigs/skin/skin_rigs.py b/rigify/rigs/skin/skin_rigs.py new file mode 100644 index 00000000..a4bc361e --- /dev/null +++ b/rigify/rigs/skin/skin_rigs.py @@ -0,0 +1,241 @@ +# ====================== 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 ...utils.naming import make_derived_name +from ...utils.misc import force_lazy, LazyRef + +from ...base_rig import BaseRig, stage + +from .skin_parents import ControlBoneParentOrg + + +class BaseSkinRig(BaseRig): + """ + Base type for all rigs involved in the skin system. + This includes chain rigs and the parent provider rigs. + """ + + def initialize(self): + self.rig_parent_bone = self.get_bone_parent(self.base_bone) + + ########################## + # Utilities + + def get_parent_skin_rig(self): + """Find the closest BaseSkinRig parent.""" + parent = self.rigify_parent + + while parent: + if isinstance(parent, BaseSkinRig): + return parent + parent = parent.rigify_parent + + return None + + def get_all_parent_skin_rigs(self): + """Get a list of all BaseSkinRig parents, starting with this rig.""" + items = [] + current = self + while current: + items.append(current) + current = current.get_parent_skin_rig() + return items + + def get_child_chain_parent_next(self, rig): + """ + Retrieves the parent bone for the child chain rig + as determined by the parent skin rig. + """ + if isinstance(self.rigify_parent, BaseSkinRig): + return self.rigify_parent.get_child_chain_parent(rig, self.rig_parent_bone) + else: + return self.rig_parent_bone + + def build_control_node_parent_next(self, node): + """ + Retrieves the parent mechanism generator for the child control node + as determined by the parent skin rig. + """ + if isinstance(self.rigify_parent, BaseSkinRig): + return self.rigify_parent.build_control_node_parent(node, self.rig_parent_bone) + else: + return ControlBoneParentOrg(self.rig_parent_bone) + + ########################## + # Methods to override + + def get_child_chain_parent(self, rig, parent_bone): + """ + Returns the (lazy) parent bone to use for the given child chain rig. + The parent_bone argument specifies the actual parent bone from caller. + """ + return parent_bone + + def build_control_node_parent(self, node, parent_bone): + """ + Returns the parent mechanism generator for the child control node. + The parent_bone argument specifies the actual parent bone from caller. + Called during the initialize stage. + """ + return ControlBoneParentOrg(self.get_child_chain_parent(node.rig, parent_bone)) + + def build_own_control_node_parent(self, node): + """ + Returns the parent mechanism generator for nodes directly owned by this rig. + Called during the initialize stage. + """ + return self.build_control_node_parent_next(node) + + def extend_control_node_parent(self, parent, node): + """ + First callback pass of adjustments to the parent mechanism generator for the given node. + Called for all BaseSkinRig parents in parent to child order during the initialize stage. + """ + return parent + + def extend_control_node_parent_post(self, parent, node): + """ + Second callback pass of adjustments to the parent mechanism generator for the given node. + Called for all BaseSkinRig parents in child to parent order during the initialize stage. + """ + return parent + + def extend_control_node_rig(self, node): + """ + A callback pass for adding constraints directly to the generated control. + Called for all BaseSkinRig parents in parent to child order during the rig stage. + """ + pass + + +def get_bone_quaternion(obj, bone): + return obj.pose.bones[bone].bone.matrix_local.to_quaternion() + + +class BaseSkinChainRig(BaseSkinRig): + """ + Base type for all skin rigs that can own control nodes, rather than + only modifying nodes of their children or other rigs. + """ + + chain_priority = 0 + + def initialize(self): + super().initialize() + + if type(self).chain_priority is None: + self.chain_priority = self.params.skin_chain_priority + + def parent_bones(self): + self.rig_parent_bone = force_lazy(self.get_child_chain_parent_next(self)) + + def get_final_control_node_rotation(self, node): + """Returns the orientation to use for the given control node owned by this rig.""" + return self.get_control_node_rotation(node) + + ########################## + # Methods to override + + def get_control_node_rotation(self, node): + """ + Returns the rig-specific orientation to use for the given control node of this rig, + if not overridden by the Orientation Bone option. + """ + return get_bone_quaternion(self.obj, self.base_bone) + + def get_control_node_layers(self, node): + """Returns the armature layers to use for the given control node owned by this rig.""" + return self.get_bone(self.base_bone).bone.layers + + def make_control_node_widget(self, node): + """Called to generate the widget for nodes with ControlNodeIcon.CUSTOM.""" + raise NotImplementedError() + + ########################## + # UI + + @classmethod + def add_parameters(self, params): + params.skin_chain_priority = bpy.props.IntProperty( + name='Chain Priority', + min=-10, max=10, default=0, + description='When merging controls, chains with higher priority always win' + ) + + @classmethod + def parameters_ui(self, layout, params): + if self.chain_priority is None: + layout.prop(params, "skin_chain_priority") + + +class BaseSkinChainRigWithRotationOption(BaseSkinChainRig): + """ + Skin chain rig with an option to override the orientation to use + for controls via specifying an arbitrary template bone. + """ + + use_skin_control_orientation_bone = True + + def get_final_control_node_rotation(self, node): + bone_name = self.params.skin_control_orientation_bone + + if bone_name and self.use_skin_control_orientation_bone: + # Retrieve the orientation from the specified ORG bone + try: + org_name = make_derived_name(bone_name, 'org') + + if org_name not in self.obj.pose.bones: + org_name = bone_name + + return get_bone_quaternion(self.obj, org_name) + + except KeyError: + self.raise_error('Could not find orientation bone {}', bone_name) + + else: + # Use the rig-specific orientation + return self.get_control_node_rotation(node) + + @classmethod + def add_parameters(self, params): + params.skin_control_orientation_bone = bpy.props.StringProperty( + name="Orientation Bone", + description="If set, control orientation is taken from the specified bone", + ) + + super().add_parameters(params) + + @classmethod + def parameters_ui(self, layout, params): + if self.use_skin_control_orientation_bone: + from rigify.operators.copy_mirror_parameters import make_copy_parameter_button + + row = layout.row() + row.prop_search(params, "skin_control_orientation_bone", + bpy.context.active_object.pose, "bones", text="Orientation") + + make_copy_parameter_button( + row, "skin_control_orientation_bone", mirror_bone=True, + base_class=BaseSkinChainRigWithRotationOption + ) + + super().parameters_ui(layout, params) |