Bendy Bones: Advanced B-Bones for Easier + Simple Rigging
authorJoshua Leung <aligorith@gmail.com>
Tue, 17 May 2016 15:19:06 +0000 (03:19 +1200)
committerJoshua Leung <aligorith@gmail.com>
Tue, 17 May 2016 15:19:06 +0000 (03:19 +1200)
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:
release/scripts/modules/keyingsets_utils.py
release/scripts/startup/bl_ui/properties_constraint.py
release/scripts/startup/bl_ui/properties_data_bone.py
release/scripts/startup/keyingsets_builtins.py
source/blender/blenkernel/BKE_action.h
source/blender/blenkernel/BKE_armature.h
source/blender/blenkernel/intern/action.c
source/blender/blenkernel/intern/armature.c
source/blender/blenkernel/intern/constraint.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/versioning_270.c
source/blender/editors/armature/armature_add.c
source/blender/editors/armature/armature_intern.h
source/blender/editors/armature/armature_utils.c
source/blender/editors/armature/editarmature_retarget.c
source/blender/editors/armature/pose_slide.c
source/blender/editors/armature/pose_transform.c
source/blender/editors/armature/pose_utils.c
source/blender/editors/include/ED_armature.h
source/blender/editors/space_view3d/drawarmature.c
source/blender/makesdna/DNA_action_types.h
source/blender/makesdna/DNA_armature_types.h
source/blender/makesdna/DNA_constraint_types.h
source/blender/makesrna/intern/rna_armature.c
source/blender/makesrna/intern/rna_constraint.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_pose.c

index 03400edc90432c9f0b63b4148f16408b76611802..375ee3feebefa8308402ac0635587b7d923d3691 100644 (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)
+
index ef0fc9c7c9f3e000e50bfd1e0ae23b0b6b6e7f35..4ca2f773dcc55123e67b3266ee2be8a05f9882cb 100644 (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")
 
index 690c22c14401cbe369d91ec2ea09a7e6c7b12db0..99c0d7f37c1c4a882217ea5e6b20311131ed9f33 100644 (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):
index 6d52a81456bea4b36c261f481e08adb6f280a131..195eaf823f4a73f3a65021d05a73e2ff9148a225 100644 (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):
 
index c164cd542f32764ff679463750c32350f8a396d9..cb282b46bec53b5dd9bdfe1e633827956dd1ae31 100644 (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)
index 6d00110e31837d5b8e9e7bf5bdf10d888a7aa518..cc082c084cec47aa5dfc2b3e04a2a0e74bc954b7 100644 (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 */
index b969c9ec78776915b3ae2a2d492d123284edc139..3a31e4c36cfc1ad1cbd5f595c2644ba38f0276cf 100644 (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 :) */
index 1a3afbb876eee57eb26b18f05bd784642188c31b..5c95cde4d8ddd5ac1b207de0ddf12687fe6b5b2d 100644 (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);
index 7144e25ba7f4c4e31515fdbdf93dd952aef275cb..a591d536b434bec4fd655982ebe054247ef876fa 100644 (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:
index 678fd84de9406af1a49f023a58aabf27e413c5c2..d6e2f237be939bc221105892fae9c18af621a4ad 100644 (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);
index efd167d49d5bedfb738132dece69bdc10fbaf4e9..58542d05879104587e8c8f926a7e16732114222f 100644 (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));
+                                       }
+                               }
+                       }
+               }
+       }
 }
index 6afc5a357c8e54c4f2c3f1a094d4612570332e46..847b45d612c3eb9ee36ee851c03bdc3e0c2f1fcc 100644 (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
index ac150b9af747f9ad27a0fb0ef64fb1ae7318abe6..02aefce346457fc5abdd0674311cdf3db6311d00 100644 (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;
 
index 85c6835a8bb337b195b64e21be3b6ef95d736eb5..d73536e5ba725f8399da43f5499f85872dd1b45c 100644 (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);
        }
index 7c09ad49f35d5105b69f89fb1f3efe535de6be5c..fa7bf6e7ad4f2e52f3b964f9342c9684ecc0f192 100644 (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
index 9ef46c63f0f5a443bcd47de2b81df0721378b27a..cd0ea23e2d356e55537ba003b5f3c7af9ace0145 100644 (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 */
                }
        }
        
index 01e16df9f08ada7dbfcd90791bca502f9ed28c2b..df906a3638a48554135c4c5838f5b4a8f027527a 100644 (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 */
index 2ba1eedd33b9acd49a12d679d52cb02750a9c321..b960bec3603a724ec1ab53c6b2d25723de4d9cbd 100644 (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);
index 15c68378b9a80aa96a3028e97236c1bced707d88..904132b8876e065cf8e6183ab205a5849e1750e9 100644 (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;
index 5cda721be4490911832c0308ac9e8c25194f3c7a..f7c1e2ee9818901207e584ccfd0090bb65b1439c 100644 (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);
        }
 }
 
index 96d7ec3128c9c3297c1db23836f54958c7312619..4d82e4528d629bc7556bfb9ebc7d70774436f5c1 100644 (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) */
index b995e6917a9fdca1f64dcb234580d3e818b85928..cda6441f0ae63e617efe07627c41a525c1669fc9 100644 (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;
 
index 869912450689d1024d226e8564d0ddefd16e5b86..5fcd374b21ffb0490ebc4a241333102dd86aa301 100644 (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 */
index a8ef4664fd766f74bb3bfe51bc15c16e9e013dac..842e220e8b5596135b50ad3d544c95f02fb2d9bf 100644 (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);
index 0b5d0f3d41d3dc545272fd3a99b8daccb655fd2d..98560bf3452258c767846719d2aa59097d22cc76 100644 (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");
        
index 703b02f9d1895fb13dafec92353eb0ae9eb596f2..161e19f581cb19f48ecb209b7715e02e3bc877ba 100644 (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,
index 5bbc2b49a0d7e4fd16e2b18114ea3d16d1a857b0..fb7d5141a6d27ed9b0255c1218f215e7ccf12c01 100644 (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");