diff options
author | Joshua Leung <aligorith@gmail.com> | 2016-05-17 18:19:06 +0300 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2016-05-17 18:19:06 +0300 |
commit | 49aeee5a3dfa9fc0ae29e99f7c5c0cc0124e560e (patch) | |
tree | 49ace019e0509cd188f24d11c8f799ab676f6bbd /release | |
parent | 29a17d54da1f4b85a59487e032165bb44dc1b065 (diff) |
Bendy Bones: Advanced B-Bones for Easier + Simple Rigging
This commit/patch/branch brings a bunch of powerful new options for B-Bones and
for working with B-Bones, making it easier for animators to create their own
rigs, using fewer bones (which also means hopefully lighter + faster rigs ;)
This functionality was first demoed by Daniel at BConf15
Some highlights from this patch include:
* You can now directly control the shape of B-Bones using a series of properties
instead of being restricted to trying to indirectly control them through the
neighbouring bones. See the "Bendy Bones" panel...
* B-Bones can be shaped in EditMode to define a "curved rest pose" for the bone.
This is useful for things like eyebrows and mouths/eyelids
* You can now make B-Bones use custom bones as their reference bone handles,
instead of only using the parent/child bones. To do so, enable the
"Use Custom Reference Handles" toggle. If none are specified, then the BBone will
only use the Bendy Bone properties.
* Constraints Head/Tail option can now slide along the B-Bone shape, instead of
just linearly interpolating between the endpoints of the bone.
For more details, see:
* http://aligorith.blogspot.co.nz/2016/05/bendy-bones-dev-update.html
* http://aligorith.blogspot.co.nz/2016/05/an-in-depth-look-at-how-b-bones-work.html
-- Credits --
Original Idea: Daniel M Lara (pepeland)
Original Patch/Research: Jose Molina
Additional Development + Polish: Joshua Leung (aligorith)
Testing/Feedback: Daniel M Lara (pepeland), Juan Pablo Bouza (jpbouza)
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/modules/keyingsets_utils.py | 51 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_constraint.py | 3 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_data_bone.py | 102 | ||||
-rw-r--r-- | release/scripts/startup/keyingsets_builtins.py | 32 |
4 files changed, 168 insertions, 20 deletions
diff --git a/release/scripts/modules/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py index 03400edc904..375ee3feebe 100644 --- a/release/scripts/modules/keyingsets_utils.py +++ b/release/scripts/modules/keyingsets_utils.py @@ -28,11 +28,14 @@ __all__ = ( "RKS_POLL_selected_objects", "RKS_POLL_selected_bones", "RKS_POLL_selected_items", + "RKS_ITER_selected_object", + "RKS_ITER_selected_bones", "RKS_ITER_selected_item", "RKS_GEN_available", "RKS_GEN_location", "RKS_GEN_rotation", "RKS_GEN_scaling", + "RKS_GEN_bendy_bones", ) import bpy @@ -93,11 +96,17 @@ def RKS_ITER_selected_item(ksi, context, ks): ksi.generate(context, ks, ob) -# all select objects only +# all selected objects only def RKS_ITER_selected_objects(ksi, context, ks): for ob in context.selected_objects: ksi.generate(context, ks, ob) + +# all seelcted bones only +def RKS_ITER_selected_bones(ksi, context, ks): + for bone in context.selected_pose_bones: + ksi.generate(context, ks, bone) + ########################### # Generate Callbacks @@ -207,3 +216,43 @@ def RKS_GEN_scaling(ksi, context, ks, data): ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping) else: ks.paths.add(id_block, path) + +# ------ + +# Property identifiers for Bendy Bones +bbone_property_ids = ( + "bbone_curveinx", + "bbone_curveiny", + "bbone_curveoutx", + "bbone_curveouty", + + "bbone_rollin", + "bbone_rollout", + + "bbone_scalein", + "bbone_scaleout", + + # NOTE: These are in the nested bone struct + # Do it this way to force them to be included + # in whatever actions are being keyed here + "bone.bbone_in", + "bone.bbone_out", +) + +# Add Keying Set entries for bendy bones +def RKS_GEN_bendy_bones(ksi, context, ks, data): + # get id-block and path info + # NOTE: This assumes that we're dealing with a bone here... + id_block, base_path, grouping = get_transform_generators_base_info(data) + + # for each of the bendy bone properties, add a Keying Set entry for it... + for propname in bbone_property_ids: + # add the property name to the base path + path = path_add_property(base_path, propname) + + # add Keying Set entry for this... + if grouping: + ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping) + else: + ks.paths.add(id_block, path) + diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index ef0fc9c7c9f..4ca2f773dcc 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -65,9 +65,10 @@ class ConstraintButtonsPanel: layout.prop_search(con, "subtarget", con.target.data, "bones", text="Bone") if hasattr(con, "head_tail"): - row = layout.row() + row = layout.row(align=True) row.label(text="Head/Tail:") row.prop(con, "head_tail", text="") + row.prop(con, "use_bbone_shape", text="", icon='IPO_BEZIER') # XXX icon, and only when bone has segments? elif con.target.type in {'MESH', 'LATTICE'}: layout.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group") diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index 690c22c1440..99c0d7f37c1 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -146,6 +146,81 @@ class BONE_PT_transform_locks(BoneButtonsPanel, Panel): sub.prop(pchan, "lock_rotation_w", text="W") +class BONE_PT_curved(BoneButtonsPanel, Panel): + bl_label = "Bendy Bones" + #bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + ob = context.object + bone = context.bone + arm = context.armature + pchan = None + + if ob and bone: + pchan = ob.pose.bones[bone.name] + bbone = pchan + elif bone is None: + bone = context.edit_bone + bbone = bone + else: + bbone = bone + + layout = self.layout + layout.prop(bone, "bbone_segments", text="Segments") + + col = layout.column() + col.active = bone.bbone_segments > 1 + + row = col.row() + sub = row.column(align=True) + sub.label(text="Curve XY Offsets:") + sub.prop(bbone, "bbone_curveinx", text="In X") + sub.prop(bbone, "bbone_curveiny", text="In Y") + sub.prop(bbone, "bbone_curveoutx", text="Out X") + sub.prop(bbone, "bbone_curveouty", text="Out Y") + + sub = row.column(align=True) + sub.label("Roll:") + sub.prop(bbone, "bbone_rollin", text="In") + sub.prop(bbone, "bbone_rollout", text="Out") + sub.prop(bone, "use_endroll_as_inroll") + + row = col.row() + sub = row.column(align=True) + sub.label(text="Scale:") + sub.prop(bbone, "bbone_scalein", text="Scale In") + sub.prop(bbone, "bbone_scaleout", text="Scale Out") + + sub = row.column(align=True) + sub.label("Easing:") + if pchan: + # XXX: have these also be an overlay? + sub.prop(bbone.bone, "bbone_in", text="Ease In") + sub.prop(bbone.bone, "bbone_out", text="Ease Out") + else: + sub.prop(bone, "bbone_in", text="Ease In") + sub.prop(bone, "bbone_out", text="Ease Out") + + if pchan: + layout.separator() + + col = layout.column() + col.prop(pchan, "use_bbone_custom_handles") + + row = col.row() + row.active = pchan.use_bbone_custom_handles + + sub = row.column(align=True) + sub.label(text="In:") + sub.prop_search(pchan, "bbone_custom_handle_start", ob.pose, "bones", text="") + sub.prop(pchan, "use_bbone_relative_start_handle", text="Relative") + + sub = row.column(align=True) + sub.label(text="Out:") + sub.prop_search(pchan, "bbone_custom_handle_end", ob.pose, "bones", text="") + sub.prop(pchan, "use_bbone_relative_end_handle", text="Relative") + + class BONE_PT_relations(BoneButtonsPanel, Panel): bl_label = "Relations" @@ -193,6 +268,7 @@ class BONE_PT_relations(BoneButtonsPanel, Panel): sub.prop(bone, "use_local_location") + class BONE_PT_display(BoneButtonsPanel, Panel): bl_label = "Display" @@ -348,28 +424,18 @@ class BONE_PT_deform(BoneButtonsPanel, Panel): layout.active = bone.use_deform - split = layout.split() + row = layout.row() - col = split.column() + col = row.column(align=True) col.label(text="Envelope:") - - sub = col.column(align=True) - sub.prop(bone, "envelope_distance", text="Distance") - sub.prop(bone, "envelope_weight", text="Weight") + col.prop(bone, "envelope_distance", text="Distance") + col.prop(bone, "envelope_weight", text="Weight") col.prop(bone, "use_envelope_multiply", text="Multiply") - sub = col.column(align=True) - sub.label(text="Radius:") - sub.prop(bone, "head_radius", text="Head") - sub.prop(bone, "tail_radius", text="Tail") - - col = split.column() - col.label(text="Curved Bones:") - - sub = col.column(align=True) - sub.prop(bone, "bbone_segments", text="Segments") - sub.prop(bone, "bbone_in", text="Ease In") - sub.prop(bone, "bbone_out", text="Ease Out") + col = row.column(align=True) + col.label(text="Envelope Radius:") + col.prop(bone, "head_radius", text="Head") + col.prop(bone, "tail_radius", text="Tail") class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel): diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py index 6d52a81456b..195eaf823f4 100644 --- a/release/scripts/startup/keyingsets_builtins.py +++ b/release/scripts/startup/keyingsets_builtins.py @@ -175,6 +175,22 @@ class BUILTIN_KSI_RotScale(KeyingSetInfo): # ------------ +# Bendy Bones +class BUILTIN_KSI_BendyBones(KeyingSetInfo): + """Insert a keyframe for each of the BBone shape properties""" + bl_label = "BBone Shape" + + # poll - use callback for selected bones + poll = keyingsets_utils.RKS_POLL_selected_bones + + # iterator - use callback for selected bones + iterator = keyingsets_utils.RKS_ITER_selected_bones + + # generator - use generator for bendy bone properties + generate = keyingsets_utils.RKS_GEN_bendy_bones + +# ------------ + # VisualLocation class BUILTIN_KSI_VisualLoc(KeyingSetInfo): @@ -387,6 +403,9 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): ksi.doRot3d(ks, bone) ksi.doScale(ks, bone) + # bbone properties? + ksi.doBBone(context, ks, bone) + # custom props? ksi.doCustomProps(ks, bone) @@ -466,6 +485,19 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): # ---------------- + # bendy bone properties + def doBBone(ksi, context, ks, pchan): + bone = pchan.bone + + # This check is crude, but is the best we can do for now + # It simply adds all of these if the bbone has segments + # (and the bone is a control bone). This may lead to some + # false positives... + if bone.bbone_segments > 1: + keyingsets_utils.RKS_GEN_bendy_bones(ksi, context, ks, pchan) + + # ---------------- + # custom properties def doCustomProps(ksi, ks, bone): |