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 | |
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)
27 files changed, 829 insertions, 117 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): diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index c164cd542f3..cb282b46bec 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -79,12 +79,15 @@ typedef enum eAction_TransformFlags { ACT_TRANS_ROT = (1 << 1), /* scaling */ ACT_TRANS_SCALE = (1 << 2), - + + /* bbone shape - for all the parameters, provided one is set */ + ACT_TRANS_BBONE = (1 << 3), + /* strictly not a transform, but custom properties are also * quite often used in modern rigs */ - ACT_TRANS_PROP = (1 << 3), - + ACT_TRANS_PROP = (1 << 4), + /* all flags */ ACT_TRANS_ONLY = (ACT_TRANS_LOC | ACT_TRANS_ROT | ACT_TRANS_SCALE), ACT_TRANS_ALL = (ACT_TRANS_ONLY | ACT_TRANS_PROP) diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 6d00110e318..cc082c084ce 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -135,6 +135,7 @@ typedef struct Mat4 { float mat[4][4]; } Mat4; +void equalize_bbone_bezier(float *data, int desired); void b_bone_spline_setup(struct bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BBONE_SUBDIV]); /* like EBONE_VISIBLE */ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index b969c9ec787..3a31e4c36cf 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -493,6 +493,8 @@ bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name) unit_axis_angle(chan->rotAxis, &chan->rotAngle); chan->size[0] = chan->size[1] = chan->size[2] = 1.0f; + chan->scaleIn = chan->scaleOut = 1.0f; + chan->limitmin[0] = chan->limitmin[1] = chan->limitmin[2] = -180.0f; chan->limitmax[0] = chan->limitmax[1] = chan->limitmax[2] = 180.0f; chan->stiffness[0] = chan->stiffness[1] = chan->stiffness[2] = 0.0f; @@ -1280,6 +1282,18 @@ short action_get_item_transforms(bAction *act, Object *ob, bPoseChannel *pchan, } } + if ((curves) || (flags & ACT_TRANS_BBONE) == 0) { + /* bbone shape properties */ + pPtr = strstr(bPtr, "bbone_"); + if (pPtr) { + flags |= ACT_TRANS_BBONE; + + if (curves) + BLI_addtail(curves, BLI_genericNodeN(fcu)); + continue; + } + } + if ((curves) || (flags & ACT_TRANS_PROP) == 0) { /* custom properties only */ pPtr = strstr(bPtr, "[\""); /* extra '"' comment here to keep my texteditor functionlist working :) */ diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 1a3afbb876e..5c95cde4d8d 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -429,7 +429,7 @@ int bone_autoside_name(char name[MAXBONENAME], int UNUSED(strip_number), short a /* ************* B-Bone support ******************* */ /* data has MAX_BBONE_SUBDIV+1 interpolated points, will become desired amount with equal distances */ -static void equalize_bezier(float *data, int desired) +void equalize_bbone_bezier(float *data, int desired) { float *fp, totdist, ddist, dist, fac1, fac2; float pdist[MAX_BBONE_SUBDIV + 1]; @@ -499,13 +499,21 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB hlength1 = bone->ease1 * length * 0.390464f; /* 0.5f * sqrt(2) * kappa, the handle length for near-perfect circles */ hlength2 = bone->ease2 * length * 0.390464f; - /* evaluate next and prev bones */ - if (bone->flag & BONE_CONNECTED) - prev = pchan->parent; - else - prev = NULL; + /* get "next" and "prev" bones - these are used for handle calculations */ + if (pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) { + /* use the provided bones as the next/prev - leave blank to eliminate this effect altogether */ + prev = pchan->bbone_prev; + next = pchan->bbone_next; + } + else { + /* evaluate next and prev bones */ + if (bone->flag & BONE_CONNECTED) + prev = pchan->parent; + else + prev = NULL; - next = pchan->child; + next = pchan->child; + } /* find the handle points, since this is inside bone space, the * first point = (0, 0, 0) @@ -525,10 +533,27 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB float difmat[4][4], result[3][3], imat3[3][3]; /* transform previous point inside this bone space */ - if (rest) - copy_v3_v3(h1, prev->bone->arm_head); - else - copy_v3_v3(h1, prev->pose_head); + if ((pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) && + (pchan->bboneflag & PCHAN_BBONE_CUSTOM_START_REL)) + { + /* Use delta movement (from restpose), and apply this relative to the current bone's head */ + if (rest) { + /* in restpose, arm_head == pose_head */ + h1[0] = h1[1] = h1[2] = 0.0f; + } + else { + float delta[3]; + sub_v3_v3v3(delta, prev->pose_head, prev->bone->arm_head); + sub_v3_v3v3(h1, pchan->pose_head, delta); + } + } + else { + /* Use bone head as absolute position */ + if (rest) + copy_v3_v3(h1, prev->bone->arm_head); + else + copy_v3_v3(h1, prev->pose_head); + } mul_m4_v3(imat, h1); if (prev->bone->segments > 1) { @@ -564,10 +589,27 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB float difmat[4][4], result[3][3], imat3[3][3]; /* transform next point inside this bone space */ - if (rest) - copy_v3_v3(h2, next->bone->arm_tail); - else - copy_v3_v3(h2, next->pose_tail); + if ((pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES) && + (pchan->bboneflag & PCHAN_BBONE_CUSTOM_END_REL)) + { + /* Use delta movement (from restpose), and apply this relative to the current bone's tail */ + if (rest) { + /* in restpose, arm_tail == pose_tail */ + h2[0] = h2[1] = h2[2] = 0.0f; + } + else { + float delta[3]; + sub_v3_v3v3(delta, next->pose_tail, next->bone->arm_tail); + add_v3_v3v3(h2, pchan->pose_tail, delta); + } + } + else { + /* Use bone tail as absolute position */ + if (rest) + copy_v3_v3(h2, next->bone->arm_tail); + else + copy_v3_v3(h2, next->pose_tail); + } mul_m4_v3(imat, h2); /* if next bone is B-bone too, use average handle direction */ @@ -601,6 +643,50 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB roll2 = 0.0; } + /* Add effects from bbone properties over the top + * - These properties allow users to hand-animate the + * bone curve/shape, without having to resort to using + * extra bones + * - The "bone" level offsets are for defining the restpose + * shape of the bone (e.g. for curved eyebrows for example). + * -> In the viewport, it's needed to define what the rest pose + * looks like + * -> For "rest == 0", we also still need to have it present + * so that we can "cancel out" this restpose when it comes + * time to deform some geometry, it won't cause double transforms. + * - The "pchan" level offsets are the ones that animators actually + * end up animating + */ + { + /* add extra rolls */ + roll1 += bone->roll1 + (!rest ? pchan->roll1 : 0.0f); + roll2 += bone->roll2 + (!rest ? pchan->roll2 : 0.0f); + + if (bone->flag & BONE_ADD_PARENT_END_ROLL) { + if (prev) { + if (prev->bone) + roll1 += prev->bone->roll2; + + if (!rest) + roll1 += prev->roll2; + } + } + + /* extra curve x / y */ + /* NOTE: Scale correction factors here are to compensate for some random floating-point glitches + * when scaling up the bone or it's parent by a factor of approximately 8.15/6, which results + * in the bone length getting scaled up too (from 1 to 8), causing the curve to flatten out. + */ + const float xscale_correction = (do_scale) ? scale[0] : 1.0f; + const float yscale_correction = (do_scale) ? scale[2] : 1.0f; + + h1[0] += (bone->curveInX + (!rest ? pchan->curveInX : 0.0f)) * xscale_correction; + h1[2] += (bone->curveInY + (!rest ? pchan->curveInY : 0.0f)) * yscale_correction; + + h2[0] += (bone->curveOutX + (!rest ? pchan->curveOutX : 0.0f)) * xscale_correction; + h2[2] += (bone->curveOutY + (!rest ? pchan->curveOutY : 0.0f)) * yscale_correction; + } + /* make curve */ if (bone->segments > MAX_BBONE_SUBDIV) bone->segments = MAX_BBONE_SUBDIV; @@ -610,20 +696,45 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB BKE_curve_forward_diff_bezier(0.0f, h1[2], h2[2], 0.0f, data[0] + 2, MAX_BBONE_SUBDIV, 4 * sizeof(float)); BKE_curve_forward_diff_bezier(roll1, roll1 + 0.390464f * (roll2 - roll1), roll2 - 0.390464f * (roll2 - roll1), roll2, data[0] + 3, MAX_BBONE_SUBDIV, 4 * sizeof(float)); - equalize_bezier(data[0], bone->segments); /* note: does stride 4! */ + equalize_bbone_bezier(data[0], bone->segments); /* note: does stride 4! */ /* make transformation matrices for the segments for drawing */ for (a = 0, fp = data[0]; a < bone->segments; a++, fp += 4) { sub_v3_v3v3(h1, fp + 4, fp); vec_roll_to_mat3(h1, fp[3], mat3); /* fp[3] is roll */ - + copy_m4_m3(result_array[a].mat, mat3); copy_v3_v3(result_array[a].mat[3], fp); - + if (do_scale) { /* correct for scaling when this matrix is used in scaled space */ mul_m4_series(result_array[a].mat, iscalemat, result_array[a].mat, scalemat); } + + /* BBone scale... */ + { + const int num_segments = bone->segments; + + const float scaleIn = bone->scaleIn * (!rest ? pchan->scaleIn : 1.0f); + const float scaleFactorIn = 1.0f + (scaleIn - 1.0f) * ((float)(num_segments - a) / (float)num_segments); + + const float scaleOut = bone->scaleOut * (!rest ? pchan->scaleOut : 1.0f); + const float scaleFactorOut = 1.0f + (scaleOut - 1.0f) * ((float)(a + 1) / (float)num_segments); + + const float scalefac = scaleFactorIn * scaleFactorOut; + float bscalemat[4][4], bscale[3]; + + bscale[0] = scalefac; + bscale[1] = 1.0f; + bscale[2] = scalefac; + + size_to_mat4(bscalemat, bscale); + + /* Note: don't multiply by inverse scale mat here, as it causes problems with scaling shearing and breaking segment chains */ + /*mul_m4_series(result_array[a].mat, ibscalemat, result_array[a].mat, bscalemat);*/ + mul_m4_series(result_array[a].mat, result_array[a].mat, bscalemat); + } + } } @@ -669,7 +780,6 @@ static void pchan_b_bone_defmats(bPoseChannel *pchan, bPoseChanDeform *pdef_info float tmat[4][4]; invert_m4_m4(tmat, b_bone_rest[a].mat); - mul_m4_series(b_bone_mats[a + 1].mat, pchan->chan_mat, bone->arm_mat, b_bone[a].mat, tmat, b_bone_mats[0].mat); if (use_quaternion) @@ -683,10 +793,10 @@ static void b_bone_deform(bPoseChanDeform *pdef_info, Bone *bone, float co[3], D float (*mat)[4] = b_bone[0].mat; float segment, y; int a; - + /* need to transform co back to bonespace, only need y */ y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1]; - + /* now calculate which of the b_bones are deforming this */ segment = bone->length / ((float)bone->segments); a = (int)(y / segment); diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 7144e25ba7f..a591d536b43 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -535,7 +535,7 @@ static void contarget_get_lattice_mat(Object *ob, const char *substring, float m /* generic function to get the appropriate matrix for most target cases */ /* The cases where the target can be object data have not been implemented */ -static void constraint_target_to_mat4(Object *ob, const char *substring, float mat[4][4], short from, short to, float headtail) +static void constraint_target_to_mat4(Object *ob, const char *substring, float mat[4][4], short from, short to, short flag, float headtail) { /* Case OBJECT */ if (substring[0] == '\0') { @@ -573,6 +573,58 @@ static void constraint_target_to_mat4(Object *ob, const char *substring, float m /* skip length interpolation if set to head */ mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); } + else if ((pchan->bone) && (pchan->bone->segments > 1) && (flag & CONSTRAINT_BBONE_SHAPE)) { + /* use point along bbone */ + Mat4 bbone[MAX_BBONE_SUBDIV]; + float tempmat[4][4]; + float loc[3], fac; + + /* get bbone segments */ + b_bone_spline_setup(pchan, 0, bbone); + + /* figure out which segment(s) the headtail value falls in */ + fac = (float)pchan->bone->segments * headtail; + + if (fac >= pchan->bone->segments - 1) { + /* special case: end segment doesn't get created properly... */ + float pt[3], sfac; + int index; + + /* bbone points are in bonespace, so need to move to posespace first */ + index = pchan->bone->segments - 1; + mul_v3_m4v3(pt, pchan->pose_mat, bbone[index].mat[3]); + + /* interpolate between last segment point and the endpoint */ + sfac = fac - (float)(pchan->bone->segments - 1); /* fac is just the "leftover" between penultimate and last points */ + interp_v3_v3v3(loc, pt, pchan->pose_tail, sfac); + } + else { + /* get indices for finding interpolating between points along the bbone */ + float pt_a[3], pt_b[3], pt[3]; + int index_a, index_b; + + index_a = floorf(fac); + CLAMP(index_a, 0, MAX_BBONE_SUBDIV - 1); + + index_b = ceilf(fac); + CLAMP(index_b, 0, MAX_BBONE_SUBDIV - 1); + + /* interpolate between these points */ + copy_v3_v3(pt_a, bbone[index_a].mat[3]); + copy_v3_v3(pt_b, bbone[index_b].mat[3]); + + interp_v3_v3v3(pt, pt_a, pt_b, fac - floorf(fac)); + + /* move the point from bone local space to pose space... */ + mul_v3_m4v3(loc, pchan->pose_mat, pt); + } + + /* use interpolated distance for subtarget */ + copy_m4_m4(tempmat, pchan->pose_mat); + copy_v3_v3(tempmat[3], loc); + + mul_m4_m4m4(mat, ob->obmat, tempmat); + } else { float tempmat[4][4], loc[3]; @@ -634,7 +686,7 @@ static bConstraintTypeInfo CTI_CONSTRNAME = { static void default_get_tarmat(bConstraint *con, bConstraintOb *UNUSED(cob), bConstraintTarget *ct, float UNUSED(ctime)) { if (VALID_CONS_TARGET(ct)) - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); else if (ct) unit_m4(ct->matrix); } @@ -1102,7 +1154,7 @@ static void kinematic_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstrai bKinematicConstraint *data = con->data; if (VALID_CONS_TARGET(ct)) - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); else if (ct) { if (data->flag & CONSTRAINT_IK_AUTO) { Object *ob = cob->ob; @@ -1985,7 +2037,7 @@ static void pycon_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstraintTa /* firstly calculate the matrix the normal way, then let the py-function override * this matrix if it needs to do so */ - constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, ct->matrix, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); /* only execute target calculation if allowed */ #ifdef WITH_PYTHON @@ -2097,7 +2149,7 @@ static void actcon_get_tarmat(bConstraint *con, bConstraintOb *cob, bConstraintT unit_m4(ct->matrix); /* get the transform matrix of the target */ - constraint_target_to_mat4(ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->headtail); + constraint_target_to_mat4(ct->tar, ct->subtarget, tempmat, CONSTRAINT_SPACE_WORLD, ct->space, con->flag, con->headtail); /* determine where in transform range target is */ /* data->type is mapped as follows for backwards compatibility: diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 678fd84de94..d6e2f237be9 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4926,6 +4926,9 @@ static void direct_link_pose(FileData *fd, bPose *pose) pchan->child = newdataadr(fd, pchan->child); pchan->custom_tx = newdataadr(fd, pchan->custom_tx); + pchan->bbone_prev = newdataadr(fd, pchan->bbone_prev); + pchan->bbone_next = newdataadr(fd, pchan->bbone_next); + direct_link_constraints(fd, &pchan->constraints); pchan->prop = newdataadr(fd, pchan->prop); diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index efd167d49d5..58542d05879 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -34,6 +34,7 @@ /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW +#include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" @@ -161,6 +162,16 @@ static void do_version_action_editor_properties_region(ListBase *regionbase) } } +static void do_version_bones_super_bbone(ListBase *lb) +{ + for (Bone *bone = lb->first; bone; bone = bone->next) { + bone->scaleIn = 1.0f; + bone->scaleOut = 1.0f; + + do_version_bones_super_bbone(&bone->childbase); + } +} + void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) { if (!MAIN_VERSION_ATLEAST(main, 270, 0)) { @@ -1151,4 +1162,32 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } } + + if (!MAIN_VERSION_ATLEAST(main, 277, 2)) { + if (!DNA_struct_elem_find(fd->filesdna, "Bone", "float", "scaleIn")) { + for (bArmature *arm = main->armature.first; arm; arm = arm->id.next) { + do_version_bones_super_bbone(&arm->bonebase); + } + } + if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "scaleIn")) { + for (Object *ob = main->object.first; ob; ob = ob->id.next) { + if (ob->pose) { + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + /* see do_version_bones_super_bbone()... */ + pchan->scaleIn = 1.0f; + pchan->scaleOut = 1.0f; + + /* also make sure some legacy (unused for over a decade) flags are unset, + * so that we can reuse them for stuff that matters now... + * (i.e. POSE_IK_MAT, (unknown/unused x 4), POSE_HAS_IK) + * + * These seem to have been runtime flags used by the IK solver, but that stuff + * should be able to be recalculated automatically anyway, so it should be fine. + */ + pchan->flag &= ~((1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8)); + } + } + } + } + } } diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 6afc5a357c8..847b45d612c 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -83,6 +83,15 @@ EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name) bone->segments = 1; bone->layer = arm->layer; + bone->roll1 = 0.0f; + bone->roll2 = 0.0f; + bone->curveInX = 0.0f; + bone->curveInY = 0.0f; + bone->curveOutX = 0.0f; + bone->curveOutY = 0.0f; + bone->scaleIn = 1.0f; + bone->scaleOut = 1.0f; + return bone; } @@ -890,6 +899,16 @@ static int armature_extrude_exec(bContext *C, wmOperator *op) newbone->segments = 1; newbone->layer = ebone->layer; + newbone->roll1 = ebone->roll1; + newbone->roll2 = ebone->roll2; + newbone->curveInX = ebone->curveInX; + newbone->curveInY = ebone->curveInY; + newbone->curveOutX = ebone->curveOutX; + newbone->curveOutY = ebone->curveOutY; + newbone->scaleIn = ebone->scaleIn; + newbone->scaleOut = ebone->scaleOut; + + BLI_strncpy(newbone->name, ebone->name, sizeof(newbone->name)); if (flipbone && forked) { // only set if mirror edit diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index ac150b9af74..02aefce3464 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -170,6 +170,11 @@ typedef struct tPChanFCurveLink { float oldangle; float oldaxis[3]; + float roll1, roll2; /* old bbone values (to be restored along with the transform properties) */ + float curveInX, curveInY; /* (NOTE: we haven't renamed these this time, as their names are already long enough) */ + float curveOutX, curveOutY; + float scaleIn, scaleOut; + struct IDProperty *oldprops; /* copy of custom properties at start of operator (to be restored before each modal step) */ } tPChanFCurveLink; diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 85c6835a8bb..d73536e5ba7 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -456,7 +456,16 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone eBone->rad_tail = curBone->rad_tail; eBone->segments = curBone->segments; eBone->layer = curBone->layer; - + + eBone->roll1 = curBone->roll1; + eBone->roll2 = curBone->roll2; + eBone->curveInX = curBone->curveInX; + eBone->curveInY = curBone->curveInY; + eBone->curveOutX = curBone->curveOutX; + eBone->curveOutY = curBone->curveOutY; + eBone->scaleIn = curBone->scaleIn; + eBone->scaleOut = curBone->scaleOut; + if (curBone->prop) eBone->prop = IDP_CopyProperty(curBone->prop); @@ -611,7 +620,17 @@ void ED_armature_from_edit(bArmature *arm) newBone->rad_tail = eBone->rad_tail; newBone->segments = eBone->segments; newBone->layer = eBone->layer; - + + newBone->roll1 = eBone->roll1; + newBone->roll2 = eBone->roll2; + newBone->curveInX = eBone->curveInX; + newBone->curveInY = eBone->curveInY; + newBone->curveOutX = eBone->curveOutX; + newBone->curveOutY = eBone->curveOutY; + newBone->scaleIn = eBone->scaleIn; + newBone->scaleOut = eBone->scaleOut; + + if (eBone->prop) newBone->prop = IDP_CopyProperty(eBone->prop); } diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c index 7c09ad49f35..fa7bf6e7ad4 100644 --- a/source/blender/editors/armature/editarmature_retarget.c +++ b/source/blender/editors/armature/editarmature_retarget.c @@ -1451,6 +1451,15 @@ static EditBone *add_editbonetolist(char *name, ListBase *list) bone->segments = 1; bone->layer = 1; //arm->layer; + bone->roll1 = 0.0f; + bone->roll2 = 0.0f; + bone->curveInX = 0.0f; + bone->curveInY = 0.0f; + bone->curveOutX = 0.0f; + bone->curveOutY = 0.0f; + bone->scaleIn = 1.0f; + bone->scaleOut = 1.0f; + return bone; } #endif diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 9ef46c63f0f..cd0ea23e2d3 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -303,8 +303,8 @@ static void pose_slide_apply_vec3(tPoseSlideOp *pso, tPChanFCurveLink *pfl, floa MEM_freeN(path); } -/* helper for apply() - perform sliding for custom properties */ -static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) +/* helper for apply() - perform sliding for custom properties or bbone properties */ +static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl, const char prop_prefix[]) { PointerRNA ptr = {{NULL}}; LinkData *ld; @@ -313,8 +313,10 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) /* setup pointer RNA for resolving paths */ RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr); - /* custom properties are just denoted using ["..."][etc.] after the end of the base path, - * so just check for opening pair after the end of the path + /* - custom properties are just denoted using ["..."][etc.] after the end of the base path, + * so just check for opening pair after the end of the path + * - bbone properties are similar, but they always start with a prefix "bbone_*", + * so a similar method should work here for those too */ for (ld = pfl->fcurves.first; ld; ld = ld->next) { FCurve *fcu = (FCurve *)ld->data; @@ -328,7 +330,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) * - pPtr is the chunk of the path which is left over */ bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len; - pPtr = strstr(bPtr, "[\""); /* dummy " for texteditor bugs */ + pPtr = strstr(bPtr, prop_prefix); if (pPtr) { /* use RNA to try and get a handle on this property, then, assuming that it is just @@ -517,9 +519,16 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso) } } + if (pchan->flag & POSE_BBONE_SHAPE) { + /* bbone properties - they all start a "bbone_" prefix */ + pose_slide_apply_props(pso, pfl, "bbone_"); + } + if (pfl->oldprops) { - /* not strictly a transform, but contributes to the pose produced in many rigs */ - pose_slide_apply_props(pso, pfl); + /* not strictly a transform, but custom properties contribute to the pose produced in many rigs + * (e.g. the facial rigs used in Sintel) + */ + pose_slide_apply_props(pso, pfl, "[\""); /* dummy " for texteditor bugs */ } } diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 01e16df9f08..df906a3638a 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -367,10 +367,26 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bo axis_angle_to_quat(pchan->quat, chan->rotAxis, pchan->rotAngle); } + /* B-Bone posing options should also be included... */ + pchan->curveInX = chan->curveInX; + pchan->curveInY = chan->curveInY; + pchan->curveOutX = chan->curveOutX; + pchan->curveOutY = chan->curveOutY; + + pchan->roll1 = chan->roll1; + pchan->roll2 = chan->roll2; + pchan->scaleIn = chan->scaleIn; + pchan->scaleOut = chan->scaleOut; + /* paste flipped pose? */ if (flip) { pchan->loc[0] *= -1; + pchan->curveInX *= -1; + pchan->curveOutX *= -1; + pchan->roll1 *= -1; // XXX? + pchan->roll2 *= -1; // XXX? + /* has to be done as eulers... */ if (pchan->rotmode > 0) { pchan->eul[1] *= -1; @@ -540,6 +556,9 @@ static void pchan_clear_scale(bPoseChannel *pchan) pchan->size[1] = 1.0f; if ((pchan->protectflag & OB_LOCK_SCALEZ) == 0) pchan->size[2] = 1.0f; + + pchan->scaleIn = 1.0f; + pchan->scaleOut = 1.0f; } /* clear location of pose-channel */ @@ -650,6 +669,15 @@ static void pchan_clear_rot(bPoseChannel *pchan) zero_v3(pchan->eul); } } + + /* Clear also Bendy Bone stuff - Roll is obvious, but Curve X/Y stuff is also kindof rotational in nature... */ + pchan->roll1 = 0.0f; + pchan->roll2 = 0.0f; + + pchan->curveInX = 0.0f; + pchan->curveInY = 0.0f; + pchan->curveOutX = 0.0f; + pchan->curveOutY = 0.0f; } /* clear loc/rot/scale of pose-channel */ diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c index 2ba1eedd33b..b960bec3603 100644 --- a/source/blender/editors/armature/pose_utils.c +++ b/source/blender/editors/armature/pose_utils.c @@ -71,7 +71,7 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a ListBase curves = {NULL, NULL}; int transFlags = action_get_item_transforms(act, ob, pchan, &curves); - pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE); + pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); /* check if any transforms found... */ if (transFlags) { @@ -96,6 +96,8 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a pchan->flag |= POSE_ROT; if (transFlags & ACT_TRANS_SCALE) pchan->flag |= POSE_SIZE; + if (transFlags & ACT_TRANS_BBONE) + pchan->flag |= POSE_BBONE_SHAPE; /* store current transforms */ copy_v3_v3(pfl->oldloc, pchan->loc); @@ -105,6 +107,16 @@ static void fcurves_to_pchan_links_get(ListBase *pfLinks, Object *ob, bAction *a copy_v3_v3(pfl->oldaxis, pchan->rotAxis); pfl->oldangle = pchan->rotAngle; + /* store current bbone values */ + pfl->roll1 = pchan->roll1; + pfl->roll2 = pchan->roll2; + pfl->curveInX = pchan->curveInX; + pfl->curveInY = pchan->curveInY; + pfl->curveOutX = pchan->curveOutX; + pfl->curveOutY = pchan->curveOutY; + pfl->scaleIn = pchan->scaleIn; + pfl->scaleOut = pchan->scaleOut; + /* make copy of custom properties */ if (pchan->prop && (transFlags & ACT_TRANS_PROP)) pfl->oldprops = IDP_CopyProperty(pchan->prop); @@ -133,6 +145,7 @@ void poseAnim_mapping_get(bContext *C, ListBase *pfLinks, Object *ob, bAction *a fcurves_to_pchan_links_get(pfLinks, ob, act, pchan); } CTX_DATA_END; + } } @@ -199,6 +212,16 @@ void poseAnim_mapping_reset(ListBase *pfLinks) copy_v3_v3(pchan->rotAxis, pfl->oldaxis); pchan->rotAngle = pfl->oldangle; + /* store current bbone values */ + pchan->roll1 = pfl->roll1; + pchan->roll2 = pfl->roll2; + pchan->curveInX = pfl->curveInX; + pchan->curveInY = pfl->curveInY; + pchan->curveOutX = pfl->curveOutX; + pchan->curveOutY = pfl->curveOutY; + pchan->scaleIn = pfl->scaleIn; + pchan->scaleOut = pfl->scaleOut; + /* just overwrite values of properties from the stored copies (there should be some) */ if (pfl->oldprops) IDP_SyncGroupValues(pfl->pchan->prop, pfl->oldprops); diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 15c68378b9a..904132b8876 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -74,7 +74,10 @@ typedef struct EditBone { float xwidth, length, zwidth; /* put them in order! transform uses this as scale */ float ease1, ease2; float rad_head, rad_tail; - + float roll1, roll2; + float curveOutX, curveOutY; + float curveInX, curveInY; + float scaleIn, scaleOut; float oldlength; /* for envelope scaling */ short segments; diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index 5cda721be44..f7c1e2ee981 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -53,6 +53,7 @@ #include "BKE_global.h" #include "BKE_modifier.h" #include "BKE_nla.h" +#include "BKE_curve.h" #include "BIF_gl.h" @@ -1093,20 +1094,101 @@ static void draw_line_bone(int armflag, int boneflag, short constflag, unsigned glPopMatrix(); } -static void draw_b_bone_boxes(const short dt, bPoseChannel *pchan, float xwidth, float length, float zwidth) +/* A partial copy of b_bone_spline_setup(), with just the parts for previewing editmode curve settings + * + * This assumes that prev/next bones don't have any impact (since they should all still be in the "straight" + * position here anyway), and that we can simply apply the bbone settings to get the desired effect... + */ +static void ebone_spline_preview(EditBone *ebone, Mat4 result_array[MAX_BBONE_SUBDIV]) +{ + float h1[3], h2[3], length, hlength1, hlength2, roll1 = 0.0f, roll2 = 0.0f; + float mat3[3][3]; + float data[MAX_BBONE_SUBDIV + 1][4], *fp; + int a; + + length = ebone->length; + + hlength1 = ebone->ease1 * length * 0.390464f; /* 0.5f * sqrt(2) * kappa, the handle length for near-perfect circles */ + hlength2 = ebone->ease2 * length * 0.390464f; + + /* find the handle points, since this is inside bone space, the + * first point = (0, 0, 0) + * last point = (0, length, 0) + * + * we also just apply all the "extra effects", since they're the whole reason we're doing this... + */ + h1[0] = ebone->curveInX; + h1[1] = hlength1; + h1[2] = ebone->curveInY; + roll1 = ebone->roll1; + + h2[0] = ebone->curveOutX; + h2[1] = -hlength2; + h2[2] = ebone->curveOutY; + roll2 = ebone->roll2; + + /* make curve */ + if (ebone->segments > MAX_BBONE_SUBDIV) + ebone->segments = MAX_BBONE_SUBDIV; + + BKE_curve_forward_diff_bezier(0.0f, h1[0], h2[0], 0.0f, data[0], MAX_BBONE_SUBDIV, 4 * sizeof(float)); + BKE_curve_forward_diff_bezier(0.0f, h1[1], length + h2[1], length, data[0] + 1, MAX_BBONE_SUBDIV, 4 * sizeof(float)); + BKE_curve_forward_diff_bezier(0.0f, h1[2], h2[2], 0.0f, data[0] + 2, MAX_BBONE_SUBDIV, 4 * sizeof(float)); + BKE_curve_forward_diff_bezier(roll1, roll1 + 0.390464f * (roll2 - roll1), roll2 - 0.390464f * (roll2 - roll1), roll2, data[0] + 3, MAX_BBONE_SUBDIV, 4 * sizeof(float)); + + equalize_bbone_bezier(data[0], ebone->segments); /* note: does stride 4! */ + + /* make transformation matrices for the segments for drawing */ + for (a = 0, fp = data[0]; a < ebone->segments; a++, fp += 4) { + sub_v3_v3v3(h1, fp + 4, fp); + vec_roll_to_mat3(h1, fp[3], mat3); /* fp[3] is roll */ + + copy_m4_m3(result_array[a].mat, mat3); + copy_v3_v3(result_array[a].mat[3], fp); + + /* "extra" scale facs... */ + { + const int num_segments = ebone->segments; + + const float scaleFactorIn = 1.0f + (ebone->scaleIn - 1.0f) * ((float)(num_segments - a) / (float)num_segments); + const float scaleFactorOut = 1.0f + (ebone->scaleOut - 1.0f) * ((float)(a + 1) / (float)num_segments); + + const float scalefac = scaleFactorIn * scaleFactorOut; + float bscalemat[4][4], bscale[3]; + + bscale[0] = scalefac; + bscale[1] = 1.0f; + bscale[2] = scalefac; + + size_to_mat4(bscalemat, bscale); + + /* Note: don't multiply by inverse scale mat here, as it causes problems with scaling shearing and breaking segment chains */ + mul_m4_series(result_array[a].mat, result_array[a].mat, bscalemat); + } + } +} + +static void draw_b_bone_boxes(const short dt, bPoseChannel *pchan, EditBone *ebone, float xwidth, float length, float zwidth) { int segments = 0; if (pchan) segments = pchan->bone->segments; + else if (ebone) + segments = ebone->segments; - if ((segments > 1) && (pchan)) { + if (segments > 1) { float dlen = length / (float)segments; Mat4 bbone[MAX_BBONE_SUBDIV]; int a; - - b_bone_spline_setup(pchan, 0, bbone); - + + if (pchan) { + b_bone_spline_setup(pchan, 0, bbone); + } + else if (ebone) { + ebone_spline_preview(ebone, bbone); + } + for (a = 0; a < segments; a++) { glPushMatrix(); glMultMatrixf(bbone[a].mat); @@ -1177,7 +1259,7 @@ static void draw_b_bone(const short dt, int armflag, int boneflag, short constfl else UI_ThemeColor(TH_BONE_SOLID); - draw_b_bone_boxes(OB_SOLID, pchan, xwidth, length, zwidth); + draw_b_bone_boxes(OB_SOLID, pchan, ebone, xwidth, length, zwidth); /* disable solid drawing */ GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); @@ -1190,7 +1272,7 @@ static void draw_b_bone(const short dt, int armflag, int boneflag, short constfl if (set_pchan_glColor(PCHAN_COLOR_CONSTS, boneflag, constflag)) { glEnable(GL_BLEND); - draw_b_bone_boxes(OB_SOLID, pchan, xwidth, length, zwidth); + draw_b_bone_boxes(OB_SOLID, pchan, ebone, xwidth, length, zwidth); glDisable(GL_BLEND); } @@ -1200,7 +1282,7 @@ static void draw_b_bone(const short dt, int armflag, int boneflag, short constfl } } - draw_b_bone_boxes(OB_WIRE, pchan, xwidth, length, zwidth); + draw_b_bone_boxes(OB_WIRE, pchan, ebone, xwidth, length, zwidth); } } diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 96d7ec3128c..4d82e4528d6 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -199,7 +199,8 @@ typedef struct bPoseChannel { char constflag; /* for quick detecting which constraints affect this channel */ char selectflag; /* copy of bone flag, so you can work with library armatures, not for runtime use */ char drawflag; - char pad0[5]; + char bboneflag; + char pad0[4]; struct Bone *bone; /* set on read file or rebuild pose */ struct bPoseChannel *parent; /* set on read file or rebuild pose */ @@ -242,7 +243,16 @@ typedef struct bPoseChannel { float ikstretch; float ikrotweight; /* weight of joint rotation constraint */ float iklinweight; /* weight of joint stretch constraint */ - + + /* curved bones settings - these are for animating, and are applied on top of the copies in pchan->bone */ + float roll1, roll2; + float curveInX, curveInY; + float curveOutX, curveOutY; + float scaleIn, scaleOut; + + struct bPoseChannel *bbone_prev; /* next/prev bones to use as handle references when calculating bbones (optional) */ + struct bPoseChannel *bbone_next; + void *temp; /* use for outliner */ } bPoseChannel; @@ -253,17 +263,17 @@ typedef enum ePchan_Flag { POSE_LOC = (1 << 0), POSE_ROT = (1 << 1), POSE_SIZE = (1 << 2), - /* old IK/cache stuff... */ -#if 0 - POSE_IK_MAT = (1 << 3), - POSE_UNUSED2 = (1 << 4), - POSE_UNUSED3 = (1 << 5), - POSE_UNUSED4 = (1 << 6), - POSE_UNUSED5 = (1 << 7), - /* has Standard IK */ - POSE_HAS_IK = (1 << 8), -#endif - /* IK/Pose solving*/ + + /* old IK/cache stuff + * - used to be here from (1 << 3) to (1 << 8) + * but has been repurposed since 2.77.2 + * as they haven't been used in over 10 years + */ + + /* has BBone deforms */ + POSE_BBONE_SHAPE = (1 << 3), + + /* IK/Pose solving */ POSE_CHAIN = (1 << 9), POSE_DONE = (1 << 10), /* visualization */ @@ -318,6 +328,16 @@ typedef enum ePchan_DrawFlag { #define PCHAN_CUSTOM_DRAW_SIZE(pchan) \ (pchan)->custom_scale * (((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length) +/* PoseChannel->bboneflag */ +typedef enum ePchan_BBoneFlag { + /* Use custom reference bones (for roll and handle alignment), instead of immediate neighbours */ + PCHAN_BBONE_CUSTOM_HANDLES = (1 << 1), + /* Evaluate start handle as being "relative" */ + PCHAN_BBONE_CUSTOM_START_REL = (1 << 2), + /* Evaluate end handle as being "relative" */ + PCHAN_BBONE_CUSTOM_END_REL = (1 << 3), +} ePchan_BBoneFlag; + /* PoseChannel->rotmode and Object->rotmode */ typedef enum eRotationModes { /* quaternion rotations (default, and for older Blender versions) */ diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index b995e6917a9..cda6441f0ae 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -68,11 +68,18 @@ typedef struct Bone { float xwidth, length, zwidth; /* width: for block bones. keep in this order, transform! */ float ease1, ease2; /* length of bezier handles */ float rad_head, rad_tail; /* radius for head/tail sphere, defining deform as well, parent->rad_tip overrides rad_head */ - + + float roll1, roll2; /* curved bones settings - these define the "restpose" for a curved bone */ + float curveInX, curveInY; + float curveOutX, curveOutY; + float scaleIn, scaleOut; + float size[3]; /* patch for upward compat, UNUSED! */ int layer; /* layers that bone appears on */ short segments; /* for B-bones */ - short pad[1]; + + short pad1; + } Bone; typedef struct bArmature { @@ -204,7 +211,8 @@ typedef enum eBone_Flag { BONE_TRANSFORM_CHILD = (1 << 20), /* Indicates that a parent is also being transformed */ BONE_UNSELECTABLE = (1 << 21), /* bone cannot be selected */ BONE_NO_LOCAL_LOCATION = (1 << 22), /* bone location is in armature space */ - BONE_RELATIVE_PARENTING = (1 << 23) /* object child will use relative transform (like deform) */ + BONE_RELATIVE_PARENTING = (1 << 23), /* object child will use relative transform (like deform) */ + BONE_ADD_PARENT_END_ROLL = (1 << 24) /* it will add the parent end roll to the inroll */ } eBone_Flag; diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 86991245068..5fcd374b21f 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -516,7 +516,9 @@ typedef enum eBConstraint_Flags { /* indicates that constraint was added locally (i.e. didn't come from the proxy-lib) */ CONSTRAINT_PROXY_LOCAL = (1<<8), /* indicates that constraint is temporarily disabled (only used in GE) */ - CONSTRAINT_OFF = (1<<9) + CONSTRAINT_OFF = (1<<9), + /* use bbone curve shape when calculating headtail values */ + CONSTRAINT_BBONE_SHAPE = (1<<10), } eBConstraint_Flags; /* bConstraint->ownspace/tarspace */ diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index a8ef4664fd7..842e220e8b5 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -482,6 +482,82 @@ static void rna_Armature_transform(struct bArmature *arm, float *mat) #else +/* Settings for curved bbone settings - The posemode values get applied over the top of the editmode ones */ +void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone) +{ +#define RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone) \ + { \ + if (is_posebone) \ + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); \ + else \ + RNA_def_property_update(prop, 0, "rna_Armature_update_data"); \ + } (void)0; + + PropertyRNA *prop; + + /* Roll In/Out */ + prop = RNA_def_property(srna, "bbone_rollin", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "roll1"); + RNA_def_property_range(prop, -M_PI * 2.0f, M_PI * 2.0f); + RNA_def_property_ui_text(prop, "Roll In", "Roll offset for the start of the B-Bone, adjusts twist"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_rollout", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "roll2"); + RNA_def_property_range(prop, -M_PI * 2.0f, M_PI * 2.0f); + RNA_def_property_ui_text(prop, "Roll Out", "Roll offset for the end of the B-Bone, adjusts twist"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + if (is_posebone == false) { + prop = RNA_def_property(srna, "use_endroll_as_inroll", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Inherit End Roll", "Use Roll Out of parent bone as Roll In of its children"); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_ADD_PARENT_END_ROLL); + RNA_def_property_update(prop, 0, "rna_Armature_update_data"); + } + + /* Curve X/Y Offsets */ + prop = RNA_def_property(srna, "bbone_curveinx", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curveInX"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_ui_text(prop, "In X", "X-axis handle offset for start of the B-Bone's curve, adjusts curvature"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_curveiny", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curveInY"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_ui_text(prop, "In Y", "Y-axis handle offset for start of the B-Bone's curve, adjusts curvature"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_curveoutx", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curveOutX"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_ui_text(prop, "Out X", "X-axis handle offset for end of the B-Bone's curve, adjusts curvature"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_curveouty", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "curveOutY"); + RNA_def_property_range(prop, -5.0f, 5.0f); + RNA_def_property_ui_text(prop, "Out Y", "Y-axis handle offset for end of the B-Bone's curve, adjusts curvature"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + /* Scale In/Out */ + prop = RNA_def_property(srna, "bbone_scalein", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "scaleIn"); + RNA_def_property_range(prop, 0.0f, 5.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Scale In", "Scale factor for start of the B-Bone, adjusts thickness (for tapering effects)"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + + prop = RNA_def_property(srna, "bbone_scaleout", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "scaleOut"); + RNA_def_property_range(prop, 0.0f, 5.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Scale Out", "Scale factor for end of the B-Bone, adjusts thickness (for tapering effects)"); + RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone); + +#undef RNA_DEF_CURVEBONE_UPDATE +} + static void rna_def_bone_common(StructRNA *srna, int editbone) { PropertyRNA *prop; @@ -653,6 +729,7 @@ static void rna_def_bone(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Children", "Bones which are children of this bone"); rna_def_bone_common(srna, 0); + rna_def_bone_curved_common(srna, 0); /* XXX should we define this in PoseChannel wrapping code instead? * But PoseChannels directly get some of their flags from here... */ @@ -766,6 +843,7 @@ static void rna_def_edit_bone(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Armature_editbone_transform_update"); rna_def_bone_common(srna, 1); + rna_def_bone_curved_common(srna, 0); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BONE_HIDDEN_A); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 0b5d0f3d41d..98560bf3452 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -483,6 +483,21 @@ static EnumPropertyItem constraint_distance_items[] = { }; +static void rna_def_constraint_headtail_common(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); + RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + + prop = RNA_def_property(srna, "use_bbone_shape", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, "bConstraint", "flag", CONSTRAINT_BBONE_SHAPE); + RNA_def_property_ui_text(prop, "Follow B-Bone", "Follow shape of B-Bone segments when calculating Head/Tail position"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); +} + static void rna_def_constrainttarget(BlenderRNA *brna) { StructRNA *srna; @@ -787,10 +802,7 @@ static void rna_def_constraint_track_to(BlenderRNA *brna) srna = RNA_def_struct(brna, "TrackToConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Track To Constraint", "Aim the constrained object toward the target"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bTrackToConstraint", "data"); @@ -831,10 +843,7 @@ static void rna_def_constraint_locate_like(BlenderRNA *brna) srna = RNA_def_struct(brna, "CopyLocationConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Copy Location Constraint", "Copy the location of the target"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bLocateLikeConstraint", "data"); @@ -1022,10 +1031,7 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna) srna = RNA_def_struct(brna, "CopyTransformsConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Copy Transforms Constraint", "Copy all the transforms of the target"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bTransLikeConstraint", "data"); @@ -1200,10 +1206,7 @@ static void rna_def_constraint_locked_track(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Locked Track Constraint", "Point toward the target along the track axis, while locking the other axis"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bLockTrackConstraint", "data"); @@ -1327,10 +1330,7 @@ static void rna_def_constraint_stretch_to(BlenderRNA *brna) srna = RNA_def_struct(brna, "StretchToConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Stretch To Constraint", "Stretch to meet the target object"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bStretchToConstraint", "data"); @@ -2122,10 +2122,7 @@ static void rna_def_constraint_distance_limit(BlenderRNA *brna) srna = RNA_def_struct(brna, "LimitDistanceConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Limit Distance Constraint", "Limit the distance from target object"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bDistLimitConstraint", "data"); @@ -2236,10 +2233,7 @@ static void rna_def_constraint_damped_track(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Damped Track Constraint", "Point toward target by taking the shortest rotation path"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); +rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bDampTrackConstraint", "data"); @@ -2396,10 +2390,7 @@ static void rna_def_constraint_pivot(BlenderRNA *brna) srna = RNA_def_struct(brna, "PivotConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Pivot Constraint", "Rotate around a different point"); - prop = RNA_def_property(srna, "head_tail", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, "bConstraint", "headtail"); - RNA_def_property_ui_text(prop, "Head/Tail", "Target along length of bone: Head=0, Tail=1"); - RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + rna_def_constraint_headtail_common(srna); RNA_def_struct_sdna_from(srna, "bPivotConstraint", "data"); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 703b02f9d18..161e19f581c 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -197,6 +197,8 @@ void rna_def_animdata_common(struct StructRNA *srna); void rna_def_animviz_common(struct StructRNA *srna); void rna_def_motionpath_common(struct StructRNA *srna); +void rna_def_bone_curved_common(struct StructRNA *srna, bool is_posebone); + void rna_def_texmat_common(struct StructRNA *srna, const char *texspace_editable); void rna_def_mtex_common(struct BlenderRNA *brna, struct StructRNA *srna, const char *begin, const char *activeget, const char *activeset, const char *activeeditable, const char *structname, diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 5bbc2b49a0d..fb7d5141a6d 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -872,6 +872,50 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Rotation Mode", ""); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + /* Curved bones settings - Applied on top of restpose values */ + rna_def_bone_curved_common(srna, true); + + /* Custom BBone next/prev sources */ + prop = RNA_def_property(srna, "use_bbone_custom_handles", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "bboneflag", PCHAN_BBONE_CUSTOM_HANDLES); + RNA_def_property_ui_text(prop, "Use Custom Handle References", + "Use custom reference bones as handles for B-Bones instead of next/previous bones, " + "leave these blank to use only B-Bone offset properties to control the shape"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "bbone_custom_handle_start", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "bbone_prev"); + RNA_def_property_struct_type(prop, "PoseBone"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "B-Bone Start Handle", + "Bone that serves as the start handle for the B-Bone curve"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "use_bbone_relative_start_handle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "bboneflag", PCHAN_BBONE_CUSTOM_START_REL); + RNA_def_property_ui_text(prop, "Relative B-Bone Start Handle", + "Use treat custom start handle position as a relative value"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "bbone_custom_handle_end", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "bbone_next"); + RNA_def_property_struct_type(prop, "PoseBone"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "B-Bone End Handle", + "Bone that serves as the end handle for the B-Bone curve"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + + prop = RNA_def_property(srna, "use_bbone_relative_end_handle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "bboneflag", PCHAN_BBONE_CUSTOM_END_REL); + RNA_def_property_ui_text(prop, "Relative B-Bone End Handle", + "Use treat custom end handle position as a relative value"); + RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable"); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + /* transform matrices - should be read-only since these are set directly by AnimSys evaluation */ prop = RNA_def_property(srna, "matrix_channel", PROP_FLOAT, PROP_MATRIX); RNA_def_property_float_sdna(prop, NULL, "chan_mat"); |