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 /source/blender/editors | |
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 'source/blender/editors')
-rw-r--r-- | source/blender/editors/armature/armature_add.c | 19 | ||||
-rw-r--r-- | source/blender/editors/armature/armature_intern.h | 5 | ||||
-rw-r--r-- | source/blender/editors/armature/armature_utils.c | 23 | ||||
-rw-r--r-- | source/blender/editors/armature/editarmature_retarget.c | 9 | ||||
-rw-r--r-- | source/blender/editors/armature/pose_slide.c | 23 | ||||
-rw-r--r-- | source/blender/editors/armature/pose_transform.c | 28 | ||||
-rw-r--r-- | source/blender/editors/armature/pose_utils.c | 25 | ||||
-rw-r--r-- | source/blender/editors/include/ED_armature.h | 5 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/drawarmature.c | 98 |
9 files changed, 216 insertions, 19 deletions
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); } } |