Merge branch 'master' into xr-actions-D9124

This commit is contained in:
Peter Kim 2020-11-26 13:31:30 +09:00
commit 27c29fbc18
23 changed files with 1809 additions and 1205 deletions

View File

@ -503,7 +503,8 @@ def automap(target_object=None, target_slot=None, tex_size=1, bg_exception=False
if mat_props.automap:
tob = bpy.data.objects[target_object]
# only automap mesh models
if tob.type == 'MESH':
if tob.type == 'MESH' and len(tob.data.polygons)>0:
#check polycount for a rare case where no polys are in editmesh
actob = bpy.context.active_object
bpy.context.view_layer.objects.active = tob

View File

@ -45,6 +45,7 @@ import ntpath
import re
import shutil
import pathlib
import stat
import time
@ -59,7 +60,7 @@ from bpy.props import (
StringProperty,
PointerProperty,
)
only_one_time = True
global_exchange_folder = ''
foundExchangeFolder = True
saved_exchange_folder = ''
@ -72,6 +73,11 @@ def every_3_seconds():
global global_exchange_folder
global liveUpdate
global mTime
global only_one_time
if(only_one_time):
only_one_time = False
folders.loadExchangeFolder()
try:
@ -87,9 +93,6 @@ def every_3_seconds():
tex.updatetextures(objekti)
mTime = os.path.getmtime(Export_folder)
if (os.path.normpath(global_exchange_folder) != os.path.normpath(coat3D.exchangeFolder) and coat3D.exchangeFolder != ''):
folders.updateExchangeFile(coat3D.exchangeFolder)
except:
pass
@ -222,7 +225,7 @@ class SCENE_OT_getback(bpy.types.Operator):
bl_label = "Export your custom property"
bl_description = "Export your custom property"
bl_options = {'UNDO'}
def invoke(self, context, event):
global global_exchange_folder
@ -255,6 +258,32 @@ class SCENE_OT_getback(bpy.types.Operator):
return {'FINISHED'}
class SCENE_OT_savenew(bpy.types.Operator):
bl_idname = "save_new_export.pilgway_3d_coat"
bl_label = "Export your custom property"
bl_description = "Export your custom property"
bl_options = {'UNDO'}
def invoke(self, context, event):
coat3D = bpy.context.scene.coat3D
platform = os.sys.platform
if(platform == 'win32' or platform == 'darwin'):
exchangeFile = os.path.expanduser("~") + os.sep + 'Documents' + os.sep + '3DC2Blender' + os.sep + 'Exchange_folder.txt'
else:
exchangeFile = os.path.expanduser("~") + os.sep + '3DC2Blender' + os.sep + 'Exchange_folder.txt'
if(os.path.isfile(exchangeFile)):
folderPath = ''
if(os.path.isfile(exchangeFile)):
file = open(exchangeFile, "w")
file.write("%s"%(coat3D.exchangeFolder))
file.close()
return {'FINISHED'}
class SCENE_OT_folder(bpy.types.Operator):
bl_idname = "update_exchange_folder.pilgway_3d_coat"
bl_label = "Export your custom property"
@ -266,8 +295,7 @@ class SCENE_OT_folder(bpy.types.Operator):
coat3D = bpy.context.scene.coat3D
if(os.path.isdir(coat3D.exchangeFolder)):
foundExchangeFolder= True
else:
foundExchangeFolder = False
folders.updateExchangeFile(coat3D.exchangeFolder)
return {'FINISHED'}
@ -1497,6 +1525,7 @@ class SCENE_PT_Settings_Folders(ObjectButtonsPanel, bpy.types.Panel):
col = flow.column()
col.prop(coat3D, "exchangeFolder", text="Exchange folder")
col.operator("save_new_export.pilgway_3d_coat", text="Save new Exchange folder")
col = flow.column()
col.prop(coat3D, "defaultfolder", text="Object/Texture folder")
@ -1918,6 +1947,7 @@ classes = (
SCENE_OT_opencoat,
SCENE_OT_export,
SCENE_OT_getback,
SCENE_OT_savenew,
SCENE_OT_delete_material_nodes,
SCENE_OT_delete_object_nodes,
SCENE_OT_delete_collection_nodes,

View File

@ -14,26 +14,30 @@ def InitFolders():
# 1. #################################################################
if(platform == 'win32' or platform == 'darwin'):
exchangeFile = os.path.expanduser("~") + os.sep + 'Documents' + os.sep + '3DC2Blender' + os.sep + 'Exchange_folder.txt'
DC2Folder = os.path.expanduser("~") + os.sep + 'Documents' + os.sep + '3DC2Blender'
else:
exchangeFile = os.path.expanduser("~") + os.sep + '3DC2Blender' + os.sep + 'Exchange_folder.txt'
if(os.path.isfile(exchangeFile)):
folderPath = ''
DC2Folder = os.path.expanduser("~") + os.sep + '3DC2Blender'
exchangeFile = DC2Folder + os.sep + 'Exchange_folder.txt'
folderPathh = open(exchangeFile)
for line in folderPathh:
folderPath = line
if(not os.path.isdir(DC2Folder)):
os.mkdir(DC2Folder)
if(not os.path.isfile(exchangeFile)):
file = open(exchangeFile, 'w')
file.close()
else:
savedExchangePath = ''
folderPath = open(exchangeFile)
for line in folderPath:
savedExchangePath = line
break
folderPathh.close()
folderPath.close()
if(os.path.isdir(os.path.abspath(folderPath)) and folderPath.rfind('Exchange') >= 0):
coat3D.exchangeFolder = folderPath
return True, coat3D.exchangeFolder
else:
try:
os.makedirs(os.path.dirname(exchangeFile))
except:
pass
coat3D.exchangeFolder = savedExchangePath
return True, coat3D.exchangeFolder
# 2. #################################################################
@ -50,7 +54,7 @@ def InitFolders():
Blender_folder = ("%s%sBlender"%(exchangeFolder,os.sep))
if(not(os.path.isdir(Blender_folder))):
os.makedirs(Blender_folder)
os.makedirs(Blender_folder, mode = 0o666)
Blender_folder1 = os.path.join(Blender_folder,"run.txt")
file = open(Blender_folder1, "w")
file.close()
@ -90,6 +94,35 @@ def updateExchangeFile(newPath):
file.write("%s"%(newPath))
file.close()
def loadExchangeFolder():
platform = os.sys.platform
coat3D = bpy.context.scene.coat3D
if(platform == 'win32' or platform == 'darwin'):
DC2Folder = os.path.expanduser("~") + os.sep + 'Documents' + os.sep + '3DC2Blender'
else:
DC2Folder = os.path.expanduser("~") + os.sep + '3DC2Blender'
exchangeFile = DC2Folder + os.sep + 'Exchange_folder.txt'
if(not os.path.isdir(DC2Folder)):
os.mkdir(DC2Folder)
if(not os.path.isfile(exchangeFile)):
file = open(exchangeFile, 'w')
file.close()
else:
savedExchangePath = ''
folderPath = open(exchangeFile)
for line in folderPath:
savedExchangePath = line
break
folderPath.close()
coat3D.exchangeFolder = savedExchangePath
def set_working_folders():
platform = os.sys.platform
@ -101,13 +134,13 @@ def set_working_folders():
else:
folder_objects = os.path.expanduser("~") + os.sep + 'Documents' + os.sep + '3DC2Blender' + os.sep + 'ApplinkObjects'
if(not(os.path.isdir(folder_objects))):
os.makedirs(folder_objects)
os.makedirs(folder_objects, mode = 0o666)
else:
if (coat3D.defaultfolder != '' and os.path.isdir(coat3D.defaultfolder)):
return coat3D.defaultfolder
else:
folder_objects = os.path.expanduser("~") + os.sep + '3DC2Blender' + os.sep + 'ApplinkObjects'
if(not(os.path.isdir(folder_objects))):
os.makedirs(folder_objects)
os.makedirs(folder_objects, mode = 0o666)
return folder_objects

View File

@ -21,7 +21,7 @@
bl_info = {
"name": "FBX format",
"author": "Campbell Barton, Bastien Montagne, Jens Restemeier",
"version": (4, 21, 3),
"version": (4, 22, 0),
"blender": (2, 90, 0),
"location": "File > Import-Export",
"description": "FBX IO meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions",
@ -426,6 +426,13 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
"(Blender uses FBX scale to detect units on import, "
"but many other applications do not handle the same way)",
)
use_space_transform: BoolProperty(
name="Use Space Transform",
description="Apply global space transform to the object rotations. When disabled "
"only the axis space is written to the file and all object transforms are left as-is",
default=True,
)
bake_space_transform: BoolProperty(
name="Apply Transform",
description="Bake space transform into object data, avoids getting unwanted rotations to objects when "
@ -623,7 +630,8 @@ class ExportFBX(bpy.types.Operator, ExportHelper):
global_matrix = (axis_conversion(to_forward=self.axis_forward,
to_up=self.axis_up,
).to_4x4())
).to_4x4()
if self.use_space_transform else Matrix())
keywords = self.as_keywords(ignore=("check_existing",
"filter_glob",
@ -727,6 +735,7 @@ class FBX_PT_export_transform(bpy.types.Panel):
layout.prop(operator, "axis_up")
layout.prop(operator, "apply_unit_scale")
layout.prop(operator, "use_space_transform")
row = layout.row()
row.prop(operator, "bake_space_transform")
row.label(text="", icon='ERROR')

View File

@ -1079,6 +1079,12 @@ def blen_read_geom_layer_color(fbx_obj, mesh):
# Always init our new layers with full white opaque color.
color_lay = mesh.vertex_colors.new(name=fbx_layer_name, do_init=False)
if color_lay is None:
print("Failed to add {%r %r} vertex color layer to %r (probably too many of them?)"
"" % (layer_id, fbx_layer_name, mesh.name))
continue
blen_data = color_lay.data
# some valid files omit this data

View File

@ -15,7 +15,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
"version": (1, 5, 1),
"version": (1, 5, 2),
'blender': (2, 91, 0),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',

View File

@ -275,12 +275,22 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
for uv_i in range(num_uvs):
name = 'UVMap' if uv_i == 0 else 'UVMap.%03d' % uv_i
layer = mesh.uv_layers.new(name=name)
if layer is None:
print("WARNING: UV map is ignored because the maximum number of UV layers has been reached.")
break
layer.data.foreach_set('uv', squish(loop_uvs[uv_i]))
for col_i in range(num_cols):
name = 'Col' if col_i == 0 else 'Col.%03d' % col_i
layer = mesh.vertex_colors.new(name=name)
if layer is None:
print("WARNING: Vertex colors are ignored because the maximum number of vertex color layers has been "
"reached.")
break
layer.data.foreach_set('color', squish(loop_cols[col_i]))
# Skinning

View File

@ -359,6 +359,10 @@ class BaseGenerator:
self.__run_object_stage('configure_bones')
def invoke_preapply_bones(self):
self.__run_object_stage('preapply_bones')
def invoke_apply_bones(self):
self.__run_edit_stage('apply_bones')

View File

@ -119,6 +119,13 @@ class GenerateCallbackHost(BaseStagedClass, define_stages=True):
"""
pass
def preapply_bones(self):
"""
Read bone matrices for applying to edit mode.
Called in Object mode. May not do Edit mode operations.
"""
pass
def apply_bones(self):
"""
Can be used to apply some constraints to rest pose, and for final parenting.

View File

@ -30,6 +30,7 @@ from .utils.widgets import WGT_PREFIX
from .utils.widgets_special import create_root_widget
from .utils.misc import gamma_correct, select_object
from .utils.collections import ensure_widget_collection, list_layer_collections, filter_layer_collections_by_object
from .utils.rig import get_rigify_type
from . import base_generate
from . import rig_ui_template
@ -198,9 +199,12 @@ class Generator(base_generate.BaseGenerator):
# Add the ORG_PREFIX to the original bones.
for i in range(0, len(original_bones)):
new_name = make_original_name(original_bones[i])
obj.data.bones[original_bones[i]].name = new_name
original_bones[i] = new_name
bone = obj.pose.bones[original_bones[i]]
# This rig type is special in that it preserves the name of the bone.
if get_rigify_type(bone) != 'basic.raw_copy':
bone.name = make_original_name(original_bones[i])
original_bones[i] = bone.name
self.original_bones = original_bones
@ -428,6 +432,13 @@ class Generator(base_generate.BaseGenerator):
t.tick("Configure bones: ")
#------------------------------------------
bpy.ops.object.mode_set(mode='OBJECT')
self.invoke_preapply_bones()
t.tick("Preapply bones: ")
#------------------------------------------
bpy.ops.object.mode_set(mode='EDIT')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -125,6 +125,11 @@ def get_pose_matrix_in_other_space(mat, pose_bone):
return pose_bone.id_data.convert_space(matrix=mat, pose_bone=pose_bone, from_space='POSE', to_space='LOCAL')
def convert_pose_matrix_via_rest_delta(mat, from_bone, to_bone):
"""Convert pose of one bone to another bone, preserving the rest pose difference between them."""
return mat @ from_bone.bone.matrix_local.inverted() @ to_bone.bone.matrix_local
def get_local_pose_matrix(pose_bone):
""" Returns the local transform matrix of the given pose bone.
"""
@ -193,39 +198,41 @@ def match_pose_scale(pose_bone, target_bone):
## IK/FK snapping functions ##
##############################
def correct_rotation(view_layer, bone_ik, target_matrix):
def correct_rotation(view_layer, bone_ik, target_matrix, *, ctrl_ik=None):
""" Corrects the ik rotation in ik2fk snapping functions
"""
axis = target_matrix.to_3x3().col[1].normalized()
ctrl_ik = ctrl_ik or bone_ik
def distance(angle):
# Rotate the bone and return the actual angle between bones
bone_ik.rotation_euler[1] = angle
ctrl_ik.rotation_euler[1] = angle
view_layer.update()
return -(bone_ik.vector.normalized().dot(axis))
if bone_ik.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
bone_ik.rotation_mode = 'ZXY'
if ctrl_ik.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
ctrl_ik.rotation_mode = 'ZXY'
start_angle = bone_ik.rotation_euler[1]
start_angle = ctrl_ik.rotation_euler[1]
alfarange = find_min_range(distance, start_angle)
alfamin = ternarySearch(distance, alfarange[0], alfarange[1], pi / 180)
bone_ik.rotation_euler[1] = alfamin
ctrl_ik.rotation_euler[1] = alfamin
view_layer.update()
def correct_scale(view_layer, bone_ik, target_matrix):
def correct_scale(view_layer, bone_ik, target_matrix, *, ctrl_ik=None):
""" Correct the scale of the base IK bone. """
input_scale = target_matrix.to_scale()
ctrl_ik = ctrl_ik or bone_ik
for i in range(3):
cur_scale = bone_ik.matrix.to_scale()
bone_ik.scale = [
ctrl_ik.scale = [
v * i / c for v, i, c in zip(bone_ik.scale, input_scale, cur_scale)
]
@ -456,8 +463,8 @@ def fk2ik_leg(obj, fk, ik):
view_layer.update()
# Foot position
mat = mfoot.bone.matrix_local.inverted() @ foot.bone.matrix_local
footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot) @ mat
footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot)
footmat = convert_pose_matrix_via_rest_delta(footmat, mfoot, foot)
set_pose_rotation(foot, footmat)
set_pose_scale(foot, footmat)
view_layer.update()
@ -475,8 +482,8 @@ def fk2ik_leg(obj, fk, ik):
view_layer.update()
# Foot position
mat = mfoot.bone.matrix_local.inverted() @ foot.bone.matrix_local
footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot) @ mat
footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot)
footmat = convert_pose_matrix_via_rest_delta(footmat, mfoot, foot)
set_pose_rotation(foot, footmat)
set_pose_scale(foot, footmat)
view_layer.update()
@ -516,8 +523,8 @@ def ik2fk_leg(obj, fk, ik):
view_layer.update()
# Foot position
mat = mfooti.bone.matrix_local.inverted() @ footi.bone.matrix_local
footmat = get_pose_matrix_in_other_space(foot.matrix, footi) @ mat
footmat = get_pose_matrix_in_other_space(foot.matrix, footi)
footmat = convert_pose_matrix_via_rest_delta(footmat, mfooti, footi)
set_pose_translation(footi, footmat)
set_pose_rotation(footi, footmat)
set_pose_scale(footi, footmat)
@ -544,8 +551,8 @@ def ik2fk_leg(obj, fk, ik):
view_layer.update()
# Foot position
mat = mfooti.bone.matrix_local.inverted() @ footi.bone.matrix_local
footmat = get_pose_matrix_in_other_space(mfoot.matrix, footi) @ mat
footmat = get_pose_matrix_in_other_space(mfoot.matrix, footi)
footmat = convert_pose_matrix_via_rest_delta(footmat, mfooti, footi)
set_pose_translation(footi, footmat)
set_pose_rotation(footi, footmat)
set_pose_scale(footi, footmat)

