diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-29 11:00:38 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2019-09-29 17:48:56 +0300 |
commit | 6bb8ab3ad7b8131ffa9ed3261b6da8627903f3b1 (patch) | |
tree | e9df96fbef4073c43ee1a316763c632b9392a387 /rigify/utils | |
parent | 73784e78de99cb647870cbd93ef497f3e437a1a6 (diff) |
Rigify: various additions to bone, mechanism and widget utilities.
Support easier setting of bone orientation via matrix, inherit_scale,
invert_x/y/z constraint properties, computing a matrix from two axis
vectors, adjusting widget positions, and add a pivot widget.
Diffstat (limited to 'rigify/utils')
-rw-r--r-- | rigify/utils/bones.py | 31 | ||||
-rw-r--r-- | rigify/utils/mechanism.py | 6 | ||||
-rw-r--r-- | rigify/utils/misc.py | 23 | ||||
-rw-r--r-- | rigify/utils/rig.py | 4 | ||||
-rw-r--r-- | rigify/utils/widgets.py | 14 | ||||
-rw-r--r-- | rigify/utils/widgets_basic.py | 32 |
6 files changed, 98 insertions, 12 deletions
diff --git a/rigify/utils/bones.py b/rigify/utils/bones.py index 738f5d42..6a09cee1 100644 --- a/rigify/utils/bones.py +++ b/rigify/utils/bones.py @@ -261,7 +261,7 @@ def flip_bone_chain(obj, bone_names): bone.use_connect = True -def put_bone(obj, bone_name, pos): +def put_bone(obj, bone_name, pos, *, matrix=None, length=None, scale=None): """ Places a bone at the given position. """ if bone_name not in obj.data.edit_bones: @@ -270,8 +270,25 @@ def put_bone(obj, bone_name, pos): if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE': bone = obj.data.edit_bones[bone_name] - delta = pos - bone.head - bone.translate(delta) + if matrix is not None: + old_len = len(matrix) + matrix = matrix.to_4x4() + + if pos is not None: + matrix.translation = pos + elif old_len < 4: + matrix.translation = bone.head + + bone.matrix = matrix + + else: + delta = pos - bone.head + bone.translate(delta) + + if length is not None: + bone.length = length + elif scale is not None: + bone.length *= scale else: raise MetarigError("Cannot 'put' bones outside of edit mode") @@ -394,18 +411,20 @@ class BoneUtilityMixin(object): """Get the name of the parent bone, or None.""" return get_name(self.get_bone(bone_name).parent) - def set_bone_parent(self, bone_name, parent_name, use_connect=False): + def set_bone_parent(self, bone_name, parent_name, use_connect=False, inherit_scale=None): """Set the parent of the bone.""" eb = self.obj.data.edit_bones bone = eb[bone_name] if use_connect is not None: bone.use_connect = use_connect + if inherit_scale is not None: + bone.inherit_scale = inherit_scale bone.parent = (eb[parent_name] if parent_name else None) - def parent_bone_chain(self, bone_names, use_connect=None): + def parent_bone_chain(self, bone_names, use_connect=None, inherit_scale=None): """Link bones into a chain with parenting. First bone may be None.""" for parent, child in pairwise(bone_names): - self.set_bone_parent(child, parent, use_connect=use_connect) + self.set_bone_parent(child, parent, use_connect=use_connect, inherit_scale=inherit_scale) #============================================= # B-Bones diff --git a/rigify/utils/mechanism.py b/rigify/utils/mechanism.py index 8cd34624..46545096 100644 --- a/rigify/utils/mechanism.py +++ b/rigify/utils/mechanism.py @@ -38,7 +38,7 @@ def _set_default_attr(obj, options, attr, value): def make_constraint( owner, type, target=None, subtarget=None, *, insert_index=None, - space=None, track_axis=None, use_xyz=None, use_limit_xyz=None, + space=None, track_axis=None, use_xyz=None, use_limit_xyz=None, invert_xyz=None, **options): """ Creates and initializes constraint of the specified type for the owner bone. @@ -51,6 +51,7 @@ def make_constraint( track_axis : allows shorter X, Y, Z, -X, -Y, -Z notation use_xyz : list of 3 items is assigned to use_x, use_y and use_z options use_limit_xyz : list of 3 items is assigned to use_limit_x/y/z options + invert_xyz : list of 3 items is assigned to invert_x, invert_y and invert_z options min/max_x/y/z : a corresponding use_(min/max/limit)_(x/y/z) option is set to True Other keyword arguments are directly assigned to the constraint options. @@ -80,6 +81,9 @@ def make_constraint( if use_limit_xyz is not None: con.use_limit_x, con.use_limit_y, con.use_limit_z = use_limit_xyz[0:3] + if invert_xyz is not None: + con.invert_x, con.invert_y, con.invert_z = invert_xyz[0:3] + for key in ['min_x', 'max_x', 'min_y', 'max_y', 'min_z', 'max_z']: if key in options: _set_default_attr(con, options, 'use_'+key, True) diff --git a/rigify/utils/misc.py b/rigify/utils/misc.py index b0f79ea7..4d0fbad3 100644 --- a/rigify/utils/misc.py +++ b/rigify/utils/misc.py @@ -18,6 +18,7 @@ # <pep8 compliant> +import bpy import math import collections @@ -56,6 +57,28 @@ def angle_on_plane(plane, vec1, vec2): return angle * sign + +# Convert between a matrix and axis+roll representations. +# Re-export the C implementation internally used by bones. +matrix_from_axis_roll = bpy.types.Bone.MatrixFromAxisRoll +axis_roll_from_matrix = bpy.types.Bone.AxisRollFromMatrix + + +def matrix_from_axis_pair(y_axis, other_axis, axis_name): + assert axis_name in 'xz' + + y_axis = Vector(y_axis).normalized() + + if axis_name == 'x': + z_axis = Vector(other_axis).cross(y_axis).normalized() + x_axis = y_axis.cross(z_axis) + else: + x_axis = y_axis.cross(other_axis).normalized() + z_axis = x_axis.cross(y_axis) + + return Matrix((x_axis, y_axis, z_axis)).transposed() + + #============================================= # Color correction functions #============================================= diff --git a/rigify/utils/rig.py b/rigify/utils/rig.py index 41027c69..8c646ab5 100644 --- a/rigify/utils/rig.py +++ b/rigify/utils/rig.py @@ -235,8 +235,8 @@ def write_metarig(obj, layers=False, func_name="create", groups=False): for bone_name in bones: bone = arm.edit_bones[bone_name] code.append(" bone = arm.edit_bones.new(%r)" % bone.name) - code.append(" bone.head[:] = %.4f, %.4f, %.4f" % bone.head.to_tuple(4)) - code.append(" bone.tail[:] = %.4f, %.4f, %.4f" % bone.tail.to_tuple(4)) + code.append(" bone.head = %.4f, %.4f, %.4f" % bone.head.to_tuple(4)) + 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.parent: diff --git a/rigify/utils/widgets.py b/rigify/utils/widgets.py index 6d83264a..037d4118 100644 --- a/rigify/utils/widgets.py +++ b/rigify/utils/widgets.py @@ -160,6 +160,14 @@ def adjust_widget_axis(obj, axis='y', offset=0.0): vert.co = matrix @ vert.co +def adjust_widget_transform(obj, matrix): + """Adjust the generated widget by applying a world space correction matrix to the mesh.""" + if obj: + obmat = obj.matrix_basis + matrix = obmat.inverted() @ matrix @ obmat + obj.data.transform(matrix) + + def write_widget(obj): """ Write a mesh object as a python script for widget use. """ @@ -170,9 +178,9 @@ def write_widget(obj): # Vertices script += " verts = [" - for v in obj.data.vertices: - script += "(" + str(v.co[0]) + "*size, " + str(v.co[1]) + "*size, " + str(v.co[2]) + "*size)," - script += "\n " + for i, v in enumerate(obj.data.vertices): + script += "({:g}*size, {:g}*size, {:g}*size),".format(v.co[0], v.co[1], v.co[2]) + script += "\n " if i % 2 == 1 else " " script += "]\n" # Edges diff --git a/rigify/utils/widgets_basic.py b/rigify/utils/widgets_basic.py index bb60237c..a40806ad 100644 --- a/rigify/utils/widgets_basic.py +++ b/rigify/utils/widgets_basic.py @@ -127,3 +127,35 @@ def create_bone_widget(rig, bone_name, r1=0.1, l1=0.0, r2=0.04, l2=1.0, bone_tra mesh.update() +def create_pivot_widget(rig, bone_name, axis_size=1.0, cap_size=1.0, square=False, bone_transform_name=None): + """Creates a widget similar to Plain Axes empty, but with a cross or + a square on the end of each axis line. + """ + obj = create_widget(rig, bone_name, bone_transform_name) + if obj is not None: + axis = 0.5 * axis_size + cap = 0.05 * cap_size + if square: + verts = [(0, 0, -axis), (-axis, 0, 0), (0, 0, axis), (axis, 0, 0), (axis, cap, -cap), (axis, cap, cap), + (0, -axis, 0), (0, axis, 0), (cap, axis, cap), (cap, axis, -cap), (axis, -cap, -cap), (axis, -cap, cap), + (-cap, axis, cap), (-cap, axis, -cap), (-axis, cap, cap), (-axis, cap, -cap), (-axis, -cap, cap), (-axis, -cap, -cap), + (-cap, -axis, cap), (-cap, -axis, -cap), (cap, -axis, cap), (cap, -axis, -cap), (-cap, -cap, -axis), (-cap, cap, -axis), + (cap, -cap, -axis), (cap, cap, -axis), (-cap, cap, axis), (-cap, -cap, axis), (cap, cap, axis), (cap, -cap, axis) ] + edges = [(10, 4), (4, 5), (8, 9), (0, 2), (12, 8), (6, 7), (11, 10), (13, 12), (5, 11), (9, 13), + (3, 1), (14, 15), (16, 14), (17, 16), (15, 17), (18, 19), (20, 18), (21, 20), (19, 21), (22, 23), + (24, 22), (25, 24), (23, 25), (26, 27), (28, 26), (29, 28), (27, 29) ] + else: + verts = [(0, 0, -axis), (-axis, 0, 0), (0, 0, axis), (axis, 0, 0), (-cap, 0, -axis), (-axis, 0, -cap), + (-axis, 0, cap), (-cap, 0, axis), (cap, 0, axis), (axis, 0, cap), (axis, 0, -cap), (cap, 0, -axis), + (0, -axis, 0), (0, axis, 0), (0, -cap, -axis), (0, -axis, -cap), (0, -axis, cap), (0, -cap, axis), + (0, cap, axis), (0, axis, cap), (0, axis, -cap), (0, cap, -axis), (-axis, -cap, 0), (-cap, -axis, 0), + (cap, -axis, 0), (axis, -cap, 0), (axis, cap, 0), (cap, axis, 0), (-cap, axis, 0), (-axis, cap, 0) ] + edges = [(4, 0), (6, 1), (8, 2), (10, 3), (1, 5), (2, 7), (3, 9), (0, 11), (16, 12), (0, 21), + (2, 17), (20, 13), (12, 15), (0, 2), (18, 2), (13, 19), (12, 13), (1, 29), (22, 1), (3, 25), + (13, 27), (14, 0), (26, 3), (28, 13), (24, 12), (12, 23), (3, 1) ] + mesh = obj.data + mesh.from_pydata(verts, edges, []) + mesh.update() + return obj + else: + return None |