[Bf-extensions-cvs] [efd3869] rigify_fixes: Pitchipoy FK/IK switch implemented
Lucio Rossi
noreply at git.blender.org
Fri Jul 29 13:27:29 CEST 2016
Commit: efd386991e12320590ae9943496d4a90961f132f
Author: Lucio Rossi
Date: Thu Jul 28 18:32:36 2016 +0200
Branches: rigify_fixes
https://developer.blender.org/rBAefd386991e12320590ae9943496d4a90961f132f
Pitchipoy FK/IK switch implemented
===================================================================
M rigify/generate.py
A rigify/rig_ui_pitchipoy_template.py
M rigify/rigs/pitchipoy/limbs/ui.py
===================================================================
diff --git a/rigify/generate.py b/rigify/generate.py
index 4fb83f5..0536377 100644
--- a/rigify/generate.py
+++ b/rigify/generate.py
@@ -32,6 +32,8 @@ from .utils import create_root_widget
from .utils import random_id
from .utils import copy_attributes
from .rig_ui_template import UI_SLIDERS, layers_ui, UI_REGISTER
+from .rig_ui_pitchipoy_template import UI_P_SLIDERS, layers_P_ui, UI_P_REGISTER
+
RIG_MODULE = "rigs"
ORG_LAYER = [n == 31 for n in range(0, 32)] # Armature layer that original bones should be moved to.
@@ -385,18 +387,35 @@ def generate_rig(context, metarig):
print( l.name )
layer_layout += [(l.name, l.row)]
- # Generate the UI script
- if "rig_ui.py" in bpy.data.texts:
- script = bpy.data.texts["rig_ui.py"]
- script.clear()
+
+ if isPitchipoy(metarig):
+
+ # Generate the UI Pitchipoy script
+ if "rig_ui.py" in bpy.data.texts:
+ script = bpy.data.texts["rig_ui.py"]
+ script.clear()
+ else:
+ script = bpy.data.texts.new("rig_ui.py")
+ script.write(UI_P_SLIDERS % rig_id)
+ for s in ui_scripts:
+ script.write("\n " + s.replace("\n", "\n ") + "\n")
+ script.write(layers_P_ui(vis_layers, layer_layout))
+ script.write(UI_P_REGISTER)
+ script.use_module = True
+
else:
- script = bpy.data.texts.new("rig_ui.py")
- script.write(UI_SLIDERS % rig_id)
- for s in ui_scripts:
- script.write("\n " + s.replace("\n", "\n ") + "\n")
- script.write(layers_ui(vis_layers, layer_layout))
- script.write(UI_REGISTER)
- script.use_module = True
+ # Generate the UI script
+ if "rig_ui.py" in bpy.data.texts:
+ script = bpy.data.texts["rig_ui.py"]
+ script.clear()
+ else:
+ script = bpy.data.texts.new("rig_ui.py")
+ script.write(UI_SLIDERS % rig_id)
+ for s in ui_scripts:
+ script.write("\n " + s.replace("\n", "\n ") + "\n")
+ script.write(layers_ui(vis_layers, layer_layout))
+ script.write(UI_REGISTER)
+ script.use_module = True
# Run UI script
exec(script.as_string(), {})
@@ -451,3 +470,13 @@ def param_name(param_name, rig_type):
""" Get the actual parameter name, sans-rig-type.
"""
return param_name[len(rig_type) + 1:]
+
+def isPitchipoy(metarig):
+ """ Returns True if metarig is type pitchipoy.
+ """
+ pbones=metarig.pose.bones
+ for pb in pbones:
+ words = pb.rigify_type.partition('.')
+ if words[0] == 'pitchipoy':
+ return True
+ return False
\ No newline at end of file
diff --git a/rigify/rig_ui_pitchipoy_template.py b/rigify/rig_ui_pitchipoy_template.py
new file mode 100644
index 0000000..da3bda7
--- /dev/null
+++ b/rigify/rig_ui_pitchipoy_template.py
@@ -0,0 +1,717 @@
+#====================== 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>
+
+UI_P_SLIDERS = '''
+import bpy
+from mathutils import Matrix, Vector
+from math import acos, pi, radians
+
+rig_id = "%s"
+
+
+############################
+## Math utility functions ##
+############################
+
+def perpendicular_vector(v):
+ """ Returns a vector that is perpendicular to the one given.
+ The returned vector is _not_ guaranteed to be normalized.
+ """
+ # Create a vector that is not aligned with v.
+ # It doesn't matter what vector. Just any vector
+ # that's guaranteed to not be pointing in the same
+ # direction.
+ if abs(v[0]) < abs(v[1]):
+ tv = Vector((1,0,0))
+ else:
+ tv = Vector((0,1,0))
+
+ # Use cross prouct to generate a vector perpendicular to
+ # both tv and (more importantly) v.
+ return v.cross(tv)
+
+
+def rotation_difference(mat1, mat2):
+ """ Returns the shortest-path rotational difference between two
+ matrices.
+ """
+ q1 = mat1.to_quaternion()
+ q2 = mat2.to_quaternion()
+ angle = acos(min(1,max(-1,q1.dot(q2)))) * 2
+ if angle > pi:
+ angle = -angle + (2*pi)
+ return angle
+
+def tail_distance(angle,bone_ik,bone_fk):
+ """ Returns the distance between the tails of two bones
+ after rotating bone_ik in AXIS_ANGLE mode.
+ """
+ rot_mod=bone_ik.rotation_mode
+ if rot_mod != 'AXIS_ANGLE':
+ bone_ik.rotation_mode = 'AXIS_ANGLE'
+ bone_ik.rotation_axis_angle[0] = angle
+ bpy.context.scene.update()
+
+ dv = (bone_fk.tail - bone_ik.tail).length
+
+ bone_ik.rotation_mode = rot_mod
+ return dv
+
+def find_min_range(bone_ik,bone_fk,f=tail_distance,delta=pi/8):
+ """ finds the range where lies the minimum of function f applied on bone_ik and bone_fk
+ at a certain angle.
+ """
+ rot_mod=bone_ik.rotation_mode
+ if rot_mod != 'AXIS_ANGLE':
+ bone_ik.rotation_mode = 'AXIS_ANGLE'
+
+ start_angle = bone_ik.rotation_axis_angle[0]
+ angle = start_angle
+ while (angle > (start_angle - 2*pi)) and (angle < (start_angle + 2*pi)):
+ l_dist = f(angle-delta,bone_ik,bone_fk)
+ c_dist = f(angle,bone_ik,bone_fk)
+ r_dist = f(angle+delta,bone_ik,bone_fk)
+ if min((l_dist,c_dist,r_dist)) == c_dist:
+ bone_ik.rotation_mode = rot_mod
+ return (angle-delta,angle+delta)
+ else:
+ angle=angle+delta
+
+def ternarySearch(f, left, right, bone_ik, bone_fk, absolutePrecision):
+ """
+ Find minimum of unimodal function f() within [left, right]
+ To find the maximum, revert the if/else statement or revert the comparison.
+ """
+ while True:
+ #left and right are the current bounds; the maximum is between them
+ if abs(right - left) < absolutePrecision:
+ return (left + right)/2
+
+ leftThird = left + (right - left)/3
+ rightThird = right - (right - left)/3
+
+ if f(leftThird, bone_ik, bone_fk) > f(rightThird, bone_ik, bone_fk):
+ left = leftThird
+ else:
+ right = rightThird
+
+#########################################
+## "Visual Transform" helper functions ##
+#########################################
+
+def get_pose_matrix_in_other_space(mat, pose_bone):
+ """ Returns the transform matrix relative to pose_bone's current
+ transform space. In other words, presuming that mat is in
+ armature space, slapping the returned matrix onto pose_bone
+ should give it the armature-space transforms of mat.
+ TODO: try to handle cases with axis-scaled parents better.
+ """
+ rest = pose_bone.bone.matrix_local.copy()
+ rest_inv = rest.inverted()
+ if pose_bone.parent:
+ par_mat = pose_bone.parent.matrix.copy()
+ par_inv = par_mat.inverted()
+ par_rest = pose_bone.parent.bone.matrix_local.copy()
+ else:
+ par_mat = Matrix()
+ par_inv = Matrix()
+ par_rest = Matrix()
+
+ # Get matrix in bone's current transform space
+ smat = rest_inv * (par_rest * (par_inv * mat))
+
+ # Compensate for non-local location
+ #if not pose_bone.bone.use_local_location:
+ # loc = smat.to_translation() * (par_rest.inverted() * rest).to_quaternion()
+ # smat.translation = loc
+
+ return smat
+
+
+def get_local_pose_matrix(pose_bone):
+ """ Returns the local transform matrix of the given pose bone.
+ """
+ return get_pose_matrix_in_other_space(pose_bone.matrix, pose_bone)
+
+
+def set_pose_translation(pose_bone, mat):
+ """ Sets the pose bone's translation to the same translation as the given matrix.
+ Matrix should be given in bone's local space.
+ """
+ if pose_bone.bone.use_local_location == True:
+ pose_bone.location = mat.to_translation()
+ else:
+ loc = mat.to_translation()
+
+ rest = pose_bone.bone.matrix_local.copy()
+ if pose_bone.bone.parent:
+ par_rest = pose_bone.bone.parent.matrix_local.copy()
+ else:
+ par_rest = Matrix()
+
+ q = (par_rest.inverted() * rest).to_quaternion()
+ pose_bone.location = q * loc
+
+
+def set_pose_rotation(pose_bone, mat):
+ """ Sets the pose bone's rotation to the same rotation as the given matrix.
+ Matrix should be given in bone's local space.
+ """
+ q = mat.to_quaternion()
+
+ if pose_bone.rotation_mode == 'QUATERNION':
+ pose_bone.rotation_quaternion = q
+ elif pose_bone.rotation_mode == 'AXIS_ANGLE':
+ pose_bone.rotation_axis_angle[0] = q.angle
+ pose_bone.rotation_axis_angle[1] = q.axis[0]
+ pose_bone.rotation_axis_angle[2] = q.axis[1]
+ pose_bone.rotation_axis_angle[3] = q.axis[2]
+ else:
+ pose_bone.rotation_euler = q.to_euler(pose_bone.rotation_mode)
+
+
+def set_pose_scale(pose_bone, mat):
+ """ Sets the pose bone's scale to the same scale as the given matrix.
+ Matrix should be given in bone's local space.
+ """
+ pose_bone.scale = mat.to_scale()
+
+
+def match_pose_translation(pose_bone, target_bone):
+ """ Matches pose_bone's visual translation to target_bone's visual
+ translation.
+ This function assumes you are in pose mode on the relevant armature.
+ """
+ mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
+ set_pose_translation(pose_bone, mat)
+ bpy.ops.object.mode_set(mode='OBJECT')
+ bpy.ops.object.mode_set(mode='POSE')
+
+
+def mat
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list