View File

@ -27,6 +27,8 @@ from ...base_generate import SubstitutionRig
from itertools import repeat
'''
Due to T80764, bone name handling for 'limbs.raw_copy' was hard-coded in generate.py
class Rig(SubstitutionRig):
""" A raw copy rig, preserving the metarig bone as is, without the ORG prefix. """
@ -37,7 +39,7 @@ class Rig(SubstitutionRig):
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. """
@ -119,8 +121,10 @@ class RelinkConstraintsMixin:
r = layout.row()
r.prop(params, "parent_bone")
layout.label(text="Constraint names have special meanings.", icon='ERROR')
class InstanceRig(BaseRig, RelinkConstraintsMixin):
class Rig(BaseRig, RelinkConstraintsMixin):
def find_org_bones(self, pose_bone):
return pose_bone.name
@ -146,8 +150,8 @@ class InstanceRig(BaseRig, RelinkConstraintsMixin):
self.add_relink_constraints_ui(layout, params)
add_parameters = InstanceRig.add_parameters
parameters_ui = InstanceRig.parameters_ui
#add_parameters = InstanceRig.add_parameters
#parameters_ui = InstanceRig.parameters_ui
def create_sample(obj):

View File

@ -37,10 +37,9 @@ from .limb_rigs import BaseLimbRig
class Rig(BaseLimbRig):
"""Human arm rig."""
def initialize(self):
if len(self.bones.org.main) != 3:
self.raise_error("Input to rig type must be a chain of 3 bones.")
min_valid_orgs = max_valid_orgs = 3
def initialize(self):
super().initialize()
self.make_wrist_pivot = self.params.make_ik_wrist_pivot

View File

@ -0,0 +1,288 @@
#====================== 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.bones import align_bone_roll, put_bone, copy_bone_position, flip_bone
from ...utils.naming import make_derived_name
from ...utils.misc import map_list
from itertools import count
from ...base_rig import stage
from .limb_rigs import BaseLimbRig
from .paw import Rig as pawRig
class Rig(pawRig):
"""Front paw rig with special IK automation."""
####################################################
# EXTRA BONES
#
# mch:
# ik2_chain[2]
# Second IK system (pre-driving heel)
# heel_track
# Bone tracking IK2 to rotate heel
# heel_parent
# Parent of the heel control
#
####################################################
####################################################
# IK controls
def get_middle_ik_controls(self):
return [self.bones.ctrl.heel]
def get_ik_fk_position_chains(self):
ik_chain, fk_chain = super().get_ik_fk_position_chains()
if not self.use_heel2:
return [*ik_chain, ik_chain[-1]], [*fk_chain, fk_chain[-1]]
return ik_chain, fk_chain
def get_extra_ik_controls(self):
extra = [self.bones.ctrl.heel2] if self.use_heel2 else []
return BaseLimbRig.get_extra_ik_controls(self) + extra
def get_ik_pole_parents(self):
return [(self.get_ik2_target_bone(), self.bones.ctrl.ik)]
####################################################
# Second IK system (pre-driving heel)
use_mch_ik_base = True
def get_ik2_target_bone(self):
return self.bones.mch.ik2_target if self.use_heel2 else self.bones.mch.toe_socket
@stage.generate_bones
def make_ik2_mch_chain(self):
orgs = self.bones.org.main
chain = map_list(self.make_ik2_mch_bone, count(0), orgs[0:2])
self.bones.mch.ik2_chain = chain
if self.use_heel2:
self.bones.mch.ik2_target = self.make_ik2_mch_target_bone(orgs)
# Connect the chain end to the target
self.get_bone(chain[1]).tail = self.get_bone(orgs[2]).tail
align_bone_roll(self.obj, chain[1], orgs[1])
def make_ik2_mch_target_bone(self, orgs):
return self.copy_bone(orgs[3], make_derived_name(orgs[0], 'mch', '_ik2_target'), scale=1/2)
def make_ik2_mch_bone(self, i, org):
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik2'))
@stage.parent_bones
def parent_ik2_mch_chain(self):
mch = self.bones.mch
if self.use_heel2:
self.set_bone_parent(mch.ik2_target, self.bones.ctrl.heel2)
self.set_bone_parent(mch.ik2_chain[0], self.bones.ctrl.ik_base, inherit_scale='AVERAGE')
self.parent_bone_chain(mch.ik2_chain, use_connect=True)
@stage.configure_bones
def configure_ik2_mch_chain(self):
for i, mch in enumerate(self.bones.mch.ik2_chain):
self.configure_ik2_mch_bone(i, mch)
def configure_ik2_mch_bone(self, i, mch):
bone = self.get_bone(mch)
bone.ik_stretch = 0.1
if i == 1:
bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
setattr(bone, 'lock_ik_' + self.main_axis, False)
@stage.rig_bones
def rig_ik2_mch_chain(self):
target_bone = self.get_ik2_target_bone()
self.rig_ik_mch_end_bone(self.bones.mch.ik2_chain[-1], target_bone, self.bones.ctrl.ik_pole)
####################################################
# Heel tracking from IK2
@stage.generate_bones
def make_heel_track_bones(self):
orgs = self.bones.org.main
mch = self.bones.mch
mch.heel_track = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'mch', '_track'))
mch.heel_parent = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'mch', '_parent'))
# This two bone setup is used to move the damped track singularity out
# of the way to a forbidden zone of the rig, and thus avoid flipping.
# The bones are aligned to the center of the valid transformation zone.
self.align_ik_control_bone(mch.heel_track)
put_bone(self.obj, mch.heel_track, self.get_bone(orgs[2]).tail, scale=1/3)
copy_bone_position(self.obj, mch.heel_track, mch.heel_parent, scale=3/4)
@stage.parent_bones
def parent_heel_control_bone(self):
self.set_bone_parent(self.bones.ctrl.heel, self.bones.mch.heel_parent)
@stage.parent_bones
def parent_heel_track_bones(self):
# Parenting heel_parent deferred to apply_bones.
self.set_bone_parent(self.bones.mch.heel_track, self.get_ik2_target_bone())
@stage.configure_bones
def prerig_heel_track_bones(self):
# Assign the constraint before the apply stage.
self.make_constraint(
self.bones.mch.heel_track, 'DAMPED_TRACK', self.bones.mch.ik2_chain[1],
influence=self.params.front_paw_heel_influence
)
@stage.preapply_bones
def preapply_heel_track_bones(self):
# Assign local transform negating the effect of the constraint at rest.
track_bone = self.get_bone(self.bones.mch.heel_track)
bone = self.get_bone(self.bones.mch.heel_parent)
bone.matrix_basis = track_bone.matrix.inverted() @ bone.matrix
@stage.apply_bones
def apply_heel_track_bones(self):
# Complete the parent chain.
self.set_bone_parent(self.bones.mch.heel_parent, self.bones.mch.heel_track)
####################################################
# Settings
@classmethod
def add_parameters(self, params):
super().add_parameters(params)
params.front_paw_heel_influence = bpy.props.FloatProperty(
name = 'Heel IK Influence',
default = 0.8,
min = 0,
max = 1,
description = 'Influence of the secondary IK on the heel control rotation'
)
@classmethod
def parameters_ui(self, layout, params):
r = layout.row()
r.prop(params, "front_paw_heel_influence", slider=True)
super().parameters_ui(layout, params)
def create_sample(obj):
# generated by rigify.utils.write_metarig
bpy.ops.object.mode_set(mode='EDIT')
arm = obj.data
bones = {}
bone = arm.edit_bones.new('front_thigh.L')
bone.head = 0.0000, 0.0000, 0.6902
bone.tail = 0.0000, 0.0916, 0.4418
bone.roll = 0.0000
bone.use_connect = False
bones['front_thigh.L'] = bone.name
bone = arm.edit_bones.new('front_shin.L')
bone.head = 0.0000, 0.0916, 0.4418
bone.tail = 0.0000, 0.1014, 0.1698
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['front_thigh.L']]
bones['front_shin.L'] = bone.name
bone = arm.edit_bones.new('front_foot.L')
bone.head = 0.0000, 0.1014, 0.1698
bone.tail = 0.0000, 0.0699, 0.0411
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['front_shin.L']]
bones['front_foot.L'] = bone.name
bone = arm.edit_bones.new('front_toe.L')
bone.head = 0.0000, 0.0699, 0.0411
bone.tail = 0.0000, -0.0540, 0.0000
bone.roll = 3.1416
bone.use_connect = True
bone.parent = arm.edit_bones[bones['front_foot.L']]
bones['front_toe.L'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
pbone = obj.pose.bones[bones['front_thigh.L']]
pbone.rigify_type = 'limbs.front_paw'
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'
try:
pbone.rigify_parameters.limb_type = "paw"
except AttributeError:
pass
try:
pbone.rigify_parameters.fk_layers = [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['front_shin.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['front_foot.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['front_toe.L']]
pbone.rigify_type = ''
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'
try:
pbone.rigify_parameters.limb_type = "paw"
except AttributeError:
pass
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
bone.bbone_x = bone.bbone_z = bone.length * 0.05
arm.edit_bones.active = bone
return bones

View File

@ -44,6 +44,8 @@ ALL_TRUE = (True, True, True)
class Rig(BaseLimbRig):
"""Human leg rig."""
min_valid_orgs = max_valid_orgs = 4
def find_org_bones(self, bone):
bones = super().find_org_bones(bone)
@ -57,9 +59,6 @@ class Rig(BaseLimbRig):
return bones
def initialize(self):
if len(self.bones.org.main) != 4:
self.raise_error("Input to rig type must be a chain of 4 bones.")
super().initialize()
self.pivot_type = self.params.foot_pivot_type

View File

@ -50,6 +50,8 @@ class BaseLimbRig(BaseRig):
"""Common base for limb rigs."""
segmented_orgs = 2 # Number of org bones to segment
min_valid_orgs = None
max_valid_orgs = None
def find_org_bones(self, bone):
return BoneDict(
@ -59,8 +61,11 @@ class BaseLimbRig(BaseRig):
def initialize(self):
orgs = self.bones.org.main
if len(orgs) < self.segmented_orgs + 1:
self.raise_error("Input to rig type must be a chain of at least 3 bones.")
min_length = max(self.segmented_orgs + 1, self.min_valid_orgs or 0)
if len(orgs) < min_length:
self.raise_error("Input to rig type must be a chain of at least {} bones.", min_length)
if self.max_valid_orgs and len(orgs) > self.max_valid_orgs:
self.raise_error("Input to rig type must be a chain of at most {} bones.", self.max_valid_orgs)
self.segments = self.params.segments
self.bbone_segments = self.params.bbones
@ -161,6 +166,8 @@ class BaseLimbRig(BaseRig):
# IK stretch switch implementation.
# ik_target
# Corrected target position.
# ik_base
# Optionally the base of the ik chain (otherwise ctrl.ik_base)
# ik_end
# End of the IK chain: [ik_base, ik_end]
# deform[]:
@ -242,8 +249,10 @@ class BaseLimbRig(BaseRig):
def make_fk_control_chain(self):
self.bones.ctrl.fk = map_list(self.make_fk_control_bone, count(0), self.bones.org.main)
fk_name_suffix_cutoff = 2
def get_fk_name(self, i, org, kind):
return make_derived_name(org, kind, '_fk' if i <= 2 else '')
return make_derived_name(org, kind, '_fk' if i <= self.fk_name_suffix_cutoff else '')
def make_fk_control_bone(self, i, org):
return self.copy_bone(org, self.get_fk_name(i, org, 'ctrl'))
@ -296,7 +305,7 @@ class BaseLimbRig(BaseRig):
self.bones.mch.fk = map_list(self.make_fk_parent_bone, count(0), self.bones.org.main)
def make_fk_parent_bone(self, i, org):
if 2 <= i <= 3:
if i >= 2:
return self.copy_bone(org, self.get_fk_name(i, org, 'mch'), parent=True, scale=1/4)
@stage.parent_bones
@ -307,7 +316,7 @@ class BaseLimbRig(BaseRig):
self.parent_fk_parent_bone(*args)
def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org):
if i == 2:
if i >= 2:
self.set_bone_parent(parent_mch, prev_ctrl, use_connect=True, inherit_scale='NONE')
@stage.rig_bones
@ -316,7 +325,7 @@ class BaseLimbRig(BaseRig):
self.rig_fk_parent_bone(*args)
def rig_fk_parent_bone(self, i, parent_mch, org):
if i == 2:
if i >= 2:
self.make_constraint(parent_mch, 'COPY_SCALE', 'root', use_make_uniform=True)
@ -329,10 +338,19 @@ class BaseLimbRig(BaseRig):
else:
return []
def get_all_ik_controls(self):
def get_middle_ik_controls(self):
return []
def get_ik_fk_position_chains(self):
ik_chain = self.get_ik_output_chain()
return ik_chain, self.bones.ctrl.fk[0:len(ik_chain)]
def get_ik_control_chain(self):
ctrl = self.bones.ctrl
controls = [ctrl.ik_base, ctrl.ik_pole, ctrl.ik]
return controls + self.get_extra_ik_controls()
return [ctrl.ik_base, ctrl.ik_pole, *self.get_middle_ik_controls(), ctrl.ik]
def get_all_ik_controls(self):
return self.get_ik_control_chain() + self.get_extra_ik_controls()
@stage.generate_bones
def make_ik_controls(self):
@ -371,6 +389,9 @@ class BaseLimbRig(BaseRig):
else:
return self.bones.ctrl.ik
def get_ik_pole_parents(self):
return [(self.bones.mch.ik_target, self.bones.ctrl.ik)]
def register_switch_parents(self, pbuilder):
if self.rig_parent_bone:
pbuilder.register_parent(self, self.rig_parent_bone)
@ -385,7 +406,6 @@ class BaseLimbRig(BaseRig):
master = lambda: self.bones.ctrl.master
pcontrols = lambda: [ ctrl.master ] + self.get_all_ik_controls()
pole_parents = lambda: [(self.bones.mch.ik_target, ctrl.ik)]
self.register_switch_parents(pbuilder)
@ -395,7 +415,7 @@ class BaseLimbRig(BaseRig):
)
pbuilder.build_child(
self, ctrl.ik_pole, prop_bone=master, extra_parents=pole_parents,
self, ctrl.ik_pole, prop_bone=master, extra_parents=self.get_ik_pole_parents,
prop_id='pole_parent', prop_name='Pole Parent', controls=pcontrols,
no_fix_rotation=True, no_fix_scale=True,
)
@ -409,7 +429,6 @@ class BaseLimbRig(BaseRig):
base = self.get_bone(self.bones.ctrl.ik_base)
base.rotation_mode = 'ZXY'
base.lock_rotation = True, False, True
base.ik_stretch = 0.1
@stage.rig_bones
def rig_ik_controls(self):
@ -421,6 +440,9 @@ class BaseLimbRig(BaseRig):
set_bone_widget_transform(self.obj, ctrl.ik, self.get_ik_control_output())
if self.use_mch_ik_base:
set_bone_widget_transform(self.obj, ctrl.ik_base, self.bones.mch.ik_base, target_size=True)
self.make_ik_base_widget(ctrl.ik_base)
self.make_ik_pole_widget(ctrl.ik_pole)
self.make_ik_ctrl_widget(ctrl.ik)
@ -475,19 +497,31 @@ class BaseLimbRig(BaseRig):
ik_input_head_tail = 0.0
use_mch_ik_base = False
def get_ik_input_bone(self):
return self.get_ik_control_output()
def get_ik_chain_base(self):
return self.bones.mch.ik_base if self.use_mch_ik_base else self.bones.ctrl.ik_base
def get_ik_output_chain(self):
return [self.bones.ctrl.ik_base, self.bones.mch.ik_end, self.bones.mch.ik_target]
return [self.get_ik_chain_base(), self.bones.mch.ik_end, self.bones.mch.ik_target]
@stage.generate_bones
def make_ik_mch_chain(self):
orgs = self.bones.org.main
if self.use_mch_ik_base:
self.bones.mch.ik_base = self.make_ik_mch_base_bone(orgs)
self.bones.mch.ik_stretch = self.make_ik_mch_stretch_bone(orgs)
self.bones.mch.ik_target = self.make_ik_mch_target_bone(orgs)
self.bones.mch.ik_end = self.copy_bone(orgs[1], make_derived_name(orgs[1], 'mch', '_ik'))
def make_ik_mch_base_bone(self, orgs):
return self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik'))
def make_ik_mch_stretch_bone(self, orgs):
name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik_stretch'))
self.get_bone(name).tail = self.get_bone(orgs[2]).head
@ -498,12 +532,24 @@ class BaseLimbRig(BaseRig):
@stage.parent_bones
def parent_ik_mch_chain(self):
if self.use_mch_ik_base:
self.set_bone_parent(self.bones.mch.ik_base, self.bones.ctrl.ik_base, inherit_scale='AVERAGE')
self.set_bone_parent(self.bones.mch.ik_stretch, self.bones.mch.follow)
self.set_bone_parent(self.bones.mch.ik_target, self.get_ik_input_bone())
self.set_bone_parent(self.bones.mch.ik_end, self.bones.ctrl.ik_base)
self.set_bone_parent(self.bones.mch.ik_end, self.get_ik_chain_base())
@stage.configure_bones
def configure_ik_mch_chain(self):
bone = self.get_bone(self.get_ik_chain_base())
bone.ik_stretch = 0.1
bone = self.get_bone(self.bones.mch.ik_end)
bone.ik_stretch = 0.1
bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
setattr(bone, 'lock_ik_' + self.main_axis, False)
@stage.configure_bones
def configure_ik_mch_panel(self):
ctrl = self.bones.ctrl
panel = self.script.panel_with_selected_check(self, ctrl.flatten())
@ -526,32 +572,34 @@ class BaseLimbRig(BaseRig):
def add_global_buttons(self, panel, rig_name):
ctrl = self.bones.ctrl
ik_chain = self.get_ik_output_chain()
fk_chain = ctrl.fk[0:len(ik_chain)]
add_generic_snap_fk_to_ik(
panel,
fk_bones=self.bones.ctrl.fk[0:len(ik_chain)],
ik_bones=ik_chain,
fk_bones=fk_chain, ik_bones=ik_chain,
ik_ctrl_bones=self.get_all_ik_controls(),
rig_name=rig_name
)
ik_chain, fk_chain = self.get_ik_fk_position_chains()
add_limb_snap_ik_to_fk(
panel,
master=ctrl.master,
fk_bones=self.bones.ctrl.fk, ik_bones=ik_chain,
ik_ctrl_bones=[ctrl.ik_base, ctrl.ik, ctrl.ik_pole],
fk_bones=fk_chain, ik_bones=ik_chain,
ik_ctrl_bones=self.get_ik_control_chain(),
ik_extra_ctrls=self.get_extra_ik_controls(),
rig_name=rig_name
)
def add_ik_only_buttons(self, panel, rig_name):
ctrl = self.bones.ctrl
ik_chain = self.get_ik_output_chain()
ik_chain, fk_chain = self.get_ik_fk_position_chains()
add_limb_toggle_pole(
panel, master=ctrl.master,
ik_bones=ik_chain,
ik_ctrl_bones=[ctrl.ik_base, ctrl.ik, ctrl.ik_pole],
ik_ctrl_bones=self.get_ik_control_chain(),
ik_extra_ctrls=self.get_extra_ik_controls(),
)
@ -560,35 +608,35 @@ class BaseLimbRig(BaseRig):
mch = self.bones.mch
input_bone = self.get_ik_input_bone()
self.rig_ik_mch_stretch_bone(mch.ik_stretch, input_bone)
self.rig_ik_mch_target_bone(mch.ik_target, mch.ik_stretch, input_bone)
self.rig_ik_mch_stretch_bones(mch.ik_target, mch.ik_stretch, input_bone, self.ik_input_head_tail, 2)
self.rig_ik_mch_end_bone(mch.ik_end, mch.ik_target, self.bones.ctrl.ik_pole)
def rig_ik_mch_stretch_bone(self, mch_stretch, input_bone):
self.make_constraint(mch_stretch, 'STRETCH_TO', input_bone, head_tail=self.ik_input_head_tail, keep_axis='SWING_Y')
def rig_ik_mch_stretch_bones(self, mch_target, mch_stretch, input_bone, head_tail, org_count, bias=1.035):
# Compute increase in length to fully straighten
orgs = self.bones.org.main[0:org_count]
len_cur = (self.get_bone(orgs[-1]).tail - self.get_bone(orgs[0]).head).length
len_full = sum(self.get_bone(org).length for org in orgs)
len_scale = len_full / len_cur
con = self.make_constraint(mch_stretch, 'LIMIT_SCALE', min_y=0.0, max_y=1.05, owner_space='LOCAL')
# Limited stretch on the stretch bone
self.make_constraint(mch_stretch, 'STRETCH_TO', input_bone, head_tail=head_tail, keep_axis='SWING_Y')
con = self.make_constraint(mch_stretch, 'LIMIT_SCALE', min_y=0.0, max_y=len_scale*bias, owner_space='LOCAL')
self.make_driver(con, "influence", variables=[(self.prop_bone, 'IK_Stretch')], polynomial=[1.0, -1.0])
def rig_ik_mch_target_bone(self, mch_target, mch_stretch, input_bone):
# Snap the target to the end of the stretch bone
self.make_constraint(mch_target, 'COPY_LOCATION', mch_stretch, head_tail=1.0)
def rig_ik_mch_end_bone(self, mch_ik, mch_target, ctrl_pole):
bone = self.get_bone(mch_ik)
bone.ik_stretch = 0.1
bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
setattr(bone, 'lock_ik_' + self.main_axis, False)
def rig_ik_mch_end_bone(self, mch_ik, mch_target, ctrl_pole, chain=2):
con = self.make_constraint(
mch_ik, 'IK', mch_target, chain_count=2,
mch_ik, 'IK', mch_target, chain_count=chain,
)
self.make_driver(con, "mute", variables=[(self.prop_bone, 'pole_vector')], polynomial=[0.0, 1.0])
con_pole = self.make_constraint(
mch_ik, 'IK', mch_target, chain_count=2,
mch_ik, 'IK', mch_target, chain_count=chain,
pole_target=self.obj, pole_subtarget=ctrl_pole, pole_angle=self.pole_angle,
)
@ -901,26 +949,32 @@ class RigifyLimbIk2FkBase:
if use_pole:
match_pole_target(
context.view_layer,
ik_bones[0], ik_bones[1], ctrl_bones[2], matrices[0],
ik_bones[0], ik_bones[1], ctrl_bones[1], matrices[0],
(ik_bones[0].length + ik_bones[1].length)
)
else:
correct_rotation(context.view_layer, ctrl_bones[0], matrices[0])
correct_rotation(context.view_layer, ik_bones[0], matrices[0], ctrl_ik=ctrl_bones[0])
def assign_middle_controls(self, context, obj, matrices, ik_bones, ctrl_bones, *, lock=False, keyflags=None):
for mat, ik, ctrl in reversed(list(zip(matrices[2:-1], ik_bones[2:-1], ctrl_bones[2:-1]))):
ctrl.bone.use_inherit_rotation = not lock
ctrl.bone.inherit_scale = 'NONE' if lock else 'FULL'
context.view_layer.update()
mat = convert_pose_matrix_via_rest_delta(mat, ik, ctrl)
set_transform_from_matrix(obj, ctrl.name, mat, keyflags=keyflags)
def apply_frame_state(self, context, obj, matrices):
ik_bones = [ obj.pose.bones[k] for k in self.ik_bone_list ]
ctrl_bones = [ obj.pose.bones[k] for k in self.ctrl_bone_list ]
use_pole = len(ctrl_bones) > 2 and self.get_use_pole(obj)
use_pole = self.get_use_pole(obj)
# Set the end control position
tgt_matrix = ik_bones[2].bone.matrix_local
ctrl_matrix = ctrl_bones[1].bone.matrix_local
endmat = matrices[2] @ tgt_matrix.inverted() @ ctrl_matrix
endmat = convert_pose_matrix_via_rest_delta(matrices[-1], ik_bones[-1], ctrl_bones[-1])
set_transform_from_matrix(
obj, self.ctrl_bone_list[1], endmat, keyflags=self.keyflags
obj, self.ctrl_bone_list[-1], endmat, keyflags=self.keyflags
)
# Remove foot heel transform, if present
@ -937,15 +991,22 @@ class RigifyLimbIk2FkBase:
no_scale=True, no_rot=use_pole,
)
# Lock middle control transforms (first pass)
self.assign_middle_controls(context, obj, matrices, ik_bones, ctrl_bones, lock=True)
# Adjust the base bone state
self.compute_base_rotation(context, ik_bones, ctrl_bones, matrices, use_pole)
correct_scale(context.view_layer, ctrl_bones[0], matrices[0])
correct_scale(context.view_layer, ik_bones[0], matrices[0], ctrl_ik=ctrl_bones[0])
# Assign middle control transforms (final pass)
self.assign_middle_controls(context, obj, matrices, ik_bones, ctrl_bones, keyflags=self.keyflags)
# Keyframe controls
if self.keyflags is not None:
if use_pole:
keyframe_transform_properties(
obj, self.ctrl_bone_list[2], self.keyflags,
obj, self.ctrl_bone_list[1], self.keyflags,
no_rot=True, no_scale=True,
)
@ -1015,11 +1076,17 @@ class RigifyLimbTogglePoleBase(RigifyLimbIk2FkBase):
keyflags=self.keyflags_switch
)
# Lock middle control transforms
self.assign_middle_controls(context, obj, matrices, ik_bones, ctrl_bones, lock=True)
# Reset the base bone rotation
set_pose_rotation(ctrl_bones[0], Matrix.Identity(4))
self.compute_base_rotation(context, ik_bones, ctrl_bones, matrices, self.use_pole)
# Assign middle control transforms (final pass)
self.assign_middle_controls(context, obj, matrices, ik_bones, ctrl_bones, keyflags=self.keyflags)
# Keyframe controls
if self.keyflags is not None:
if self.use_pole:

View File

@ -23,6 +23,7 @@ import bpy
from ...utils.bones import compute_chain_x_axis, align_bone_x_axis, align_bone_z_axis
from ...utils.bones import align_bone_to_axis, flip_bone
from ...utils.naming import make_derived_name
from ...utils.widgets_basic import create_circle_widget, create_limb_widget
from ..widgets import create_foot_widget, create_ballsocket_widget
@ -32,13 +33,19 @@ from .limb_rigs import BaseLimbRig
class Rig(BaseLimbRig):
"""Paw rig."""
"""Paw rig with an optional second heel control."""
segmented_orgs = 3
min_valid_orgs = 4
max_valid_orgs = 5
toe_bone_index = 3
def initialize(self):
if len(self.bones.org.main) != 4:
self.raise_error("Input to rig type must be a chain of 4 bones.")
self.use_heel2 = len(self.bones.org.main) > 4
if self.use_heel2:
self.toe_bone_index = 4
self.fk_name_suffix_cutoff = 3
super().initialize()
@ -61,28 +68,10 @@ class Rig(BaseLimbRig):
align_bone_z_axis(self.obj, orgs[2], foot_x)
align_bone_z_axis(self.obj, orgs[3], -foot_x)
####################################################
# EXTRA BONES
#
# ctrl:
# heel:
# Foot heel control
# mch:
# toe_socket:
# IK toe orientation bone.
#
####################################################
# Utilities
####################################################
# IK controls
def get_extra_ik_controls(self):
return super().get_extra_ik_controls() + [self.bones.ctrl.heel]
def make_ik_control_bone(self, orgs):
name = self.copy_bone(orgs[3], make_derived_name(orgs[2], 'ctrl', '_ik'))
def align_ik_control_bone(self, name):
if self.params.rotation_axis == 'automatic' or self.params.auto_align_extremity:
align_bone_to_axis(self.obj, name, 'y', flip=True)
@ -93,7 +82,42 @@ class Rig(BaseLimbRig):
bone.tail[2] = bone.head[2]
bone.roll = 0
vec = self.get_bone(orgs[3]).tail - self.get_bone(orgs[2]).head
####################################################
# EXTRA BONES
#
# ctrl:
# heel:
# Foot heel control
# heel2 (optional):
# Second foot heel control
# mch:
# toe_socket:
# IK toe orientation bone.
# ik_heel2 (optional):
# Final position of heel2 in the IK output.
#
####################################################
####################################################
# IK controls
def get_middle_ik_controls(self):
return [self.bones.ctrl.heel] if self.use_heel2 else []
def get_extra_ik_controls(self):
extra = [self.bones.ctrl.heel2] if self.use_heel2 else [self.bones.ctrl.heel]
return super().get_extra_ik_controls() + extra
def make_ik_control_bone(self, orgs):
return self.make_paw_ik_control_bone(orgs[-2], orgs[-1], orgs[2])
def make_paw_ik_control_bone(self, org_one, org_two, org_name):
name = self.copy_bone(org_two, make_derived_name(org_name, 'ctrl', '_ik'))
self.align_ik_control_bone(name)
vec = self.get_bone(org_two).tail - self.get_bone(org_one).head
self.get_bone(name).length = self.vector_without_z(vec).length
return name
@ -120,37 +144,83 @@ class Rig(BaseLimbRig):
@stage.parent_bones
def parent_heel_control_bone(self):
self.set_bone_parent(self.bones.ctrl.heel, self.get_ik_control_output())
if self.use_heel2:
self.set_bone_parent(self.bones.ctrl.heel, self.bones.ctrl.heel2)
else:
self.set_bone_parent(self.bones.ctrl.heel, self.get_ik_control_output())
@stage.configure_bones
def configure_heel_control_bone(self):
bone = self.get_bone(self.bones.ctrl.heel)
bone.lock_location = True, True, True
bone.lock_scale = True, True, True
@stage.generate_widgets
def generate_heel_control_widget(self):
create_ballsocket_widget(self.obj, self.bones.ctrl.heel)
####################################################
# Second Heel control
@stage.generate_bones
def make_heel2_control_bone(self):
if self.use_heel2:
org = self.bones.org.main[3]
name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_ik'))
self.bones.ctrl.heel2 = name
flip_bone(self.obj, name)
@stage.parent_bones
def parent_heel2_control_bone(self):
if self.use_heel2:
self.set_bone_parent(self.bones.ctrl.heel2, self.get_ik_control_output())
@stage.configure_bones
def configure_heel2_control_bone(self):
if self.use_heel2:
bone = self.get_bone(self.bones.ctrl.heel2)
bone.lock_location = True, True, True
@stage.generate_widgets
def generate_heel2_control_widget(self):
if self.use_heel2:
create_ballsocket_widget(self.obj, self.bones.ctrl.heel2)
####################################################
# FK control chain
def make_fk_control_widget(self, i, ctrl):
if i < self.toe_bone_index - 1:
create_limb_widget(self.obj, ctrl)
elif i == self.toe_bone_index - 1:
create_circle_widget(self.obj, ctrl, radius=0.4, head_tail=0.0)
else:
create_circle_widget(self.obj, ctrl, radius=0.4, head_tail=0.5)
####################################################
# FK parents MCH chain
@stage.generate_bones
def make_toe_socket_bone(self):
org = self.bones.org.main[3]
org = self.bones.org.main[self.toe_bone_index]
self.bones.mch.toe_socket = self.copy_bone(org, make_derived_name(org, 'mch', '_ik_socket'))
@stage.parent_bones
def parent_toe_socket_bone(self):
self.set_bone_parent(self.bones.mch.toe_socket, self.get_ik_control_output())
def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org):
if i == 3:
if i == self.toe_bone_index:
self.set_bone_parent(parent_mch, prev_org, use_connect=True)
self.set_bone_parent(self.bones.mch.toe_socket, self.get_ik_control_output())
else:
super().parent_fk_parent_bone(i, parent_mch, prev_ctrl, org, prev_org)
def rig_fk_parent_bone(self, i, parent_mch, org):
if i == 3:
if i == self.toe_bone_index:
con = self.make_constraint(parent_mch, 'COPY_TRANSFORMS', self.bones.mch.toe_socket)
self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0])
@ -174,6 +244,30 @@ class Rig(BaseLimbRig):
self.set_bone_parent(self.bones.mch.ik_target, self.bones.ctrl.heel)
####################################################
# IK heel2 output
def get_ik_output_chain(self):
tail = [self.bones.mch.ik_heel2] if self.use_heel2 else []
return super().get_ik_output_chain() + tail
@stage.generate_bones
def make_ik_heel2_bone(self):
if self.use_heel2:
orgs = self.bones.org.main
self.bones.mch.ik_heel2 = self.copy_bone(orgs[3], make_derived_name(orgs[3], 'mch', '_ik_out'))
@stage.parent_bones
def parent_ik_heel2_bone(self):
if self.use_heel2:
self.set_bone_parent(self.bones.mch.ik_heel2, self.bones.ctrl.heel2)
@stage.rig_bones
def rig_ik_heel2_bone(self):
if self.use_heel2:
self.make_constraint(self.bones.mch.ik_heel2, 'COPY_LOCATION', self.bones.mch.ik_target, head_tail=1)
####################################################
# Deform chain
@ -284,3 +378,5 @@ def create_sample(obj):
bone.select_head = True
bone.select_tail = True
arm.edit_bones.active = bone
return bones

View File

@ -1,300 +1,194 @@
#====================== 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 .paw import Rig as pawRig
from ...utils.bones import align_bone_roll
from ...utils.naming import make_derived_name
from ...utils.misc import map_list
from itertools import count
IMPLEMENTATION = True # Include and set True if Rig is just an implementation for a wrapper class
# add_parameters and parameters_ui are unused for implementation classes
from ...base_rig import stage
from .limb_rigs import BaseLimbRig
from .paw import Rig as pawRig, create_sample as create_paw_sample
class Rig(pawRig):
pass
"""Rear paw rig with special IK automation."""
####################################################
# EXTRA BONES
#
# mch:
# ik2_stretch, ik2_target
# Three bone IK stretch limit
# ik2_chain[2]
# Second IK system (pre-driving thigh and ik3)
# ik3_chain[2]
# Third IK system (pre-driving heel)
#
####################################################
####################################################
# IK controls
def get_middle_ik_controls(self):
return [self.bones.ctrl.heel]
def get_ik_fk_position_chains(self):
ik_chain, fk_chain = super().get_ik_fk_position_chains()
if not self.use_heel2:
return [*ik_chain, ik_chain[-1]], [*fk_chain, fk_chain[-1]]
return ik_chain, fk_chain
def get_extra_ik_controls(self):
extra = [self.bones.ctrl.heel2] if self.use_heel2 else []
return BaseLimbRig.get_extra_ik_controls(self) + extra
def get_ik_pole_parents(self):
return [(self.bones.mch.ik2_target, self.bones.ctrl.ik)]
####################################################
# Heel control
@stage.parent_bones
def parent_heel_control_bone(self):
self.set_bone_parent(self.bones.ctrl.heel, self.bones.mch.ik3_chain[-1])
####################################################
# Second IK system (pre-driving thigh)
use_mch_ik_base = True
def get_ik2_input_bone(self):
return self.bones.ctrl.heel2 if self.use_heel2 else self.bones.mch.toe_socket
@stage.generate_bones
def make_ik2_mch_stretch(self):
orgs = self.bones.org.main
self.bones.mch.ik2_stretch = self.make_ik2_mch_stretch_bone(orgs)
self.bones.mch.ik2_target = self.make_ik2_mch_target_bone(orgs)
def make_ik2_mch_stretch_bone(self, orgs):
name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik2_stretch'))
self.get_bone(name).tail = self.get_bone(orgs[3]).head
return name
def make_ik2_mch_target_bone(self, orgs):
return self.copy_bone(orgs[3], make_derived_name(orgs[0], 'mch', '_ik2_target'), scale=1/2)
@stage.generate_bones
def make_ik2_mch_chain(self):
orgs = self.bones.org.main
chain = map_list(self.make_ik2_mch_bone, count(0), orgs[0:2])
self.bones.mch.ik2_chain = chain
org_bones = map_list(self.get_bone, orgs)
chain_bones = map_list(self.get_bone, chain)
# Extend the base IK control (used in the ik2 chain) with the projected length of org2
chain_bones[0].length += org_bones[2].vector.dot(chain_bones[0].vector.normalized())
chain_bones[1].head = chain_bones[0].tail
chain_bones[1].tail = org_bones[2].tail
align_bone_roll(self.obj, chain[1], orgs[1])
def make_ik2_mch_bone(self, i, org):
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik2'))
@stage.parent_bones
def parent_ik2_mch_chain(self):
mch = self.bones.mch
self.set_bone_parent(mch.ik2_stretch, mch.follow)
self.set_bone_parent(mch.ik2_target, self.get_ik2_input_bone())
self.set_bone_parent(mch.ik2_chain[0], self.bones.ctrl.ik_base, inherit_scale='AVERAGE')
self.parent_bone_chain(mch.ik2_chain, use_connect=True)
@stage.configure_bones
def configure_ik2_mch_chain(self):
for i, mch in enumerate(self.bones.mch.ik2_chain):
self.configure_ik2_mch_bone(i, mch)
def configure_ik2_mch_bone(self, i, mch):
bone = self.get_bone(mch)
bone.ik_stretch = 0.1
if i == 1:
bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
setattr(bone, 'lock_ik_' + self.main_axis, False)
@stage.rig_bones
def rig_ik2_mch_chain(self):
mch = self.bones.mch
input_bone = self.get_ik2_input_bone()
head_tail = 1 if self.use_heel2 else 0
self.rig_ik_mch_stretch_bones(mch.ik2_target, mch.ik2_stretch, input_bone, head_tail, 3)
self.rig_ik_mch_end_bone(mch.ik2_chain[-1], mch.ik2_target, self.bones.ctrl.ik_pole)
####################################################
# Third IK system (pre-driving heel control)
@stage.generate_bones
def make_ik3_mch_chain(self):
self.bones.mch.ik3_chain = map_list(self.make_ik3_mch_bone, count(0), self.bones.org.main[1:3])
def make_ik3_mch_bone(self, i, org):
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik3'))
@stage.parent_bones
def parent_ik3_mch_chain(self):
mch = self.bones.mch
self.set_bone_parent(mch.ik3_chain[0], mch.ik2_chain[0])
self.parent_bone_chain(mch.ik3_chain, use_connect=True)
@stage.configure_bones
def configure_ik3_mch_chain(self):
for i, mch in enumerate(self.bones.mch.ik3_chain):
self.configure_ik3_mch_bone(i, mch)
def configure_ik3_mch_bone(self, i, mch):
bone = self.get_bone(mch)
bone.ik_stretch = 0.1
if i == 0:
bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
setattr(bone, 'lock_ik_' + self.main_axis, False)
@stage.rig_bones
def rig_ik3_mch_chain(self):
mch = self.bones.mch
# Mostly cancel ik2 scaling.
self.make_constraint(
mch.ik3_chain[0], 'COPY_SCALE', self.bones.ctrl.ik_base,
use_make_uniform=True, influence=0.75,
)
self.make_constraint(mch.ik3_chain[-1], 'IK', mch.ik2_target, chain_count=2)
def create_sample(obj):
# generated by rigify.utils.write_metarig
bpy.ops.object.mode_set(mode='EDIT')
arm = obj.data
bones = {}
bone = arm.edit_bones.new('thigh.L')
bone.head[:] = 0.0291, 0.1181, 0.2460
bone.tail[:] = 0.0293, 0.1107, 0.1682
bone.roll = 3.1383
bone.use_connect = False
bones['thigh.L'] = bone.name
bone = arm.edit_bones.new('shin.L')
bone.head[:] = 0.0293, 0.1107, 0.1682
bone.tail[:] = 0.0293, 0.1684, 0.1073
bone.roll = 3.1416
bone.use_connect = True
bone.parent = arm.edit_bones[bones['thigh.L']]
bones['shin.L'] = bone.name
bone = arm.edit_bones.new('foot.L')
bone.head[:] = 0.0293, 0.1684, 0.1073
bone.tail[:] = 0.0293, 0.1530, 0.0167
bone.roll = 3.1416
bone.use_connect = True
bone.parent = arm.edit_bones[bones['shin.L']]
bones['foot.L'] = bone.name
bone = arm.edit_bones.new('r_toe.L')
bone.head[:] = 0.0293, 0.1530, 0.0167
bone.tail[:] = 0.0293, 0.1224, 0.0167
bone.roll = 0.0000
bone.use_connect = True
bone.parent = arm.edit_bones[bones['foot.L']]
bones['r_toe.L'] = bone.name
bone = arm.edit_bones.new('r_palm.001.L')
bone.head[:] = 0.0220, 0.1457, 0.0123
bone.tail[:] = 0.0215, 0.1401, 0.0123
bone.roll = 0.0014
bone.use_connect = False
bone.parent = arm.edit_bones[bones['r_toe.L']]
bones['r_palm.001.L'] = bone.name
bone = arm.edit_bones.new('r_palm.002.L')
bone.head[:] = 0.0297, 0.1458, 0.0123
bone.tail[:] = 0.0311, 0.1393, 0.0123
bone.roll = -0.0005
bone.use_connect = False
bone.parent = arm.edit_bones[bones['r_toe.L']]
bones['r_palm.002.L'] = bone.name
bone = arm.edit_bones.new('r_palm.003.L')
bone.head[:] = 0.0363, 0.1473, 0.0123
bone.tail[:] = 0.0376, 0.1407, 0.0123
bone.roll = 0.0000
bone.use_connect = False
bone.parent = arm.edit_bones[bones['r_toe.L']]
bones['r_palm.003.L'] = bone.name
bone = arm.edit_bones.new('r_palm.004.L')
bone.head[:] = 0.0449, 0.1501, 0.0123
bone.tail[:] = 0.0466, 0.1479, 0.0123
bone.roll = -0.0004
bone.use_connect = False
bone.parent = arm.edit_bones[bones['r_toe.L']]
bones['r_palm.004.L'] = bone.name
bone = arm.edit_bones.new('r_index.001.L')
bone.head[:] = 0.0215, 0.1367, 0.0087
bone.tail[:] = 0.0217, 0.1325, 0.0070
bone.roll = -0.3427
bone.use_connect = False
bone.parent = arm.edit_bones[bones['r_palm.001.L']]
bones['r_index.001.L'] = bone.name
bone = arm.edit_bones.new('r_middle.001.L')
bone.head[:] = 0.0311, 0.1358, 0.0117
bone.tail[:] = 0.0324, 0.1297, 0.0092
bone.roll = -1.0029
bone.use_connect = False
bone.parent = arm.edit_bones[bones['r_palm.002.L']]
bones['r_middle.001.L'] = bone.name
bone = arm.edit_bones.new('r_ring.001.L')
bone.head[:] = 0.0376, 0.1372, 0.0117
bone.tail[:] = 0.0389, 0.1311, 0.0092
bone.roll = -1.0029
bone.use_connect = False
bone.parent = arm.edit_bones[bones['r_palm.003.L']]
bones['r_ring.001.L'] = bone.name
bone = arm.edit_bones.new('r_pinky.001.L')
bone.head[:] = 0.0466, 0.1444, 0.0083
bone.tail[:] = 0.0476, 0.1412, 0.0074
bone.roll = -1.7551
bone.use_connect = False
bone.parent = arm.edit_bones[bones['r_palm.004.L']]
bones['r_pinky.001.L'] = bone.name
bone = arm.edit_bones.new('r_index.002.L')
bone.head[:] = 0.0217, 0.1325, 0.0070
bone.tail[:] = 0.0221, 0.1271, 0.0038
bone.roll = -0.2465
bone.use_connect = True
bone.parent = arm.edit_bones[bones['r_index.001.L']]
bones['r_index.002.L'] = bone.name
bone = arm.edit_bones.new('r_middle.002.L')
bone.head[:] = 0.0324, 0.1297, 0.0092
bone.tail[:] = 0.0343, 0.1210, 0.0039
bone.roll = -0.7479
bone.use_connect = True
bone.parent = arm.edit_bones[bones['r_middle.001.L']]
bones['r_middle.002.L'] = bone.name
bone = arm.edit_bones.new('r_ring.002.L')
bone.head[:] = 0.0389, 0.1311, 0.0092
bone.tail[:] = 0.0407, 0.1229, 0.0042
bone.roll = -0.7479
bone.use_connect = True
bone.parent = arm.edit_bones[bones['r_ring.001.L']]
bones['r_ring.002.L'] = bone.name
bone = arm.edit_bones.new('r_pinky.002.L')
bone.head[:] = 0.0476, 0.1412, 0.0074
bone.tail[:] = 0.0494, 0.1351, 0.0032
bone.roll = -0.8965
bone.use_connect = True
bone.parent = arm.edit_bones[bones['r_pinky.001.L']]
bones['r_pinky.002.L'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
bones = create_paw_sample(obj)
pbone = obj.pose.bones[bones['thigh.L']]
pbone.rigify_type = 'limbs.paw'
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'
try:
pbone.rigify_parameters.limb_type = "paw"
except AttributeError:
pass
try:
pbone.rigify_parameters.fk_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
pbone.rigify_parameters.segments = 2
except AttributeError:
pass
pbone = obj.pose.bones[bones['shin.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['foot.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['r_toe.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['r_palm.001.L']]
pbone.rigify_type = 'limbs.super_palm'
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'
pbone = obj.pose.bones[bones['r_palm.002.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['r_palm.003.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['r_palm.004.L']]
pbone.rigify_type = 'limbs.super_palm'
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'
pbone = obj.pose.bones[bones['r_index.001.L']]
pbone.rigify_type = 'limbs.simple_tentacle'
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'
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['r_middle.001.L']]
pbone.rigify_type = 'limbs.simple_tentacle'
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'
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['r_ring.001.L']]
pbone.rigify_type = 'limbs.simple_tentacle'
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'
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['r_pinky.001.L']]
pbone.rigify_type = 'limbs.simple_tentacle'
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'
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['r_index.002.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['r_middle.002.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['r_ring.002.L']]
pbone.rigify_type = ''
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'
pbone = obj.pose.bones[bones['r_pinky.002.L']]
pbone.rigify_type = ''
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
for eb in arm.edit_bones:
if ('thigh' in eb.name) or ('shin' in eb.name) or ('foot' in eb.name) or ('toe' in eb.name):
eb.layers = (False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
else:
eb.layers = (False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
arm.layers = (False, False, False, False, False, True, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
if __name__ == "__main__":
create_sample(bpy.context.active_object)
pbone.rigify_type = 'limbs.rear_paw'
return bones

View File

@ -100,7 +100,7 @@ class Rig(SimpleChainRig):
self.bones.ctrl.fk += [self.make_tip_control_bone(orgs[-1], orgs[0])]
def make_control_bone(self, i, org):
return self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=False)
return self.copy_bone(org, make_derived_name(org, 'ctrl'), inherit_scale=True)
def make_tip_control_bone(self, org, name_org):
name = self.copy_bone(org, make_derived_name(name_org, 'ctrl'), parent=False)
@ -219,7 +219,7 @@ class Rig(SimpleChainRig):
self.bones.mch.bend = map_list(self.make_mch_bend_bone, self.bones.org)
def make_mch_bend_bone(self, org):
return self.copy_bone(org, make_derived_name(org, 'mch', '_drv'), parent=False, scale=0.3)
return self.copy_bone(org, make_derived_name(org, 'mch', '_drv'), inherit_scale=True, scale=0.3)
@stage.parent_bones
def parent_mch_bend_chain(self):

View File

@ -121,7 +121,7 @@ def new_bone(obj, bone_name):
raise MetarigError("Can't add new bone '%s' outside of edit mode" % bone_name)
def copy_bone(obj, bone_name, assign_name='', *, parent=False, bbone=False, length=None, scale=None):
def copy_bone(obj, bone_name, assign_name='', *, parent=False, inherit_scale=False, bbone=False, length=None, scale=None):
""" Makes a copy of the given bone in the given armature object.
Returns the resulting bone's name.
"""
@ -151,6 +151,8 @@ def copy_bone(obj, bone_name, assign_name='', *, parent=False, bbone=False, leng
edit_bone_2.use_inherit_rotation = edit_bone_1.use_inherit_rotation
edit_bone_2.use_local_location = edit_bone_1.use_local_location
if parent or inherit_scale:
edit_bone_2.inherit_scale = edit_bone_1.inherit_scale
if bbone:
@ -385,9 +387,9 @@ class BoneUtilityMixin(object):
self.register_new_bone(name)
return name
def copy_bone(self, bone_name, new_name='', *, parent=False, bbone=False, length=None, scale=None):
def copy_bone(self, bone_name, new_name='', *, parent=False, inherit_scale=False, bbone=False, length=None, scale=None):
"""Copy the bone with the given name, returning the new name."""
name = copy_bone(self.obj, bone_name, new_name, parent=parent, bbone=bbone, length=length, scale=scale)
name = copy_bone(self.obj, bone_name, new_name, parent=parent, inherit_scale=inherit_scale, bbone=bbone, length=length, scale=scale)
self.register_new_bone(name, bone_name)
return name
@ -659,13 +661,15 @@ def align_bone_to_axis(obj, bone_name, axis, *, length=None, roll=0, flip=False)
bone_e.roll = roll
def set_bone_widget_transform(obj, bone_name, transform_bone, use_size=True, scale=1.0):
def set_bone_widget_transform(obj, bone_name, transform_bone, use_size=True, scale=1.0, target_size=False):
assert obj.mode != 'EDIT'
bone = obj.pose.bones[bone_name]
if transform_bone and transform_bone != bone_name:
bone.custom_shape_transform = obj.pose.bones[transform_bone]
bone.custom_shape_transform = bone2 = obj.pose.bones[transform_bone]
if use_size and target_size:
scale *= bone2.length / bone.length
else:
bone.custom_shape_transform = None

View File

@ -23,6 +23,8 @@ import importlib
import importlib.util
import os
from bpy.types import bpy_struct, bpy_prop_array, Constraint
RIG_DIR = "rigs" # Name of the directory where rig types are kept
METARIG_DIR = "metarigs" # Name of the directory where metarigs are kept
TEMPLATE_DIR = "ui_templates" # Name of the directory where ui templates are kept
@ -147,6 +149,30 @@ def list_bone_names_depth_first_sorted(obj):
return result_list
def _get_property_value(obj, name):
value = getattr(obj, name, None)
if isinstance(value, bpy_prop_array):
value = tuple(value)
return value
def _generate_properties(lines, prefix, obj, base_class, *, defaults={}, objects={}):
block_props = set(prop.identifier for prop in base_class.bl_rna.properties) - set(defaults.keys())
for prop in type(obj).bl_rna.properties:
if prop.identifier not in block_props and not prop.is_readonly:
cur_value = _get_property_value(obj, prop.identifier)
if prop.identifier in defaults:
if cur_value == defaults[prop.identifier]:
continue
if isinstance(cur_value, bpy_struct):
if cur_value in objects:
lines.append('%s.%s = %s' % (prefix, prop.identifier, objects[cur_value]))
else:
lines.append('%s.%s = %r' % (prefix, prop.identifier, cur_value))
def write_metarig(obj, layers=False, func_name="create", groups=False):
"""
Write a metarig as a python script, this rig is to have all info needed for
@ -211,6 +237,8 @@ def write_metarig(obj, layers=False, func_name="create", groups=False):
code.append(" bone.tail = %.4f, %.4f, %.4f" % bone.tail.to_tuple(4))
code.append(" bone.roll = %.4f" % bone.roll)
code.append(" bone.use_connect = %s" % str(bone.use_connect))
if bone.inherit_scale != 'FULL':
code.append(" bone.inherit_scale = %r" % str(bone.inherit_scale))
if bone.parent:
code.append(" bone.parent = arm.edit_bones[bones[%r]]" % bone.parent.name)
code.append(" bones[%r] = bone.name" % bone.name)
@ -243,6 +271,29 @@ def write_metarig(obj, layers=False, func_name="create", groups=False):
code.append(" pbone.rigify_parameters.%s = %s" % (param_name, str(param)))
code.append(" except AttributeError:")
code.append(" pass")
# Constraints
for con in pbone.constraints:
code.append(" con = pbone.constraints.new(%r)" % (con.type))
code.append(" con.name = %r" % (con.name))
# Add target first because of target_space handling
if con.type == 'ARMATURE':
for tgt in con.targets:
code.append(" tgt = con.targets.new()")
code.append(" tgt.target = obj")
code.append(" tgt.subtarget = %r" % (tgt.subtarget))
code.append(" tgt.weight = %.3f" % (tgt.weight))
elif getattr(con, 'target', None) == obj:
code.append(" con.target = obj")
# Generic properties
_generate_properties(
code, " con", con, Constraint,
defaults={
'owner_space': 'WORLD', 'target_space': 'WORLD',
'mute': False, 'influence': 1.0,
'target': obj,
},
objects={obj: 'obj'},
)
code.append("\n bpy.ops.object.mode_set(mode='EDIT')")
code.append(" for bone in arm.edit_bones:")