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)
This commit is contained in:
Joshua Leung 2016-05-18 03:19:06 +12:00
parent 29a17d54da
commit 49aeee5a3d
27 changed files with 829 additions and 117 deletions

View File

@ -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)

View File

@ -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")

View File

@ -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):

View File

@ -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):

View File

@ -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)

View File

@ -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 */

View File

@ -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 :) */

View File

@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -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));
}
}
}
}
}
}

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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 */
}
}

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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) */

View File

@ -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;

View File

@ -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 */

View File

@ -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);

View File

@ -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");

View File

@ -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,

View File

@ -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");