Rigify: replace Rename To Deform with a new basic.raw_copy rig.
Add a separate rig aimed at transferring bones from the metarig completely verbatim without the ORG prefix, and remove the hacky copy_chain/super_copy option for renaming ORG to DEF. Share the constraint retargeting feature between super_copy and raw_copy.
This commit is contained in:
parent
86e03ba498
commit
a50e874849
Notes:
blender-bot
2023-02-14 19:05:33 +01:00
Referenced by issue #71447, Rigify: Generate error with basic.copy_chain
|
@ -219,6 +219,9 @@ class BaseGenerator:
|
|||
# different rigs don't collide id's
|
||||
self.rig_id = random_id(16)
|
||||
|
||||
# Table of renamed ORG bones
|
||||
self.org_rename_table = dict()
|
||||
|
||||
|
||||
def disable_auto_parent(self, bone_name):
|
||||
"""Prevent automatically parenting the bone to root if parentless."""
|
||||
|
@ -231,6 +234,20 @@ class BaseGenerator:
|
|||
self.layer_group_priorities[bone_name][i] = priority
|
||||
|
||||
|
||||
def rename_org_bone(self, old_name, new_name):
|
||||
assert self.stage == 'instantiate'
|
||||
assert old_name == self.org_rename_table.get(old_name, None)
|
||||
assert old_name not in self.bone_owners
|
||||
|
||||
bone = self.obj.data.bones[old_name]
|
||||
|
||||
bone.name = new_name
|
||||
new_name = bone.name
|
||||
|
||||
self.org_rename_table[old_name] = new_name
|
||||
return new_name
|
||||
|
||||
|
||||
def __run_object_stage(self, method_name):
|
||||
assert(self.context.active_object == self.obj)
|
||||
assert(self.obj.mode == 'OBJECT')
|
||||
|
@ -431,9 +448,16 @@ class BaseGenerator:
|
|||
assert(self.context.active_object == self.obj)
|
||||
assert(self.obj.mode == 'OBJECT')
|
||||
|
||||
self.stage = 'instantiate'
|
||||
|
||||
# Compute the list of bones
|
||||
bone_list = list_bone_names_depth_first_sorted(self.obj)
|
||||
|
||||
self.org_rename_table = {n: n for n in bone_list}
|
||||
|
||||
# Construct the rig instances
|
||||
for name in list_bone_names_depth_first_sorted(self.obj):
|
||||
self.__create_rigs(name, halt_on_missing)
|
||||
for name in bone_list:
|
||||
self.__create_rigs(self.org_rename_table[name], halt_on_missing)
|
||||
|
||||
# Connect rigs and bones into a tree
|
||||
handled = {}
|
||||
|
|
|
@ -287,7 +287,9 @@ class Generator(base_generate.BaseGenerator):
|
|||
and prop in obj.pose.bones[bone].keys():
|
||||
tar.data_path = tar.data_path[7:]
|
||||
else:
|
||||
tar.data_path = 'pose.bones["%s"]["%s"]' % (make_original_name(bone), prop)
|
||||
org_name = make_original_name(bone)
|
||||
org_name = self.org_rename_table.get(org_name, org_name)
|
||||
tar.data_path = 'pose.bones["%s"]["%s"]' % (org_name, prop)
|
||||
|
||||
|
||||
def __assign_widgets(self):
|
||||
|
|
|
@ -22,7 +22,6 @@ import bpy
|
|||
|
||||
from ..chain_rigs import SimpleChainRig
|
||||
|
||||
from ...utils.layers import DEF_LAYER
|
||||
from ...utils.errors import MetarigError
|
||||
from ...utils.rig import connected_children_names
|
||||
from ...utils.naming import make_derived_name
|
||||
|
@ -42,12 +41,7 @@ class Rig(SimpleChainRig):
|
|||
""" Gather and validate data about the rig.
|
||||
"""
|
||||
self.make_controls = self.params.make_controls
|
||||
|
||||
deform = self.params.make_deforms
|
||||
rename = self.params.rename_to_deform
|
||||
|
||||
self.make_deforms = deform and not rename
|
||||
self.rename_deforms = deform and rename
|
||||
self.make_deforms = self.params.make_deforms
|
||||
|
||||
##############################
|
||||
# Control chain
|
||||
|
@ -98,18 +92,6 @@ class Rig(SimpleChainRig):
|
|||
if self.make_deforms:
|
||||
super().rig_deform_chain()
|
||||
|
||||
##############################
|
||||
# Rename To Deform
|
||||
|
||||
def finalize(self):
|
||||
if self.rename_deform:
|
||||
new_names = [ self.rename_bone(name, make_derived_name(name, 'def')) for name in self.bones.org ]
|
||||
|
||||
for name in new_names:
|
||||
bone = self.get_bone(name).bone
|
||||
bone.use_deform = True
|
||||
bone.layers = DEF_LAYER
|
||||
|
||||
##############################
|
||||
# Parameter UI
|
||||
|
||||
|
@ -121,12 +103,6 @@ class Rig(SimpleChainRig):
|
|||
params.make_controls = bpy.props.BoolProperty(name="Controls", default=True, description="Create control bones for the copy")
|
||||
params.make_deforms = bpy.props.BoolProperty(name="Deform", default=True, description="Create deform bones for the copy")
|
||||
|
||||
params.rename_to_deform = bpy.props.BoolProperty(
|
||||
name = "Rename To Deform",
|
||||
default = False,
|
||||
description = "Rename the original bone itself to use as deform bone (advanced feature)"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def parameters_ui(self, layout, params):
|
||||
""" Create the ui for the rig parameters.
|
||||
|
@ -136,10 +112,6 @@ class Rig(SimpleChainRig):
|
|||
r = layout.row()
|
||||
r.prop(params, "make_deforms")
|
||||
|
||||
if params.make_deforms:
|
||||
r = layout.row()
|
||||
r.prop(params, "rename_to_deform")
|
||||
|
||||
|
||||
def create_sample(obj):
|
||||
""" Create a sample metarig for this rig type.
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
#====================== 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 strip_org, strip_prefix
|
||||
|
||||
from ...base_rig import BaseRig
|
||||
from ...base_generate import SubstitutionRig
|
||||
|
||||
from itertools import repeat
|
||||
|
||||
|
||||
class Rig(SubstitutionRig):
|
||||
""" A raw copy rig, preserving the metarig bone as is, without the ORG prefix. """
|
||||
|
||||
def substitute(self):
|
||||
# Strip the ORG prefix during the rig instantiation phase
|
||||
new_name = strip_org(self.base_bone)
|
||||
new_name = self.generator.rename_org_bone(self.base_bone, new_name)
|
||||
|
||||
return [ self.instantiate_rig(InstanceRig, new_name) ]
|
||||
|
||||
|
||||
class RelinkConstraintsMixin:
|
||||
""" Utilities for constraint relinking. """
|
||||
|
||||
def relink_bone_constraints(self, bone_name):
|
||||
if self.params.relink_constraints:
|
||||
for con in self.get_bone(bone_name).constraints:
|
||||
parts = con.name.split('@')
|
||||
|
||||
if len(parts) > 1:
|
||||
self.relink_constraint(con, parts[1:])
|
||||
|
||||
|
||||
def relink_bone_parent(self, bone_name):
|
||||
if self.params.relink_constraints:
|
||||
self.generator.disable_auto_parent(bone_name)
|
||||
|
||||
parent_spec = self.params.parent_bone
|
||||
if parent_spec:
|
||||
old_parent = self.get_bone_parent(bone_name)
|
||||
new_parent = self.find_relink_target(parent_spec, old_parent or '') or None
|
||||
self.set_bone_parent(bone_name, new_parent)
|
||||
return new_parent
|
||||
|
||||
|
||||
def relink_constraint(self, con, specs):
|
||||
if con.type == 'ARMATURE':
|
||||
if len(specs) == 1:
|
||||
specs = repeat(specs[0])
|
||||
elif len(specs) != len(con.targets):
|
||||
self.report_error("Constraint {} actually has {} targets", con.name, len(con.targets))
|
||||
|
||||
for tgt, spec in zip(con.targets, specs):
|
||||
tgt.subtarget = self.find_relink_target(spec, tgt.subtarget)
|
||||
|
||||
else:
|
||||
if len(specs) > 1:
|
||||
self.report_error("Only the Armature constraint can have multiple '@' targets: {}", con.name)
|
||||
|
||||
con.subtarget = self.find_relink_target(specs[0], con.subtarget)
|
||||
|
||||
|
||||
def find_relink_target(self, spec, old_target):
|
||||
if spec == '':
|
||||
return old_target
|
||||
elif spec == 'CTRL':
|
||||
spec = strip_prefix(old_target)
|
||||
elif spec in {'DEF', 'MCH'}:
|
||||
spec = spec + '-' + strip_prefix(old_target)
|
||||
|
||||
if spec not in self.obj.pose.bones:
|
||||
self.report_error("Cannot find bone '{}' for relinking", spec)
|
||||
|
||||
return spec
|
||||
|
||||
|
||||
@classmethod
|
||||
def add_relink_constraints_params(self, params):
|
||||
params.relink_constraints = bpy.props.BoolProperty(
|
||||
name = "Relink Constraints",
|
||||
default = False,
|
||||
description = "For constraints with names formed like 'base@bonename', use the part after '@' as the new subtarget after all bones are created. Use '@CTRL', '@DEF' or '@MCH' to simply replace the prefix"
|
||||
)
|
||||
|
||||
params.parent_bone = bpy.props.StringProperty(
|
||||
name = "Parent",
|
||||
default = "",
|
||||
description = "Replace the parent with a different bone after all bones are created. Using simply CTRL, DEF or MCH will replace the prefix instead"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def add_relink_constraints_ui(self, layout, params):
|
||||
r = layout.row()
|
||||
r.prop(params, "relink_constraints")
|
||||
|
||||
if params.relink_constraints:
|
||||
r = layout.row()
|
||||
r.prop(params, "parent_bone")
|
||||
|
||||
|
||||
class InstanceRig(BaseRig, RelinkConstraintsMixin):
|
||||
def find_org_bones(self, pose_bone):
|
||||
return pose_bone.name
|
||||
|
||||
def initialize(self):
|
||||
self.relink = self.params.relink_constraints
|
||||
|
||||
def parent_bones(self):
|
||||
self.relink_bone_parent(self.bones.org)
|
||||
|
||||
def rig_bones(self):
|
||||
self.relink_bone_constraints(self.bones.org)
|
||||
|
||||
@classmethod
|
||||
def add_parameters(self, params):
|
||||
self.add_relink_constraints_params(params)
|
||||
|
||||
@classmethod
|
||||
def parameters_ui(self, layout, params):
|
||||
col = layout.column()
|
||||
col.label(text='This rig type does not add the ORG prefix.')
|
||||
col.label(text='Manually add ORG, MCH or DEF as needed.')
|
||||
|
||||
self.add_relink_constraints_ui(layout, params)
|
||||
|
||||
|
||||
add_parameters = InstanceRig.add_parameters
|
||||
parameters_ui = InstanceRig.parameters_ui
|
||||
|
||||
|
||||
def create_sample(obj):
|
||||
""" Create a sample metarig for this rig type.
|
||||
"""
|
||||
# generated by rigify.utils.write_metarig
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
arm = obj.data
|
||||
|
||||
bones = {}
|
||||
|
||||
bone = arm.edit_bones.new('DEF-bone')
|
||||
bone.head[:] = 0.0000, 0.0000, 0.0000
|
||||
bone.tail[:] = 0.0000, 0.0000, 0.2000
|
||||
bone.roll = 0.0000
|
||||
bone.use_connect = False
|
||||
bones['DEF-bone'] = bone.name
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
pbone = obj.pose.bones[bones['DEF-bone']]
|
||||
pbone.rigify_type = 'basic.raw_copy'
|
||||
pbone.lock_location = (False, False, False)
|
||||
pbone.lock_rotation = (False, False, False)
|
||||
pbone.lock_rotation_w = False
|
||||
pbone.lock_scale = (False, False, False)
|
||||
pbone.rotation_mode = 'QUATERNION'
|
||||
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
for bone in arm.edit_bones:
|
||||
bone.select = False
|
||||
bone.select_head = False
|
||||
bone.select_tail = False
|
||||
for b in bones:
|
||||
bone = arm.edit_bones[bones[b]]
|
||||
bone.select = True
|
||||
bone.select_head = True
|
||||
bone.select_tail = True
|
||||
arm.edit_bones.active = bone
|
||||
|
||||
return bones
|
|
@ -22,14 +22,13 @@ import bpy
|
|||
|
||||
from ...base_rig import BaseRig
|
||||
|
||||
from ...utils.layers import DEF_LAYER
|
||||
from ...utils.naming import strip_org, make_deformer_name
|
||||
from ...utils.widgets_basic import create_bone_widget, create_circle_widget
|
||||
|
||||
from itertools import repeat
|
||||
from .raw_copy import RelinkConstraintsMixin
|
||||
|
||||
|
||||
class Rig(BaseRig):
|
||||
class Rig(BaseRig, RelinkConstraintsMixin):
|
||||
""" A "copy" rig. All it does is duplicate the original bone and
|
||||
constrain it.
|
||||
This is a control and deformation rig.
|
||||
|
@ -46,14 +45,7 @@ class Rig(BaseRig):
|
|||
|
||||
self.make_control = self.params.make_control
|
||||
self.make_widget = self.params.make_widget
|
||||
|
||||
deform = self.params.make_deform
|
||||
rename = self.params.rename_to_deform
|
||||
|
||||
self.make_deform = deform and not rename
|
||||
self.rename_deform = deform and rename
|
||||
|
||||
self.relink = self.params.relink_constraints
|
||||
self.make_deform = self.params.make_deform
|
||||
|
||||
|
||||
def generate_bones(self):
|
||||
|
@ -74,17 +66,10 @@ class Rig(BaseRig):
|
|||
if self.make_deform:
|
||||
self.set_bone_parent(bones.deform, bones.org, use_connect=False)
|
||||
|
||||
if self.relink:
|
||||
self.generator.disable_auto_parent(bones.org)
|
||||
new_parent = self.relink_bone_parent(bones.org)
|
||||
|
||||
parent_spec = self.params.parent_bone
|
||||
if parent_spec:
|
||||
old_parent = self.get_bone_parent(bones.org)
|
||||
new_parent = self.find_relink_target(parent_spec, old_parent or '') or None
|
||||
self.set_bone_parent(bones.org, new_parent)
|
||||
|
||||
if self.make_control:
|
||||
self.set_bone_parent(bones.ctrl, new_parent)
|
||||
if self.make_control and new_parent:
|
||||
self.set_bone_parent(bones.ctrl, new_parent)
|
||||
|
||||
|
||||
def configure_bones(self):
|
||||
|
@ -97,54 +82,12 @@ class Rig(BaseRig):
|
|||
def rig_bones(self):
|
||||
bones = self.bones
|
||||
|
||||
if self.relink:
|
||||
for con in self.get_bone(bones.org).constraints:
|
||||
parts = con.name.split('@')
|
||||
|
||||
if len(parts) > 1:
|
||||
self.relink_constraint(con, parts[1:])
|
||||
self.relink_bone_constraints(bones.org)
|
||||
|
||||
if self.make_control:
|
||||
# Constrain the original bone.
|
||||
self.make_constraint(bones.org, 'COPY_TRANSFORMS', bones.ctrl, insert_index=0)
|
||||
|
||||
def relink_constraint(self, con, specs):
|
||||
if con.type == 'ARMATURE':
|
||||
if len(specs) == 1:
|
||||
specs = repeat(specs[0])
|
||||
elif len(specs) != len(con.specs):
|
||||
self.report_error("Constraint {} actually has {} targets", con.name, len(con.targets))
|
||||
|
||||
for tgt, spec in zip(con.targets, specs):
|
||||
tgt.subtarget = self.find_relink_target(spec, tgt.subtarget)
|
||||
|
||||
else:
|
||||
if len(specs) > 1:
|
||||
self.report_error("Only the Armature constraint can have multiple '@' targets: {}", con.name)
|
||||
|
||||
con.subtarget = self.find_relink_target(specs[0], con.subtarget)
|
||||
|
||||
def find_relink_target(self, spec, old_target):
|
||||
if spec == '':
|
||||
return old_target
|
||||
elif spec in {'DEF', 'MCH'}:
|
||||
spec = spec + '-' + strip_org(old_target)
|
||||
|
||||
if spec not in self.obj.pose.bones:
|
||||
# Hack: allow referring to copy rigs using Rename To Deform as DEF
|
||||
if old_target.startswith('ORG-') and spec == make_deformer_name(strip_org(old_target)):
|
||||
from . import copy_chain
|
||||
|
||||
owner = self.generator.bone_owners.get(old_target)
|
||||
|
||||
if ((isinstance(owner, Rig) and owner.rename_deform) or
|
||||
(isinstance(owner, copy_chain.Rig) and owner.rename_deforms)):
|
||||
return old_target
|
||||
|
||||
self.report_error("Cannot find bone '{}' for relinking", spec)
|
||||
|
||||
return spec
|
||||
|
||||
|
||||
def generate_widgets(self):
|
||||
bones = self.bones
|
||||
|
@ -157,15 +100,6 @@ class Rig(BaseRig):
|
|||
create_bone_widget(self.obj, bones.ctrl)
|
||||
|
||||
|
||||
def finalize(self):
|
||||
if self.rename_deform:
|
||||
new_name = self.rename_bone(self.bones.org, make_deformer_name(self.org_name))
|
||||
|
||||
bone = self.get_bone(new_name).bone
|
||||
bone.use_deform = True
|
||||
bone.layers = DEF_LAYER
|
||||
|
||||
|
||||
@classmethod
|
||||
def add_parameters(self, params):
|
||||
""" Add the parameters of this rig type to the
|
||||
|
@ -189,23 +123,7 @@ class Rig(BaseRig):
|
|||
description = "Create a deform bone for the copy"
|
||||
)
|
||||
|
||||
params.rename_to_deform = bpy.props.BoolProperty(
|
||||
name = "Rename To Deform",
|
||||
default = False,
|
||||
description = "Rename the original bone itself to use as deform bone (advanced feature)"
|
||||
)
|
||||
|
||||
params.relink_constraints = bpy.props.BoolProperty(
|
||||
name = "Relink Constraints",
|
||||
default = False,
|
||||
description = "For constraints with names formed like 'base@bonename', use the part after '@' as the new subtarget after all bones are created. Use '@DEF' or '@MCH' to simply prepend the prefix"
|
||||
)
|
||||
|
||||
params.parent_bone = bpy.props.StringProperty(
|
||||
name = "Parent",
|
||||
default = "",
|
||||
description = "Replace the parent with a different bone after all bones are created. Using simply DEF or MCH will prepend the prefix instead"
|
||||
)
|
||||
self.add_relink_constraints_params(params)
|
||||
|
||||
|
||||
@classmethod
|
||||
|
@ -220,16 +138,7 @@ class Rig(BaseRig):
|
|||
r = layout.row()
|
||||
r.prop(params, "make_deform")
|
||||
|
||||
if params.make_deform:
|
||||
r = layout.row()
|
||||
r.prop(params, "rename_to_deform")
|
||||
|
||||
r = layout.row()
|
||||
r.prop(params, "relink_constraints")
|
||||
|
||||
if params.relink_constraints:
|
||||
r = layout.row()
|
||||
r.prop(params, "parent_bone")
|
||||
self.add_relink_constraints_ui(layout, params)
|
||||
|
||||
|
||||
def create_sample(obj):
|
||||
|
|
Loading…
Reference in New Issue