Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Sun, 2 Dec 2018 03:32:31 +0000 (14:32 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Sun, 2 Dec 2018 03:37:04 +0000 (14:37 +1100)
1  2 
source/blender/blenkernel/BKE_armature.h
source/blender/blenkernel/intern/armature.c
source/blender/blenkernel/intern/armature_update.c
source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
source/blender/draw/intern/draw_armature.c
source/blender/editors/armature/armature_skinning.c
source/blender/editors/gpencil/gpencil_armature.c
source/blender/editors/space_view3d/view3d_gizmo_armature.c
source/blender/makesrna/intern/rna_pose_api.c

@@@ -142,38 -138,8 +142,48 @@@ typedef struct Mat4 
        float mat[4][4];
  } Mat4;
  
 -void equalize_bbone_bezier(float *data, int desired);
 -void BKE_pchan_bbone_spline_setup(struct bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BBONE_SUBDIV]);
 +typedef struct BBoneSplineParameters {
 +      int segments;
 +      float length;
 +
 +      /* Non-uniform scale correction. */
 +      bool do_scale;
 +      float scale[3];
 +
 +      /* Handle control bone data. */
 +      bool use_prev, prev_bbone;
 +      bool use_next, next_bbone;
 +
 +      float prev_h[3], next_h[3];
 +      float prev_mat[4][4], next_mat[4][4];
 +
 +      /* Control values. */
 +      float ease1, ease2;
 +      float roll1, roll2;
 +      float scaleIn, scaleOut;
 +      float curveInX, curveInY, curveOutX, curveOutY;
 +} BBoneSplineParameters;
 +
- void BKE_pchan_get_bbone_handles(struct bPoseChannel *pchan, struct bPoseChannel **r_prev, struct bPoseChannel **r_next);
- void BKE_pchan_get_bbone_spline_parameters(struct bPoseChannel *pchan, const bool rest, struct BBoneSplineParameters *r_param);
- void b_bone_spline_setup(struct bPoseChannel *pchan, const bool rest, Mat4 result_array[MAX_BBONE_SUBDIV]);
- void BKE_compute_b_bone_handles(const BBoneSplineParameters *param, float h1[3], float *r_roll1, float h2[3], float *r_roll2, bool ease, bool offsets);
- int BKE_compute_b_bone_spline(struct BBoneSplineParameters *param, Mat4 result_array[MAX_BBONE_SUBDIV]);
- void BKE_pchan_cache_bbone_segments(struct bPoseChannel *pchan);
- void BKE_pchan_copy_bbone_segments_cache(struct bPoseChannel *pchan, struct bPoseChannel *pchan_from);
++void BKE_pchan_bbone_handles_get(
++        struct bPoseChannel *pchan, struct bPoseChannel **r_prev, struct bPoseChannel **r_next);
++void BKE_pchan_bbone_spline_params_get(
++        struct bPoseChannel *pchan, const bool rest, struct BBoneSplineParameters *r_param);
++
++void BKE_pchan_bbone_spline_setup(
++        struct bPoseChannel *pchan, const bool rest, Mat4 result_array[MAX_BBONE_SUBDIV]);
++
++void BKE_pchan_bbone_handles_compute(
++        const BBoneSplineParameters *param,
++        float h1[3], float *r_roll1,
++        float h2[3], float *r_roll2,
++        bool ease, bool offsets);
++int  BKE_pchan_bbone_spline_compute(
++        struct BBoneSplineParameters *param, Mat4 result_array[MAX_BBONE_SUBDIV]);
++
++void BKE_pchan_bbone_segments_cache_compute(
++        struct bPoseChannel *pchan);
++void BKE_pchan_bbone_segments_cache_copy(
++        struct bPoseChannel *pchan, struct bPoseChannel *pchan_from);
  
  /* like EBONE_VISIBLE */
  #define PBONE_VISIBLE(arm, bone) ( \
@@@ -435,35 -441,9 +435,35 @@@ static void equalize_bbone_bezier(floa
        copy_qt_qt(fp, temp[MAX_BBONE_SUBDIV]);
  }
  
 -/* returns pointer to static array, filled with desired amount of bone->segments elements */
 -/* this calculation is done  within unit bone space */
 -void BKE_pchan_bbone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BBONE_SUBDIV])
 +/* Get "next" and "prev" bones - these are used for handle calculations. */
- void BKE_pchan_get_bbone_handles(bPoseChannel *pchan, bPoseChannel **r_prev, bPoseChannel **r_next)
++void BKE_pchan_bbone_handles_get(bPoseChannel *pchan, bPoseChannel **r_prev, bPoseChannel **r_next)
 +{
 +      if (pchan->bone->bbone_prev_type == BBONE_HANDLE_AUTO) {
 +              /* Use connected parent. */
 +              if (pchan->bone->flag & BONE_CONNECTED) {
 +                      *r_prev = pchan->parent;
 +              }
 +              else {
 +                      *r_prev = NULL;
 +              }
 +      }
 +      else {
 +              /* Use the provided bone as prev - leave blank to eliminate this effect altogether. */
 +              *r_prev = pchan->bbone_prev;
 +      }
 +
 +      if (pchan->bone->bbone_next_type == BBONE_HANDLE_AUTO) {
 +              /* Use connected child. */
 +              *r_next = pchan->child;
 +      }
 +      else {
 +              /* Use the provided bone as next - leave blank to eliminate this effect altogether. */
 +              *r_next = pchan->bbone_next;
 +      }
 +}
 +
 +/* Compute B-Bone spline parameters for the given channel. */
- void BKE_pchan_get_bbone_spline_parameters(struct bPoseChannel *pchan, const bool rest, struct BBoneSplineParameters *param)
++void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, const bool rest, struct BBoneSplineParameters *param)
  {
        bPoseChannel *next, *prev;
        Bone *bone = pchan->bone;
                }
        }
  
-       BKE_pchan_get_bbone_handles(pchan, &prev, &next);
 -      /* 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;
 -      }
++      BKE_pchan_bbone_handles_get(pchan, &prev, &next);
  
 -      /* find the handle points, since this is inside bone space, the
 +      /* Find the handle points, since this is inside bone space, the
         * first point = (0, 0, 0)
         * last point =  (0, length, 0) */
        if (rest) {
                        }
                }
                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);
 +                      /* Apply special handling for smoothly joining B-Bone chains */
 +                      param->next_bbone = (next->bone->segments > 1);
 +
 +                      /* Use bone tail as absolute position. */
 +                      copy_v3_v3(h2, rest ? next->bone->arm_tail : next->pose_tail);
                }
 -              mul_m4_v3(imat, h2);
  
 -              /* if next bone is B-bone too, use average handle direction */
 -              if (next->bone->segments > 1) {
 -                      /* pass */
 +              if (!done) {
 +                      mul_v3_m4v3(param->next_h, imat, h2);
                }
 -              else {
 -                      h2[1] -= length;
 +
 +              /* Find the next roll to interpolate as well. */
 +              mul_m4_m4m4(param->next_mat, imat, rest ? next->bone->arm_mat : next->pose_mat);
 +      }
 +
 +      /* 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
 +       */
 +      {
 +              param->ease1 = bone->ease1 + (!rest ? pchan->ease1 : 0.0f);
 +              param->ease2 = bone->ease2 + (!rest ? pchan->ease2 : 0.0f);
 +
 +              param->roll1 = bone->roll1 + (!rest ? pchan->roll1 : 0.0f);
 +              param->roll2 = bone->roll2 + (!rest ? pchan->roll2 : 0.0f);
 +
 +              if (bone->flag & BONE_ADD_PARENT_END_ROLL) {
 +                      if (prev) {
 +                              if (prev->bone) {
 +                                      param->roll1 += prev->bone->roll2;
 +                              }
 +
 +                              if (!rest) {
 +                                      param->roll1 += prev->roll2;
 +                              }
 +                      }
                }
 -              normalize_v3(h2);
  
 -              /* find the next roll to interpolate as well */
 -              if (rest)
 -                      mul_m4_m4m4(difmat, imat, next->bone->arm_mat);
 -              else
 -                      mul_m4_m4m4(difmat, imat, next->pose_mat);
 -              copy_m3_m4(result, difmat); /* the desired rotation at beginning of next bone */
 +              param->scaleIn = bone->scaleIn * (!rest ? pchan->scaleIn : 1.0f);
 +              param->scaleOut = bone->scaleOut * (!rest ? pchan->scaleOut : 1.0f);
 +
 +              /* Extra curve x / y */
 +              param->curveInX = bone->curveInX + (!rest ? pchan->curveInX : 0.0f);
 +              param->curveInY = bone->curveInY + (!rest ? pchan->curveInY : 0.0f);
 +
 +              param->curveOutX = bone->curveOutX + (!rest ? pchan->curveOutX : 0.0f);
 +              param->curveOutY = bone->curveOutY + (!rest ? pchan->curveOutY : 0.0f);
 +      }
 +}
 +
 +/* Fills the array with the desired amount of bone->segments elements.
 + * This calculation is done within unit bone space. */
- void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array[MAX_BBONE_SUBDIV])
++void BKE_pchan_bbone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array[MAX_BBONE_SUBDIV])
 +{
 +      BBoneSplineParameters param;
 +
-       BKE_pchan_get_bbone_spline_parameters(pchan, rest, &param);
++      BKE_pchan_bbone_spline_params_get(pchan, rest, &param);
 +
-       pchan->bone->segments = BKE_compute_b_bone_spline(&param, result_array);
++      pchan->bone->segments = BKE_pchan_bbone_spline_compute(&param, result_array);
 +}
 +
 +/* Computes the bezier handle vectors and rolls coming from custom handles. */
- void BKE_compute_b_bone_handles(const BBoneSplineParameters *param, float h1[3], float *r_roll1, float h2[3], float *r_roll2, bool ease, bool offsets)
++void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h1[3], float *r_roll1, float h2[3], float *r_roll2, bool ease, bool offsets)
 +{
 +      float mat3[3][3];
 +      float length = param->length;
  
 -              vec_roll_to_mat3(h2, 0.0f, mat3); /* the result of vec_roll without roll */
 +      if (param->do_scale) {
 +              length *= param->scale[1];
 +      }
  
 -              invert_m3_m3(imat3, mat3);
 -              mul_m3_m3m3(mat3, imat3, result); /* the matrix transforming vec_roll to desired roll */
 +      *r_roll1 = *r_roll2 = 0.0f;
  
 -              roll2 = atan2f(mat3[2][0], mat3[2][2]);
 +      if (param->use_prev) {
 +              copy_v3_v3(h1, param->prev_h);
  
 +              if (param->prev_bbone) {
 +                      /* If previous bone is B-bone too, use average handle direction. */
 +                      h1[1] -= length;
 +              }
 +
 +              normalize_v3(h1);
 +              negate_v3(h1);
 +
 +              if (!param->prev_bbone) {
 +                      /* Find the previous roll to interpolate. */
 +                      copy_m3_m4(mat3, param->prev_mat);
 +                      mat3_vec_to_roll(mat3, h1, r_roll1);
 +              }
 +      }
 +      else {
 +              h1[0] = 0.0f; h1[1] = 1.0; h1[2] = 0.0f;
 +      }
 +
 +      if (param->use_next) {
 +              copy_v3_v3(h2, param->next_h);
 +
 +              /* If next bone is B-bone too, use average handle direction. */
 +              if (param->next_bbone) {
 +                      /* pass */
 +              }
 +              else {
 +                      h2[1] -= length;
 +              }
 +
 +              normalize_v3(h2);
 +
 +              /* Find the next roll to interpolate as well. */
 +              copy_m3_m4(mat3, param->next_mat);
 +              mat3_vec_to_roll(mat3, h2, r_roll2);
        }
        else {
                h2[0] = 0.0f; h2[1] = 1.0f; h2[2] = 0.0f;
                 *       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;
 +              const float xscale_correction = (param->do_scale) ? param->scale[0] : 1.0f;
 +              const float yscale_correction = (param->do_scale) ? param->scale[2] : 1.0f;
 +
 +              h1[0] += param->curveInX * xscale_correction;
 +              h1[2] += param->curveInY * yscale_correction;
 +
 +              h2[0] += param->curveOutX * xscale_correction;
 +              h2[2] += param->curveOutY * yscale_correction;
 +      }
 +}
 +
 +/* Fills the array with the desired amount of bone->segments elements.
 + * This calculation is done within unit bone space. */
- int BKE_compute_b_bone_spline(BBoneSplineParameters *param, Mat4 result_array[MAX_BBONE_SUBDIV])
++int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, Mat4 result_array[MAX_BBONE_SUBDIV])
 +{
 +      float scalemat[4][4], iscalemat[4][4];
 +      float mat3[3][3];
 +      float h1[3], roll1, h2[3], roll2;
 +      float data[MAX_BBONE_SUBDIV + 1][4], *fp;
 +      float length = param->length;
 +      int a;
  
 -              h1[0] += (bone->curveInX + (!rest ? pchan->curveInX : 0.0f)) * xscale_correction;
 -              h1[2] += (bone->curveInY + (!rest ? pchan->curveInY : 0.0f)) * yscale_correction;
 +      if (param->do_scale) {
 +              size_to_mat4(scalemat, param->scale);
 +              invert_m4_m4(iscalemat, scalemat);
  
 -              h2[0] += (bone->curveOutX + (!rest ? pchan->curveOutX : 0.0f)) * xscale_correction;
 -              h2[2] += (bone->curveOutY + (!rest ? pchan->curveOutY : 0.0f)) * yscale_correction;
 +              length *= param->scale[1];
        }
  
-       BKE_compute_b_bone_handles(param, h1, &roll1, h2, &roll2, true, true);
 -      /* make curve */
 -      if (bone->segments > MAX_BBONE_SUBDIV)
 -              bone->segments = MAX_BBONE_SUBDIV;
++      BKE_pchan_bbone_handles_compute(param, h1, &roll1, h2, &roll2, true, true);
 +
 +      /* Make curve. */
 +      CLAMP_MAX(param->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));
@@@ -833,53 -726,26 +833,53 @@@ typedef struct bPoseChanDeform 
        DualQuat *b_bone_dual_quats;
  } bPoseChanDeform;
  
 -static void pchan_b_bone_defmats(bPoseChannel *pchan, bPoseChanDeform *pdef_info, const bool use_quaternion)
 +/* Definition of cached object bbone deformations. */
 +typedef struct ObjectBBoneDeform {
 +      DualQuat *dualquats;
 +      bPoseChanDeform *pdef_info_array;
 +      int num_pchan;
 +} ObjectBBoneDeform;
 +
 +static void allocate_bbone_cache(bPoseChannel *pchan, int segments)
  {
- void BKE_pchan_cache_bbone_segments(bPoseChannel *pchan)
 +      bPoseChannelRuntime *runtime = &pchan->runtime;
 +
 +      if (runtime->bbone_segments != segments) {
 +              if (runtime->bbone_segments != 0) {
 +                      BKE_pose_channel_free_bbone_cache(pchan);
 +              }
 +
 +              runtime->bbone_segments = segments;
 +              runtime->bbone_rest_mats = MEM_malloc_arrayN(sizeof(Mat4), (uint)segments, "bPoseChannelRuntime::bbone_rest_mats");
 +              runtime->bbone_pose_mats = MEM_malloc_arrayN(sizeof(Mat4), (uint)segments, "bPoseChannelRuntime::bbone_pose_mats");
 +              runtime->bbone_deform_mats = MEM_malloc_arrayN(sizeof(Mat4), 1 + (uint)segments, "bPoseChannelRuntime::bbone_deform_mats");
 +              runtime->bbone_dual_quats = MEM_malloc_arrayN(sizeof(DualQuat), (uint)segments, "bPoseChannelRuntime::bbone_dual_quats");
 +      }
 +}
 +
 +/** Compute and cache the B-Bone shape in the channel runtime struct. */
++void BKE_pchan_bbone_segments_cache_compute(bPoseChannel *pchan)
 +{
 +      bPoseChannelRuntime *runtime = &pchan->runtime;
        Bone *bone = pchan->bone;
 -      Mat4 b_bone[MAX_BBONE_SUBDIV], b_bone_rest[MAX_BBONE_SUBDIV];
 -      Mat4 *b_bone_mats;
 -      DualQuat *b_bone_dual_quats = NULL;
 -      int a;
 +      int segments = bone->segments;
  
 -      BKE_pchan_bbone_spline_setup(pchan, 0, b_bone);
 -      BKE_pchan_bbone_spline_setup(pchan, 1, b_bone_rest);
 +      BLI_assert(segments > 1);
  
 -      /* allocate b_bone matrices and dual quats */
 -      b_bone_mats = MEM_mallocN((1 + bone->segments) * sizeof(Mat4), "BBone defmats");
 -      pdef_info->b_bone_mats = b_bone_mats;
 +      /* Allocate the cache if needed. */
 +      allocate_bbone_cache(pchan, segments);
  
 -      if (use_quaternion) {
 -              b_bone_dual_quats = MEM_mallocN((bone->segments) * sizeof(DualQuat), "BBone dqs");
 -              pdef_info->b_bone_dual_quats = b_bone_dual_quats;
 -      }
 +      /* Compute the shape. */
 +      Mat4 *b_bone = runtime->bbone_pose_mats;
 +      Mat4 *b_bone_rest = runtime->bbone_rest_mats;
 +      Mat4 *b_bone_mats = runtime->bbone_deform_mats;
 +      DualQuat *b_bone_dual_quats = runtime->bbone_dual_quats;
 +      int a;
 +
-       b_bone_spline_setup(pchan, false, b_bone);
-       b_bone_spline_setup(pchan, true, b_bone_rest);
++      BKE_pchan_bbone_spline_setup(pchan, false, b_bone);
++      BKE_pchan_bbone_spline_setup(pchan, true, b_bone_rest);
  
 +      /* Compute deform matrices. */
        /* first matrix is the inverse arm_mat, to bring points in local bone space
         * for finding out which segment it belongs to */
        invert_m4_m4(b_bone_mats[0].mat, bone->arm_mat);
                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)
 -                      mat4_to_dquat(&b_bone_dual_quats[a], bone->arm_mat, b_bone_mats[a + 1].mat);
 +              mat4_to_dquat(&b_bone_dual_quats[a], bone->arm_mat, b_bone_mats[a + 1].mat);
 +      }
 +}
 +
 +/** Copy cached B-Bone segments from one channel to another */
- void BKE_pchan_copy_bbone_segments_cache(bPoseChannel *pchan, bPoseChannel *pchan_from)
++void BKE_pchan_bbone_segments_cache_copy(bPoseChannel *pchan, bPoseChannel *pchan_from)
 +{
 +      bPoseChannelRuntime *runtime = &pchan->runtime;
 +      bPoseChannelRuntime *runtime_from = &pchan_from->runtime;
 +      int segments = runtime_from->bbone_segments;
 +
 +      if (segments <= 1) {
 +              BKE_pose_channel_free_bbone_cache(pchan);
 +      }
 +      else {
 +              allocate_bbone_cache(pchan, segments);
 +
 +              memcpy(runtime->bbone_rest_mats, runtime_from->bbone_rest_mats, sizeof(Mat4) * segments);
 +              memcpy(runtime->bbone_pose_mats, runtime_from->bbone_pose_mats, sizeof(Mat4) * segments);
 +              memcpy(runtime->bbone_deform_mats, runtime_from->bbone_deform_mats, sizeof(Mat4) * (1 + segments));
 +              memcpy(runtime->bbone_dual_quats, runtime_from->bbone_dual_quats, sizeof(DualQuat) * segments);
        }
  }
  
@@@ -707,36 -681,9 +707,36 @@@ void BKE_pose_bone_done(struct Depsgrap
                invert_m4_m4(imat, pchan->bone->arm_mat);
                mul_m4_m4m4(pchan->chan_mat, pchan->pose_mat, imat);
        }
-               BKE_pchan_cache_bbone_segments(pchan);
 +      bArmature *arm = (bArmature *)ob->data;
 +      if (DEG_is_active(depsgraph) && arm->edbo == NULL) {
 +              bPoseChannel *pchan_orig = pchan->orig_pchan;
 +              copy_m4_m4(pchan_orig->pose_mat, pchan->pose_mat);
 +              copy_m4_m4(pchan_orig->chan_mat, pchan->chan_mat);
 +              copy_v3_v3(pchan_orig->pose_head, pchan->pose_mat[3]);
 +              copy_m4_m4(pchan_orig->constinv, pchan->constinv);
 +              BKE_pose_where_is_bone_tail(pchan_orig);
 +              if (pchan->bone == NULL || pchan->bone->segments <= 1) {
 +                      BKE_pose_channel_free_bbone_cache(pchan_orig);
 +              }
 +      }
 +}
 +
 +void BKE_pose_eval_bbone_segments(struct Depsgraph *depsgraph,
 +                                  struct Object *ob,
 +                                  int pchan_index)
 +{
 +      bPoseChannel *pchan = pose_pchan_get_indexed(ob, pchan_index);
 +      DEG_debug_print_eval(depsgraph, __func__, pchan->name, pchan);
 +      if (pchan->bone != NULL && pchan->bone->segments > 1) {
-                       BKE_pchan_copy_bbone_segments_cache(pchan->orig_pchan, pchan);
++              BKE_pchan_bbone_segments_cache_compute(pchan);
 +              bArmature *arm = (bArmature *)ob->data;
 +              if (DEG_is_active(depsgraph) && arm->edbo == NULL) {
++                      BKE_pchan_bbone_segments_cache_copy(pchan->orig_pchan, pchan);
 +              }
 +      }
  }
  
 -void BKE_pose_iktree_evaluate(EvaluationContext *UNUSED(eval_ctx),
 +void BKE_pose_iktree_evaluate(struct Depsgraph *depsgraph,
                                Scene *scene,
                                Object *ob,
                                int rootchan_index)
@@@ -768,94 -715,40 +768,94 @@@ void BKE_pose_splineik_evaluate(struct 
        if (arm->flag & ARM_RESTPOS) {
                return;
        }
 -      BKE_splineik_execute_tree(scene, ob, rootchan, ctime);
 +      BKE_splineik_execute_tree(depsgraph, scene, ob, rootchan, ctime);
  }
  
 -void BKE_pose_eval_flush(EvaluationContext *UNUSED(eval_ctx),
 -                         Scene *scene,
 -                         Object *ob)
 +/* Common part for both original and proxy armatrues. */
 +static void pose_eval_done_common(struct Depsgraph *depsgraph, Object *object)
  {
 -      bPose *pose = ob->pose;
 +      bPose *pose = object->pose;
 +      UNUSED_VARS_NDEBUG(pose);
 +      BLI_assert(pose != NULL);
 +      BKE_armature_cached_bbone_deformation_update(object);
 +      BKE_object_eval_boundbox(depsgraph, object);
 +}
 +static void pose_eval_cleanup_common(Object *object)
 +{
 +      bPose *pose = object->pose;
        BLI_assert(pose != NULL);
 +      BLI_assert(pose->chan_array != NULL || BLI_listbase_is_empty(&pose->chanbase));
 +      MEM_SAFE_FREE(pose->chan_array);
 +}
  
 -      float ctime = BKE_scene_frame_get(scene); /* not accurate... */
 -      DEG_debug_print_eval(__func__, ob->id.name, ob);
 -      BLI_assert(ob->type == OB_ARMATURE);
 +void BKE_pose_eval_done(struct Depsgraph *depsgraph, Object *object)
 +{
 +      bPose *pose = object->pose;
 +      BLI_assert(pose != NULL);
 +      UNUSED_VARS_NDEBUG(pose);
 +      DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
 +      BLI_assert(object->type == OB_ARMATURE);
 +      pose_eval_done_common(depsgraph, object);
 +}
  
 -      /* release the IK tree */
 -      BIK_release_tree(scene, ob, ctime);
 +void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph,
 +                           Scene *scene,
 +                           Object *object)
 +{
 +      bPose *pose = object->pose;
 +      BLI_assert(pose != NULL);
 +      UNUSED_VARS_NDEBUG(pose);
 +      const float ctime = BKE_scene_frame_get(scene); /* not accurate... */
 +      DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
 +      BLI_assert(object->type == OB_ARMATURE);
 +      /* Release the IK tree. */
 +      BIK_release_tree(scene, object, ctime);
 +      pose_eval_cleanup_common(object);
 +}
  
 -      ob->recalc &= ~OB_RECALC_ALL;
 +void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, Object *object)
 +{
 +      BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
 +      DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
  
 -      BLI_assert(pose->chan_array != NULL);
 -      MEM_freeN(pose->chan_array);
 -      pose->chan_array = NULL;
 +      pose_pchan_index_create(object->pose);
 +      BKE_armature_cached_bbone_deformation_free_data(object);
  }
  
 -void BKE_pose_eval_proxy_copy(EvaluationContext *UNUSED(eval_ctx), Object *ob)
 +void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, Object *object)
  {
 -      BLI_assert(ID_IS_LINKED(ob) && ob->proxy_from != NULL);
 -      DEG_debug_print_eval(__func__, ob->id.name, ob);
 -      if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) {
 -              printf("Proxy copy error, lib Object: %s proxy Object: %s\n",
 -                     ob->id.name + 2, ob->proxy_from->id.name + 2);
 -      }
 -      /* Rest of operations are NO-OP in depsgraph, so can clear
 -       * flag now.
 +      BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
 +      DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
 +      pose_eval_done_common(depsgraph, object);
 +}
 +
 +void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, Object *object)
 +{
 +      BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
 +      DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
 +      pose_eval_cleanup_common(object);
 +}
 +
 +void BKE_pose_eval_proxy_copy_bone(
 +        struct Depsgraph *depsgraph,
 +        Object *object,
 +        int pchan_index)
 +{
 +      BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
 +      DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
 +      bPoseChannel *pchan = pose_pchan_get_indexed(object, pchan_index);
 +      /* TODO(sergey): Use indexec lookup, once it's guaranteed to be kept
 +       * around for the time while proxies are evaluating.
         */
 -      ob->recalc &= ~OB_RECALC_ALL;
 +#if 0
 +      bPoseChannel *pchan_from = pose_pchan_get_indexed(
 +              object->proxy_from, pchan_index);
 +#else
 +      bPoseChannel *pchan_from = BKE_pose_channel_find_name(
 +              object->proxy_from->pose, pchan->name);
 +#endif
 +      BLI_assert(pchan != NULL);
 +      BLI_assert(pchan_from != NULL);
 +      BKE_pose_copyesult_pchan_result(pchan, pchan_from);
-       BKE_pchan_copy_bbone_segments_cache(pchan, pchan_from);
++      BKE_pchan_bbone_segments_cache_copy(pchan, pchan_from);
  }
@@@ -459,73 -417,37 +459,73 @@@ void DepsgraphRelationBuilder::build_ri
                        /* Build relations for indirectly linked objects. */
                        BuilderWalkUserData data;
                        data.builder = this;
 -                      BKE_constraints_id_loop(&pchan->constraints, constraint_walk, &data);
 -
 -                      /* constraints stack and constraint dependencies */
 -                      build_constraints(&object->id, DEG_NODE_TYPE_BONE, pchan->name, &pchan->constraints, &root_map);
 -
 -                      /* pose -> constraints */
 -                      OperationKey constraints_key(&object->id, DEG_NODE_TYPE_BONE, pchan->name, DEG_OPCODE_BONE_CONSTRAINTS);
 +                      BKE_constraints_id_loop(
 +                              &pchan->constraints, constraint_walk, &data);
 +                      /* Constraints stack and constraint dependencies. */
 +                      build_constraints(&object->id,
 +                                        DEG_NODE_TYPE_BONE,
 +                                        pchan->name,
 +                                        &pchan->constraints,
 +                                        &root_map);
 +                      /* Pose -> constraints. */
 +                      OperationKey constraints_key(&object->id,
 +                                                   DEG_NODE_TYPE_BONE,
 +                                                   pchan->name,
 +                                                   DEG_OPCODE_BONE_CONSTRAINTS);
                        add_relation(bone_pose_key, constraints_key, "Constraints Stack");
 -
 -                      /* constraints -> ready */
 -                      // TODO: when constraint stack is exploded, this step should occur before the first IK solver
 -                      add_relation(constraints_key, bone_ready_key, "Constraints -> Ready");
 +                      /* Constraints -> ready/ */
 +                      /* TODO(sergey): When constraint stack is exploded, this step should
 +                       * occur before the first IK solver.  */
 +                      add_relation(
 +                              constraints_key, bone_ready_key, "Constraints -> Ready");
                }
                else {
 -                      /* pose -> ready */
 +                      /* Pose -> Ready */
                        add_relation(bone_pose_key, bone_ready_key, "Pose -> Ready");
                }
 -
 -              /* bone ready -> done
 +              /* Bone ready -> Bone done.
                 * NOTE: For bones without IK, this is all that's needed.
 -               *       For IK chains however, an additional rel is created from IK to done,
 -               *       with transitive reduction removing this one...
 -               */
 +               *       For IK chains however, an additional rel is created from IK
 +               *       to done, with transitive reduction removing this one. */
                add_relation(bone_ready_key, bone_done_key, "Ready -> Done");
 -
 -              /* assume that all bones must be done for the pose to be ready (for deformers) */
 -              add_relation(bone_done_key, flush_key, "PoseEval Result-Bone Link");
 -
 +              /* B-Bone shape is the real final step after Done if present. */
 +              if (pchan->bone != NULL && pchan->bone->segments > 1) {
 +                      OperationKey bone_segments_key(&object->id,
 +                                                     DEG_NODE_TYPE_BONE,
 +                                                     pchan->name,
 +                                                     DEG_OPCODE_BONE_SEGMENTS);
 +                      /* B-Bone shape depends on the final position of the bone. */
 +                      add_relation(bone_done_key, bone_segments_key, "Done -> B-Bone Segments");
 +                      /* B-Bone shape depends on final position of handle bones. */
 +                      bPoseChannel *prev, *next;
-                       BKE_pchan_get_bbone_handles(pchan, &prev, &next);
++                      BKE_pchan_bbone_handles_get(pchan, &prev, &next);
 +                      if (prev) {
 +                              OperationKey prev_key(&object->id,
 +                                                    DEG_NODE_TYPE_BONE,
 +                                                    prev->name,
 +                                                    DEG_OPCODE_BONE_DONE);
 +                              add_relation(prev_key, bone_segments_key, "Prev Handle -> B-Bone Segments");
 +                      }
 +                      if (next) {
 +                              OperationKey next_key(&object->id,
 +                                                    DEG_NODE_TYPE_BONE,
 +                                                    next->name,
 +                                                    DEG_OPCODE_BONE_DONE);
 +                              add_relation(next_key, bone_segments_key, "Next Handle -> B-Bone Segments");
 +                      }
 +                      /* Pose requires the B-Bone shape. */
 +                      add_relation(bone_segments_key, pose_done_key, "PoseEval Result-Bone Link");
 +                      add_relation(bone_segments_key, pose_cleanup_key, "Cleanup dependency");
 +              }
 +              else {
 +                      /* Assume that all bones must be done for the pose to be ready
 +                       * (for deformers). */
 +                      add_relation(bone_done_key, pose_done_key, "PoseEval Result-Bone Link");
 +                      add_relation(bone_done_key, pose_cleanup_key, "Cleanup dependency");
 +              }
                /* Custom shape. */
                if (pchan->custom != NULL) {
 -                      build_object(pchan->custom);
 +                      build_object(NULL, pchan->custom);
                }
        }
  }
index aec2527,0000000..b8b53fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,1915 -1,0 +1,1915 @@@
- /* A version of b_bone_spline_setup() for previewing editmode curve settings. */
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Blender Institute
 + *
 + */
 +
 +/** \file draw_armature.c
 + *  \ingroup draw
 + */
 +
 +#include <stdlib.h>
 +#include <string.h>
 +#include <math.h>
 +
 +#include "DNA_anim_types.h"
 +#include "DNA_armature_types.h"
 +#include "DNA_constraint_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_screen_types.h"
 +#include "DNA_view3d_types.h"
 +#include "DNA_object_types.h"
 +
 +#include "DRW_render.h"
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_math.h"
 +#include "BLI_dlrbTree.h"
 +#include "BLI_utildefines.h"
 +
 +#include "BKE_animsys.h"
 +#include "BKE_action.h"
 +#include "BKE_armature.h"
 +#include "BKE_global.h"
 +#include "BKE_modifier.h"
 +#include "BKE_nla.h"
 +#include "BKE_curve.h"
 +
 +#include "BIF_gl.h"
 +
 +#include "ED_armature.h"
 +#include "ED_keyframes_draw.h"
 +
 +#include "GPU_select.h"
 +
 +#include "UI_resources.h"
 +
 +#include "draw_common.h"
 +#include "draw_manager_text.h"
 +
 +#define BONE_VAR(eBone, pchan, var) ((eBone) ? (eBone->var) : (pchan->var))
 +#define BONE_FLAG(eBone, pchan) ((eBone) ? (eBone->flag) : (pchan->bone->flag))
 +
 +#define PT_DEFAULT_RAD 0.05f /* radius of the point batch. */
 +
 +/* For now just match 2.7x where possible. */
 +// #define USE_SOLID_COLOR
 +
 +/* Reset for drawing each armature object */
 +static struct {
 +      /* Current armature object */
 +      Object *ob;
 +      /* Reset when changing current_armature */
 +      DRWShadingGroup *bone_octahedral_solid;
 +      DRWShadingGroup *bone_octahedral_wire;
 +      DRWShadingGroup *bone_octahedral_outline;
 +      DRWShadingGroup *bone_box_solid;
 +      DRWShadingGroup *bone_box_wire;
 +      DRWShadingGroup *bone_box_outline;
 +      DRWShadingGroup *bone_wire;
 +      DRWShadingGroup *bone_stick;
 +      DRWShadingGroup *bone_dof_sphere;
 +      DRWShadingGroup *bone_dof_lines;
 +      DRWShadingGroup *bone_envelope_solid;
 +      DRWShadingGroup *bone_envelope_distance;
 +      DRWShadingGroup *bone_envelope_wire;
 +      DRWShadingGroup *bone_point_solid;
 +      DRWShadingGroup *bone_point_wire;
 +      DRWShadingGroup *bone_axes;
 +      DRWShadingGroup *lines_relationship;
 +      DRWShadingGroup *lines_ik;
 +      DRWShadingGroup *lines_ik_no_target;
 +      DRWShadingGroup *lines_ik_spline;
 +
 +      DRWArmaturePasses passes;
 +
 +      bool transparent;
 +} g_data = {NULL};
 +
 +
 +/**
 + * Follow `TH_*` naming except for mixed colors.
 + */
 +static struct {
 +      float select_color[4];
 +      float edge_select_color[4];
 +      float bone_select_color[4];  /* tint */
 +      float wire_color[4];
 +      float wire_edit_color[4];
 +      float bone_solid_color[4];
 +      float bone_active_unselect_color[4];  /* mix */
 +      float bone_pose_color[4];
 +      float bone_pose_active_color[4];
 +      float bone_pose_active_unselect_color[4];  /* mix */
 +      float text_hi_color[4];
 +      float text_color[4];
 +      float vertex_select_color[4];
 +      float vertex_color[4];
 +
 +      /* not a theme, this is an override */
 +      const float *const_color;
 +      float const_wire;
 +} g_theme;
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Shader Groups (DRW_shgroup)
 + * \{ */
 +
 +/* Octahedral */
 +static void drw_shgroup_bone_octahedral(
 +        const float (*bone_mat)[4],
 +        const float bone_color[4], const float hint_color[4], const float outline_color[4])
 +{
 +      if (g_data.bone_octahedral_outline == NULL) {
 +              struct GPUBatch *geom = DRW_cache_bone_octahedral_wire_get();
 +              g_data.bone_octahedral_outline = shgroup_instance_bone_shape_outline(g_data.passes.bone_outline, geom);
 +      }
 +      if (g_data.bone_octahedral_solid == NULL) {
 +              struct GPUBatch *geom = DRW_cache_bone_octahedral_get();
 +              g_data.bone_octahedral_solid = shgroup_instance_bone_shape_solid(g_data.passes.bone_solid, geom,
 +                                                                               g_data.transparent);
 +      }
 +      float final_bonemat[4][4];
 +      mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +      DRW_shgroup_call_dynamic_add(g_data.bone_octahedral_solid, final_bonemat, bone_color, hint_color);
 +      if (outline_color[3] > 0.0f) {
 +              DRW_shgroup_call_dynamic_add(g_data.bone_octahedral_outline, final_bonemat, outline_color);
 +      }
 +}
 +
 +/* Box / B-Bone */
 +static void drw_shgroup_bone_box(
 +        const float (*bone_mat)[4],
 +        const float bone_color[4], const float hint_color[4], const float outline_color[4])
 +{
 +      if (g_data.bone_box_wire == NULL) {
 +              struct GPUBatch *geom = DRW_cache_bone_box_wire_get();
 +              g_data.bone_box_outline = shgroup_instance_bone_shape_outline(g_data.passes.bone_outline, geom);
 +      }
 +      if (g_data.bone_box_solid == NULL) {
 +              struct GPUBatch *geom = DRW_cache_bone_box_get();
 +              g_data.bone_box_solid = shgroup_instance_bone_shape_solid(g_data.passes.bone_solid, geom, g_data.transparent);
 +      }
 +      float final_bonemat[4][4];
 +      mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +      DRW_shgroup_call_dynamic_add(g_data.bone_box_solid, final_bonemat, bone_color, hint_color);
 +      if (outline_color[3] > 0.0f) {
 +              DRW_shgroup_call_dynamic_add(g_data.bone_box_outline, final_bonemat, outline_color);
 +      }
 +}
 +
 +/* Wire */
 +static void drw_shgroup_bone_wire(const float (*bone_mat)[4], const float color[4])
 +{
 +      if (g_data.bone_wire == NULL) {
 +              g_data.bone_wire = shgroup_dynlines_flat_color(g_data.passes.bone_wire);
 +      }
 +      float head[3], tail[3];
 +      mul_v3_m4v3(head, g_data.ob->obmat, bone_mat[3]);
 +      DRW_shgroup_call_dynamic_add(g_data.bone_wire, head, color);
 +
 +      add_v3_v3v3(tail, bone_mat[3], bone_mat[1]);
 +      mul_m4_v3(g_data.ob->obmat, tail);
 +      DRW_shgroup_call_dynamic_add(g_data.bone_wire, tail, color);
 +}
 +
 +/* Stick */
 +static void drw_shgroup_bone_stick(
 +        const float (*bone_mat)[4],
 +        const float col_wire[4], const float col_bone[4], const float col_head[4], const float col_tail[4])
 +{
 +      if (g_data.bone_stick == NULL) {
 +              g_data.bone_stick = shgroup_instance_bone_stick(g_data.passes.bone_wire);
 +      }
 +      float final_bonemat[4][4], tail[4];
 +      mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +      add_v3_v3v3(tail, final_bonemat[3], final_bonemat[1]);
 +      DRW_shgroup_call_dynamic_add(g_data.bone_stick, final_bonemat[3], tail, col_wire, col_bone, col_head, col_tail);
 +}
 +
 +
 +/* Envelope */
 +static void drw_shgroup_bone_envelope_distance(
 +        const float (*bone_mat)[4],
 +        const float *radius_head, const float *radius_tail, const float *distance)
 +{
 +      if (g_data.passes.bone_envelope != NULL) {
 +              if (g_data.bone_envelope_distance == NULL) {
 +                      g_data.bone_envelope_distance = shgroup_instance_bone_envelope_distance(g_data.passes.bone_envelope);
 +                      /* passes.bone_envelope should have the DRW_STATE_CULL_FRONT state enabled. */
 +              }
 +              float head_sphere[4] = {0.0f, 0.0f, 0.0f, 1.0f}, tail_sphere[4] = {0.0f, 1.0f, 0.0f, 1.0f};
 +              float final_bonemat[4][4];
 +              mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +              /* We need matrix mul because we need shear applied. */
 +              /* NOTE: could be done in shader if that becomes a bottleneck. */
 +              mul_m4_v4(final_bonemat, head_sphere);
 +              mul_m4_v4(final_bonemat, tail_sphere);
 +              head_sphere[3]  = *radius_head;
 +              head_sphere[3] += *distance;
 +              tail_sphere[3]  = *radius_tail;
 +              tail_sphere[3] += *distance;
 +              DRW_shgroup_call_dynamic_add(g_data.bone_envelope_distance, head_sphere, tail_sphere, final_bonemat[0]);
 +      }
 +}
 +
 +static void drw_shgroup_bone_envelope(
 +        const float (*bone_mat)[4],
 +        const float bone_color[4], const float hint_color[4], const float outline_color[4],
 +        const float *radius_head, const float *radius_tail)
 +{
 +      if (g_data.bone_point_wire == NULL) {
 +              g_data.bone_point_wire = shgroup_instance_bone_sphere_outline(g_data.passes.bone_wire);
 +      }
 +      if (g_data.bone_point_solid == NULL) {
 +              g_data.bone_point_solid = shgroup_instance_bone_sphere_solid(g_data.passes.bone_solid, g_data.transparent);
 +      }
 +      if (g_data.bone_envelope_wire == NULL) {
 +              g_data.bone_envelope_wire = shgroup_instance_bone_envelope_outline(g_data.passes.bone_wire);
 +      }
 +      if (g_data.bone_envelope_solid == NULL) {
 +              g_data.bone_envelope_solid = shgroup_instance_bone_envelope_solid(g_data.passes.bone_solid, g_data.transparent);
 +              /* We can have a lot of overdraw if we don't do this. Also envelope are not subject to
 +               * inverted matrix. */
 +              DRW_shgroup_state_enable(g_data.bone_envelope_solid, DRW_STATE_CULL_BACK);
 +      }
 +
 +      float head_sphere[4] = {0.0f, 0.0f, 0.0f, 1.0f}, tail_sphere[4] = {0.0f, 1.0f, 0.0f, 1.0f};
 +      float final_bonemat[4][4];
 +      mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +      mul_m4_v4(final_bonemat, head_sphere);
 +      mul_m4_v4(final_bonemat, tail_sphere);
 +      head_sphere[3] = *radius_head;
 +      tail_sphere[3] = *radius_tail;
 +
 +      if (head_sphere[3] < 0.0f) {
 +              /* Draw Tail only */
 +              float tmp[4][4] = {{0.0f}};
 +              tmp[0][0] = tmp[1][1] = tmp[2][2] = tail_sphere[3] / PT_DEFAULT_RAD;
 +              tmp[3][3] = 1.0f;
 +              copy_v3_v3(tmp[3], tail_sphere);
 +              DRW_shgroup_call_dynamic_add(g_data.bone_point_solid, tmp, bone_color, hint_color);
 +              if (outline_color[3] > 0.0f) {
 +                      DRW_shgroup_call_dynamic_add(g_data.bone_point_wire, tmp, outline_color);
 +              }
 +      }
 +      else if (tail_sphere[3] < 0.0f) {
 +              /* Draw Head only */
 +              float tmp[4][4] = {{0.0f}};
 +              tmp[0][0] = tmp[1][1] = tmp[2][2] = head_sphere[3] / PT_DEFAULT_RAD;
 +              tmp[3][3] = 1.0f;
 +              copy_v3_v3(tmp[3], head_sphere);
 +              DRW_shgroup_call_dynamic_add(g_data.bone_point_solid, tmp, bone_color, hint_color);
 +              if (outline_color[3] > 0.0f) {
 +                      DRW_shgroup_call_dynamic_add(g_data.bone_point_wire, tmp, outline_color);
 +              }
 +      }
 +      else {
 +              /* Draw Body */
 +              float tmp_sphere[4];
 +              float len = len_v3v3(tail_sphere, head_sphere);
 +              float fac_head = (len - head_sphere[3]) / len;
 +              float fac_tail = (len - tail_sphere[3]) / len;
 +
 +              /* Small epsilon to avoid problem with float precision in shader. */
 +              if (len > (tail_sphere[3] + head_sphere[3]) + 1e-8f) {
 +
 +                      copy_v4_v4(tmp_sphere, head_sphere);
 +                      interp_v4_v4v4(head_sphere, tail_sphere, head_sphere, fac_head);
 +                      interp_v4_v4v4(tail_sphere, tmp_sphere,  tail_sphere, fac_tail);
 +                      DRW_shgroup_call_dynamic_add(
 +                              g_data.bone_envelope_solid, head_sphere, tail_sphere, bone_color, hint_color, final_bonemat[0]);
 +                      if (outline_color[3] > 0.0f) {
 +                              DRW_shgroup_call_dynamic_add(
 +                                      g_data.bone_envelope_wire, head_sphere, tail_sphere, outline_color, final_bonemat[0]);
 +                      }
 +              }
 +              else {
 +                      float tmp[4][4] = {{0.0f}};
 +                      float fac = max_ff(fac_head, 1.0f - fac_tail);
 +                      interp_v4_v4v4(tmp_sphere, tail_sphere, head_sphere, clamp_f(fac, 0.0f, 1.0f));
 +                      tmp[0][0] = tmp[1][1] = tmp[2][2] = tmp_sphere[3] / PT_DEFAULT_RAD;
 +                      tmp[3][3] = 1.0f;
 +                      copy_v3_v3(tmp[3], tmp_sphere);
 +                      DRW_shgroup_call_dynamic_add(g_data.bone_point_solid, tmp, bone_color, hint_color);
 +                      if (outline_color[3] > 0.0f) {
 +                              DRW_shgroup_call_dynamic_add(g_data.bone_point_wire, tmp, outline_color);
 +                      }
 +              }
 +      }
 +}
 +
 +/* Custom (geometry) */
 +
 +static void drw_shgroup_bone_custom_solid(
 +        const float (*bone_mat)[4],
 +        const float bone_color[4], const float hint_color[4], const float outline_color[4],
 +        Object *custom)
 +{
 +      /* grr, not re-using instances! */
 +      struct GPUBatch *surf = DRW_cache_object_surface_get(custom);
 +      struct GPUBatch *edges = DRW_cache_object_edge_detection_get(custom, NULL);
 +      struct GPUBatch *ledges = DRW_cache_object_loose_edges_get(custom);
 +      float final_bonemat[4][4];
 +
 +      if (surf || edges || ledges) {
 +              mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +      }
 +
 +      if (surf) {
 +              DRWShadingGroup *shgrp_geom_solid = shgroup_instance_bone_shape_solid(g_data.passes.bone_solid, surf,
 +                                                                                    g_data.transparent);
 +              DRW_shgroup_call_dynamic_add(shgrp_geom_solid, final_bonemat, bone_color, hint_color);
 +      }
 +
 +      if (edges && outline_color[3] > 0.0f) {
 +              DRWShadingGroup *shgrp_geom_wire = shgroup_instance_bone_shape_outline(g_data.passes.bone_outline, edges);
 +              DRW_shgroup_call_dynamic_add(shgrp_geom_wire, final_bonemat, outline_color);
 +      }
 +
 +      if (ledges) {
 +              DRWShadingGroup *shgrp_geom_ledges = shgroup_instance_wire(g_data.passes.bone_wire, ledges);
 +              float final_color[4];
 +              copy_v3_v3(final_color, outline_color);
 +              final_color[3] = 1.0f; /* hack */
 +              DRW_shgroup_call_dynamic_add(shgrp_geom_ledges, final_bonemat, final_color);
 +      }
 +}
 +
 +static void drw_shgroup_bone_custom_wire(
 +        const float (*bone_mat)[4],
 +        const float color[4], Object *custom)
 +{
 +      /* grr, not re-using instances! */
 +      struct GPUBatch *geom = DRW_cache_object_wire_outline_get(custom);
 +      if (geom) {
 +              DRWShadingGroup *shgrp_geom_wire = shgroup_instance_wire(g_data.passes.bone_wire, geom);
 +              float final_bonemat[4][4], final_color[4];
 +              mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +              copy_v3_v3(final_color, color);
 +              final_color[3] = 1.0f; /* hack */
 +              DRW_shgroup_call_dynamic_add(shgrp_geom_wire, final_bonemat, final_color);
 +      }
 +}
 +
 +/* Head and tail sphere */
 +static void drw_shgroup_bone_point(
 +        const float (*bone_mat)[4],
 +        const float bone_color[4], const float hint_color[4], const float outline_color[4])
 +{
 +      if (g_data.bone_point_wire == NULL) {
 +              g_data.bone_point_wire = shgroup_instance_bone_sphere_outline(g_data.passes.bone_wire);
 +      }
 +      if (g_data.bone_point_solid == NULL) {
 +              g_data.bone_point_solid = shgroup_instance_bone_sphere_solid(g_data.passes.bone_solid, g_data.transparent);
 +      }
 +      float final_bonemat[4][4];
 +      mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +      DRW_shgroup_call_dynamic_add(g_data.bone_point_solid, final_bonemat, bone_color, hint_color);
 +      if (outline_color[3] > 0.0f) {
 +              DRW_shgroup_call_dynamic_add(g_data.bone_point_wire, final_bonemat, outline_color);
 +      }
 +}
 +
 +/* Axes */
 +static void drw_shgroup_bone_axes(const float (*bone_mat)[4], const float color[4])
 +{
 +      if (g_data.bone_axes == NULL) {
 +              g_data.bone_axes = shgroup_instance_bone_axes(g_data.passes.bone_axes);
 +      }
 +      float final_bonemat[4][4];
 +      mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
 +      DRW_shgroup_call_dynamic_add(g_data.bone_axes, final_bonemat, color);
 +}
 +
 +/* Relationship lines */
 +static void drw_shgroup_bone_relationship_lines(const float start[3], const float end[3])
 +{
 +      if (g_data.lines_relationship == NULL) {
 +              g_data.lines_relationship = shgroup_dynlines_dashed_uniform_color(
 +                      g_data.passes.relationship_lines, g_theme.wire_color);
 +      }
 +      /* reverse order to have less stipple overlap */
 +      float v[3];
 +      mul_v3_m4v3(v, g_data.ob->obmat, end);
 +      DRW_shgroup_call_dynamic_add(g_data.lines_relationship, v);
 +      mul_v3_m4v3(v, g_data.ob->obmat, start);
 +      DRW_shgroup_call_dynamic_add(g_data.lines_relationship, v);
 +}
 +
 +static void drw_shgroup_bone_ik_lines(const float start[3], const float end[3])
 +{
 +      if (g_data.lines_ik == NULL) {
 +              static float fcolor[4] = {0.8f, 0.5f, 0.0f, 1.0f};  /* add theme! */
 +              g_data.lines_ik = shgroup_dynlines_dashed_uniform_color(g_data.passes.relationship_lines, fcolor);
 +      }
 +      /* reverse order to have less stipple overlap */
 +      float v[3];
 +      mul_v3_m4v3(v, g_data.ob->obmat, end);
 +      DRW_shgroup_call_dynamic_add(g_data.lines_ik, v);
 +      mul_v3_m4v3(v, g_data.ob->obmat, start);
 +      DRW_shgroup_call_dynamic_add(g_data.lines_ik, v);
 +}
 +
 +static void drw_shgroup_bone_ik_no_target_lines(const float start[3], const float end[3])
 +{
 +      if (g_data.lines_ik_no_target == NULL) {
 +              static float fcolor[4] = {0.8f, 0.8f, 0.2f, 1.0f};  /* add theme! */
 +              g_data.lines_ik_no_target = shgroup_dynlines_dashed_uniform_color(g_data.passes.relationship_lines, fcolor);
 +      }
 +      /* reverse order to have less stipple overlap */
 +      float v[3];
 +      mul_v3_m4v3(v, g_data.ob->obmat, end);
 +      DRW_shgroup_call_dynamic_add(g_data.lines_ik_no_target, v);
 +      mul_v3_m4v3(v, g_data.ob->obmat, start);
 +      DRW_shgroup_call_dynamic_add(g_data.lines_ik_no_target, v);
 +}
 +
 +static void drw_shgroup_bone_ik_spline_lines(const float start[3], const float end[3])
 +{
 +      if (g_data.lines_ik_spline == NULL) {
 +              static float fcolor[4] = {0.8f, 0.8f, 0.2f, 1.0f};  /* add theme! */
 +              g_data.lines_ik_spline = shgroup_dynlines_dashed_uniform_color(g_data.passes.relationship_lines, fcolor);
 +      }
 +      /* reverse order to have less stipple overlap */
 +      float v[3];
 +      mul_v3_m4v3(v, g_data.ob->obmat, end);
 +      DRW_shgroup_call_dynamic_add(g_data.lines_ik_spline, v);
 +      mul_v3_m4v3(v, g_data.ob->obmat, start);
 +      DRW_shgroup_call_dynamic_add(g_data.lines_ik_spline, v);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Drawing Theme Helpers
 + *
 + * Note, this section is duplicate of code in 'drawarmature.c'.
 + *
 + * \{ */
 +
 +/* global here is reset before drawing each bone */
 +static struct {
 +      const ThemeWireColor *bcolor;
 +} g_color;
 +
 +/* values of colCode for set_pchan_color */
 +enum {
 +      PCHAN_COLOR_NORMAL  = 0,        /* normal drawing */
 +      PCHAN_COLOR_SOLID,              /* specific case where "solid" color is needed */
 +      PCHAN_COLOR_CONSTS,             /* "constraint" colors (which may/may-not be suppressed) */
 +
 +      PCHAN_COLOR_SPHEREBONE_BASE,    /* for the 'stick' of sphere (envelope) bones */
 +      PCHAN_COLOR_SPHEREBONE_END,     /* for the ends of sphere (envelope) bones */
 +      PCHAN_COLOR_LINEBONE            /* for the middle of line-bones */
 +};
 +
 +/* This function sets the color-set for coloring a certain bone */
 +static void set_pchan_colorset(Object *ob, bPoseChannel *pchan)
 +{
 +      bPose *pose = (ob) ? ob->pose : NULL;
 +      bArmature *arm = (ob) ? ob->data : NULL;
 +      bActionGroup *grp = NULL;
 +      short color_index = 0;
 +
 +      /* sanity check */
 +      if (ELEM(NULL, ob, arm, pose, pchan)) {
 +              g_color.bcolor = NULL;
 +              return;
 +      }
 +
 +      /* only try to set custom color if enabled for armature */
 +      if (arm->flag & ARM_COL_CUSTOM) {
 +              /* currently, a bone can only use a custom color set if it's group (if it has one),
 +               * has been set to use one
 +               */
 +              if (pchan->agrp_index) {
 +                      grp = (bActionGroup *)BLI_findlink(&pose->agroups, (pchan->agrp_index - 1));
 +                      if (grp)
 +                              color_index = grp->customCol;
 +              }
 +      }
 +
 +      /* bcolor is a pointer to the color set to use. If NULL, then the default
 +       * color set (based on the theme colors for 3d-view) is used.
 +       */
 +      if (color_index > 0) {
 +              bTheme *btheme = UI_GetTheme();
 +              g_color.bcolor = &btheme->tarm[(color_index - 1)];
 +      }
 +      else if (color_index == -1) {
 +              /* use the group's own custom color set (grp is always != NULL here) */
 +              g_color.bcolor = &grp->cs;
 +      }
 +      else {
 +              g_color.bcolor = NULL;
 +      }
 +}
 +
 +/* This function is for brightening/darkening a given color (like UI_GetThemeColorShade3ubv()) */
 +static void cp_shade_color3ub(uchar cp[3], const int offset)
 +{
 +      int r, g, b;
 +
 +      r = offset + (int) cp[0];
 +      CLAMP(r, 0, 255);
 +      g = offset + (int) cp[1];
 +      CLAMP(g, 0, 255);
 +      b = offset + (int) cp[2];
 +      CLAMP(b, 0, 255);
 +
 +      cp[0] = r;
 +      cp[1] = g;
 +      cp[2] = b;
 +}
 +
 +static void cp_shade_color3f(float cp[3], const float offset)
 +{
 +      add_v3_fl(cp, offset);
 +      CLAMP(cp[0], 0, 255);
 +      CLAMP(cp[1], 0, 255);
 +      CLAMP(cp[2], 0, 255);
 +}
 +
 +
 +/* This function sets the gl-color for coloring a certain bone (based on bcolor) */
 +static bool set_pchan_color(short colCode, const int boneflag, const short constflag, float r_color[4])
 +{
 +      float *fcolor = r_color;
 +      const ThemeWireColor *bcolor = g_color.bcolor;
 +
 +      switch (colCode) {
 +              case PCHAN_COLOR_NORMAL:
 +              {
 +                      if (bcolor) {
 +                              uchar cp[4] = {255};
 +
 +                              if (boneflag & BONE_DRAW_ACTIVE) {
 +                                      copy_v3_v3_char((char *)cp, bcolor->active);
 +                                      if (!(boneflag & BONE_SELECTED)) {
 +                                              cp_shade_color3ub(cp, -80);
 +                                      }
 +                              }
 +                              else if (boneflag & BONE_SELECTED) {
 +                                      copy_v3_v3_char((char *)cp, bcolor->select);
 +                              }
 +                              else {
 +                                      /* a bit darker than solid */
 +                                      copy_v3_v3_char((char *)cp, bcolor->solid);
 +                                      cp_shade_color3ub(cp, -50);
 +                              }
 +
 +                              rgb_uchar_to_float(fcolor, cp);
 +                      }
 +                      else {
 +                              if ((boneflag & BONE_DRAW_ACTIVE) && (boneflag & BONE_SELECTED)) {
 +                                      UI_GetThemeColor4fv(TH_BONE_POSE_ACTIVE, fcolor);
 +                              }
 +                              else if (boneflag & BONE_DRAW_ACTIVE) {
 +                                      UI_GetThemeColorBlendShade4fv(TH_WIRE, TH_BONE_POSE, 0.15f, 0, fcolor);
 +                              }
 +                              else if (boneflag & BONE_SELECTED) {
 +                                      UI_GetThemeColor4fv(TH_BONE_POSE, fcolor);
 +                              }
 +                              else {
 +                                      UI_GetThemeColor4fv(TH_WIRE, fcolor);
 +                              }
 +                      }
 +
 +                      return true;
 +              }
 +              case PCHAN_COLOR_SOLID:
 +              {
 +                      UI_GetThemeColor4fv(TH_BONE_SOLID, fcolor);
 +
 +                      if (bcolor) {
 +                              float solid_bcolor[3];
 +                              rgb_uchar_to_float(solid_bcolor, (uchar *)bcolor->solid);
 +                              interp_v3_v3v3(fcolor, fcolor, solid_bcolor, 1.0f);
 +                      }
 +
 +                      return true;
 +              }
 +              case PCHAN_COLOR_CONSTS:
 +              {
 +                      if ((bcolor == NULL) || (bcolor->flag & TH_WIRECOLOR_CONSTCOLS)) {
 +                              uchar cp[4];
 +                              if (constflag & PCHAN_HAS_TARGET) rgba_char_args_set((char *)cp, 255, 150, 0, 80);
 +                              else if (constflag & PCHAN_HAS_IK) rgba_char_args_set((char *)cp, 255, 255, 0, 80);
 +                              else if (constflag & PCHAN_HAS_SPLINEIK) rgba_char_args_set((char *)cp, 200, 255, 0, 80);
 +                              else if (constflag & PCHAN_HAS_CONST) rgba_char_args_set((char *)cp, 0, 255, 120, 80);
 +                              else {
 +                                      return false;
 +                              }
 +
 +                              rgba_uchar_to_float(fcolor, cp);
 +
 +                              return true;
 +                      }
 +                      return false;
 +              }
 +              case PCHAN_COLOR_SPHEREBONE_BASE:
 +              {
 +                      if (bcolor) {
 +                              uchar cp[4] = {255};
 +
 +                              if (boneflag & BONE_DRAW_ACTIVE) {
 +                                      copy_v3_v3_char((char *)cp, bcolor->active);
 +                              }
 +                              else if (boneflag & BONE_SELECTED) {
 +                                      copy_v3_v3_char((char *)cp, bcolor->select);
 +                              }
 +                              else {
 +                                      copy_v3_v3_char((char *)cp, bcolor->solid);
 +                              }
 +
 +                              rgb_uchar_to_float(fcolor, cp);
 +                      }
 +                      else {
 +                              if (boneflag & BONE_DRAW_ACTIVE) {
 +                                      UI_GetThemeColorShade4fv(TH_BONE_POSE, 40, fcolor);
 +                              }
 +                              else if (boneflag & BONE_SELECTED) {
 +                                      UI_GetThemeColor4fv(TH_BONE_POSE, fcolor);
 +                              }
 +                              else {
 +                                      UI_GetThemeColor4fv(TH_BONE_SOLID, fcolor);
 +                              }
 +                      }
 +
 +                      return true;
 +              }
 +              case PCHAN_COLOR_SPHEREBONE_END:
 +              {
 +                      if (bcolor) {
 +                              uchar cp[4] = {255};
 +
 +                              if (boneflag & BONE_DRAW_ACTIVE) {
 +                                      copy_v3_v3_char((char *)cp, bcolor->active);
 +                                      cp_shade_color3ub(cp, 10);
 +                              }
 +                              else if (boneflag & BONE_SELECTED) {
 +                                      copy_v3_v3_char((char *)cp, bcolor->select);
 +                                      cp_shade_color3ub(cp, -30);
 +                              }
 +                              else {
 +                                      copy_v3_v3_char((char *)cp, bcolor->solid);
 +                                      cp_shade_color3ub(cp, -30);
 +                              }
 +
 +                              rgb_uchar_to_float(fcolor, cp);
 +                      }
 +                      else {
 +                              if (boneflag & BONE_DRAW_ACTIVE) {
 +                                      UI_GetThemeColorShade4fv(TH_BONE_POSE, 10, fcolor);
 +                              }
 +                              else if (boneflag & BONE_SELECTED) {
 +                                      UI_GetThemeColorShade4fv(TH_BONE_POSE, -30, fcolor);
 +                              }
 +                              else {
 +                                      UI_GetThemeColorShade4fv(TH_BONE_SOLID, -30, fcolor);
 +                              }
 +                      }
 +                      break;
 +              }
 +              case PCHAN_COLOR_LINEBONE:
 +              {
 +                      /* inner part in background color or constraint */
 +                      if ((constflag) && ((bcolor == NULL) || (bcolor->flag & TH_WIRECOLOR_CONSTCOLS))) {
 +                              uchar cp[4];
 +                              if (constflag & PCHAN_HAS_TARGET) rgba_char_args_set((char *)cp, 255, 150, 0, 255);
 +                              else if (constflag & PCHAN_HAS_IK) rgba_char_args_set((char *)cp, 255, 255, 0, 255);
 +                              else if (constflag & PCHAN_HAS_SPLINEIK) rgba_char_args_set((char *)cp, 200, 255, 0, 255);
 +                              else if (constflag & PCHAN_HAS_CONST) rgba_char_args_set((char *)cp, 0, 255, 120, 255);
 +                              else if (constflag) UI_GetThemeColor4ubv(TH_BONE_POSE, cp);  /* PCHAN_HAS_ACTION */
 +
 +                              rgb_uchar_to_float(fcolor, cp);
 +                      }
 +                      else {
 +                              if (bcolor) {
 +                                      const char *cp = bcolor->solid;
 +                                      rgb_uchar_to_float(fcolor, (uchar *)cp);
 +                                      fcolor[3] = 204.f / 255.f;
 +                              }
 +                              else {
 +                                      UI_GetThemeColorShade4fv(TH_BACK, -30, fcolor);
 +                              }
 +                      }
 +
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Drawing Color Helpers
 + * \{ */
 +
 +/** See: 'set_pchan_color'*/
 +static void update_color(const Object *ob, const float const_color[4])
 +{
 +      g_theme.const_color = const_color;
 +      g_theme.const_wire = ((ob->base_flag & BASE_SELECTED) != 0) ? 1.5f : 0.0f;
 +
 +#define NO_ALPHA(c) (((c)[3] = 1.0f), (c))
 +
 +      UI_GetThemeColor3fv(TH_SELECT, NO_ALPHA(g_theme.select_color));
 +      UI_GetThemeColor3fv(TH_EDGE_SELECT, NO_ALPHA(g_theme.edge_select_color));
 +      UI_GetThemeColorShade3fv(TH_EDGE_SELECT, -20, NO_ALPHA(g_theme.bone_select_color));
 +      UI_GetThemeColor3fv(TH_WIRE, NO_ALPHA(g_theme.wire_color));
 +      UI_GetThemeColor3fv(TH_WIRE_EDIT, NO_ALPHA(g_theme.wire_edit_color));
 +      UI_GetThemeColor3fv(TH_BONE_SOLID, NO_ALPHA(g_theme.bone_solid_color));
 +      UI_GetThemeColorBlendShade3fv(TH_WIRE_EDIT, TH_EDGE_SELECT, 0.15f, 0, NO_ALPHA(g_theme.bone_active_unselect_color));
 +      UI_GetThemeColor3fv(TH_BONE_POSE, NO_ALPHA(g_theme.bone_pose_color));
 +      UI_GetThemeColor3fv(TH_BONE_POSE_ACTIVE, NO_ALPHA(g_theme.bone_pose_active_color));
 +      UI_GetThemeColorBlendShade3fv(TH_WIRE, TH_BONE_POSE, 0.15f, 0, NO_ALPHA(g_theme.bone_pose_active_unselect_color));
 +      UI_GetThemeColor3fv(TH_TEXT_HI, NO_ALPHA(g_theme.text_hi_color));
 +      UI_GetThemeColor3fv(TH_TEXT, NO_ALPHA(g_theme.text_color));
 +      UI_GetThemeColor3fv(TH_VERTEX_SELECT, NO_ALPHA(g_theme.vertex_select_color));
 +      UI_GetThemeColor3fv(TH_VERTEX, NO_ALPHA(g_theme.vertex_color));
 +
 +#undef NO_ALPHA
 +}
 +
 +static const float *get_bone_solid_color(
 +        const EditBone *UNUSED(eBone), const bPoseChannel *pchan, const bArmature *arm,
 +        const int boneflag, const short constflag)
 +{
 +      if (g_theme.const_color)
 +              return g_theme.bone_solid_color;
 +
 +      if (arm->flag & ARM_POSEMODE) {
 +              static float disp_color[4];
 +              copy_v4_v4(disp_color, pchan->draw_data->solid_color);
 +              set_pchan_color(PCHAN_COLOR_SOLID, boneflag, constflag, disp_color);
 +              return disp_color;
 +      }
 +
 +      return g_theme.bone_solid_color;
 +}
 +
 +static const float *get_bone_solid_with_consts_color(
 +        const EditBone *eBone, const bPoseChannel *pchan, const bArmature *arm,
 +        const int boneflag, const short constflag)
 +{
 +      if (g_theme.const_color)
 +              return g_theme.bone_solid_color;
 +
 +      const float *col = get_bone_solid_color(eBone, pchan, arm, boneflag, constflag);
 +
 +      static float consts_color[4];
 +      if (set_pchan_color(PCHAN_COLOR_CONSTS, boneflag, constflag, consts_color)) {
 +              interp_v3_v3v3(consts_color, col, consts_color, 0.5f);
 +      }
 +      else {
 +              copy_v4_v4(consts_color, col);
 +      }
 +      return consts_color;
 +}
 +
 +static float get_bone_wire_thickness(int boneflag)
 +{
 +      if (g_theme.const_color)
 +              return g_theme.const_wire;
 +      else if (boneflag & (BONE_DRAW_ACTIVE | BONE_SELECTED))
 +              return 2.0f;
 +      else
 +              return 1.0f;
 +}
 +
 +static const float *get_bone_wire_color(
 +        const EditBone *eBone, const bPoseChannel *pchan, const bArmature *arm,
 +        const int boneflag, const short constflag)
 +{
 +      static float disp_color[4];
 +
 +      if (g_theme.const_color) {
 +              copy_v3_v3(disp_color, g_theme.const_color);
 +      }
 +      else if (eBone) {
 +              if (boneflag & BONE_SELECTED) {
 +                      if (boneflag & BONE_DRAW_ACTIVE) {
 +                              copy_v3_v3(disp_color, g_theme.edge_select_color);
 +                      }
 +                      else {
 +                              copy_v3_v3(disp_color, g_theme.bone_select_color);
 +                      }
 +              }
 +              else {
 +                      if (boneflag & BONE_DRAW_ACTIVE) {
 +                              copy_v3_v3(disp_color, g_theme.bone_active_unselect_color);
 +                      }
 +                      else {
 +                              copy_v3_v3(disp_color, g_theme.wire_edit_color);
 +                      }
 +              }
 +      }
 +      else if (arm->flag & ARM_POSEMODE) {
 +              copy_v4_v4(disp_color, pchan->draw_data->wire_color);
 +              set_pchan_color(PCHAN_COLOR_NORMAL, boneflag, constflag, disp_color);
 +      }
 +      else {
 +              copy_v3_v3(disp_color, g_theme.vertex_color);
 +      }
 +
 +      disp_color[3] = get_bone_wire_thickness(boneflag);
 +
 +      return disp_color;
 +}
 +
 +#define HINT_MUL 0.5f
 +#define HINT_SHADE 0.2f
 +
 +static void bone_hint_color_shade(float hint_color[4], const float color[4])
 +{
 +      mul_v3_v3fl(hint_color, color, HINT_MUL);
 +      cp_shade_color3f(hint_color, -HINT_SHADE);
 +      hint_color[3] = 1.0f;
 +}
 +
 +static const float *get_bone_hint_color(
 +        const EditBone *eBone, const bPoseChannel *pchan, const bArmature *arm,
 +        const int boneflag, const short constflag)
 +{
 +      static float hint_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
 +
 +      if (g_theme.const_color) {
 +              bone_hint_color_shade(hint_color, g_theme.bone_solid_color);
 +      }
 +      else {
 +              const float *wire_color = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
 +              bone_hint_color_shade(hint_color, wire_color);
 +      }
 +
 +      return hint_color;
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Helper Utils
 + * \{ */
 +
 +static void pchan_draw_data_init(bPoseChannel *pchan)
 +{
 +      if (pchan->draw_data != NULL) {
 +              if (pchan->draw_data->bbone_matrix_len != pchan->bone->segments) {
 +                      MEM_SAFE_FREE(pchan->draw_data);
 +              }
 +      }
 +
 +      if (pchan->draw_data == NULL) {
 +              pchan->draw_data = MEM_mallocN(sizeof(*pchan->draw_data) + sizeof(Mat4) * pchan->bone->segments, __func__);
 +              pchan->draw_data->bbone_matrix_len = pchan->bone->segments;
 +      }
 +}
 +
 +static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *pchan)
 +{
 +      float s[4][4], ebmat[4][4];
 +      float length;
 +      float (*bone_mat)[4];
 +      float (*disp_mat)[4];
 +      float (*disp_tail_mat)[4];
 +
 +      /* TODO : This should be moved to depsgraph or armature refresh
 +       * and not be tight to the draw pass creation.
 +       * This would refresh armature without invalidating the draw cache */
 +      if (pchan) {
 +              length = pchan->bone->length;
 +              bone_mat = pchan->pose_mat;
 +              disp_mat = pchan->disp_mat;
 +              disp_tail_mat = pchan->disp_tail_mat;
 +      }
 +      else {
 +              eBone->length = len_v3v3(eBone->tail, eBone->head);
 +              ED_armature_ebone_to_mat4(eBone, ebmat);
 +
 +              length = eBone->length;
 +              bone_mat = ebmat;
 +              disp_mat = eBone->disp_mat;
 +              disp_tail_mat = eBone->disp_tail_mat;
 +      }
 +
 +      scale_m4_fl(s, length);
 +      mul_m4_m4m4(disp_mat, bone_mat, s);
 +      copy_m4_m4(disp_tail_mat, disp_mat);
 +      translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f);
 +}
 +
 +/* compute connected child pointer for B-Bone drawing */
 +static void edbo_compute_bbone_child(bArmature *arm)
 +{
 +      EditBone *eBone;
 +
 +      for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
 +              eBone->bbone_child = NULL;
 +      }
 +
 +      for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
 +              if (eBone->parent && (eBone->flag & BONE_CONNECTED)) {
 +                      eBone->parent->bbone_child = eBone;
 +              }
 +      }
 +}
 +
-       ebone->segments = BKE_compute_b_bone_spline(&param, (Mat4 *)result_array);
++/* A version of BKE_pchan_bbone_spline_setup() for previewing editmode curve settings. */
 +static void ebone_spline_preview(EditBone *ebone, float result_array[MAX_BBONE_SUBDIV][4][4])
 +{
 +      BBoneSplineParameters param;
 +      EditBone *prev, *next;
 +      float imat[4][4], bonemat[4][4];
 +      float tmp[3];
 +
 +      memset(&param, 0, sizeof(param));
 +
 +      param.segments = ebone->segments;
 +      param.length = ebone->length;
 +
 +      /* Get "next" and "prev" bones - these are used for handle calculations. */
 +      if (ebone->bbone_prev_type == BBONE_HANDLE_AUTO) {
 +              /* Use connected parent. */
 +              if (ebone->flag & BONE_CONNECTED) {
 +                      prev = ebone->parent;
 +              }
 +              else {
 +                      prev = NULL;
 +              }
 +      }
 +      else {
 +              prev = ebone->bbone_prev;
 +      }
 +
 +      if (ebone->bbone_next_type == BBONE_HANDLE_AUTO) {
 +              /* Use connected child. */
 +              next = ebone->bbone_child;
 +      }
 +      else {
 +              next = ebone->bbone_next;
 +      }
 +
 +      /* compute handles from connected bones */
 +      if (prev || next) {
 +              ED_armature_ebone_to_mat4(ebone, imat);
 +              invert_m4(imat);
 +
 +              if (prev) {
 +                      param.use_prev = true;
 +
 +                      if (ebone->bbone_prev_type == BBONE_HANDLE_RELATIVE) {
 +                              zero_v3(param.prev_h);
 +                      }
 +                      else if (ebone->bbone_prev_type == BBONE_HANDLE_TANGENT) {
 +                              sub_v3_v3v3(tmp, prev->tail, prev->head);
 +                              sub_v3_v3v3(tmp, ebone->head, tmp);
 +                              mul_v3_m4v3(param.prev_h, imat, tmp);
 +                      }
 +                      else {
 +                              param.prev_bbone = (prev->segments > 1);
 +
 +                              mul_v3_m4v3(param.prev_h, imat, prev->head);
 +                      }
 +
 +                      if (!param.prev_bbone) {
 +                              ED_armature_ebone_to_mat4(prev, bonemat);
 +                              mul_m4_m4m4(param.prev_mat, imat, bonemat);
 +                      }
 +              }
 +
 +              if (next) {
 +                      param.use_next = true;
 +
 +                      if (ebone->bbone_next_type == BBONE_HANDLE_RELATIVE) {
 +                              copy_v3_fl3(param.next_h, 0.0f, param.length, 0.0);
 +                      }
 +                      else if (ebone->bbone_next_type == BBONE_HANDLE_TANGENT) {
 +                              sub_v3_v3v3(tmp, next->tail, next->head);
 +                              add_v3_v3v3(tmp, ebone->tail, tmp);
 +                              mul_v3_m4v3(param.next_h, imat, tmp);
 +                      }
 +                      else {
 +                              param.next_bbone = (next->segments > 1);
 +
 +                              mul_v3_m4v3(param.next_h, imat, next->tail);
 +                      }
 +
 +                      ED_armature_ebone_to_mat4(next, bonemat);
 +                      mul_m4_m4m4(param.next_mat, imat, bonemat);
 +              }
 +      }
 +
 +      param.ease1 = ebone->ease1;
 +      param.ease2 = ebone->ease2;
 +      param.roll1 = ebone->roll1;
 +      param.roll2 = ebone->roll2;
 +
 +      if (prev && (ebone->flag & BONE_ADD_PARENT_END_ROLL)) {
 +              param.roll1 += prev->roll2;
 +      }
 +
 +      param.scaleIn = ebone->scaleIn;
 +      param.scaleOut = ebone->scaleOut;
 +
 +      param.curveInX = ebone->curveInX;
 +      param.curveInY = ebone->curveInY;
 +
 +      param.curveOutX = ebone->curveOutX;
 +      param.curveOutY = ebone->curveOutY;
 +
-                               b_bone_spline_setup(pchan, false, bbones_mat);
++      ebone->segments = BKE_pchan_bbone_spline_compute(&param, (Mat4 *)result_array);
 +}
 +
 +static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pchan)
 +{
 +      float s[4][4], ebmat[4][4];
 +      float length, xwidth, zwidth;
 +      float (*bone_mat)[4];
 +      short bbone_segments;
 +
 +      /* TODO : This should be moved to depsgraph or armature refresh
 +       * and not be tight to the draw pass creation.
 +       * This would refresh armature without invalidating the draw cache */
 +      if (pchan) {
 +              length = pchan->bone->length;
 +              xwidth = pchan->bone->xwidth;
 +              zwidth = pchan->bone->zwidth;
 +              bone_mat = pchan->pose_mat;
 +              bbone_segments = pchan->bone->segments;
 +      }
 +      else {
 +              eBone->length = len_v3v3(eBone->tail, eBone->head);
 +              ED_armature_ebone_to_mat4(eBone, ebmat);
 +
 +              length = eBone->length;
 +              xwidth = eBone->xwidth;
 +              zwidth = eBone->zwidth;
 +              bone_mat = ebmat;
 +              bbone_segments = eBone->segments;
 +      }
 +
 +      size_to_mat4(s, (const float[3]){xwidth, length / bbone_segments, zwidth});
 +
 +      /* Compute BBones segment matrices... */
 +      /* Note that we need this even for one-segment bones, because box drawing need specific weirdo matrix for the box,
 +       * that we cannot use to draw end points & co. */
 +      if (pchan) {
 +              Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix;
 +              if (bbone_segments > 1) {
 +                      if (bbone_segments == pchan->runtime.bbone_segments) {
 +                              memcpy(bbones_mat, pchan->runtime.bbone_pose_mats, sizeof(Mat4) * bbone_segments);
 +                      }
 +                      else {
++                              BKE_pchan_bbone_spline_setup(pchan, false, bbones_mat);
 +                      }
 +
 +                      for (int i = bbone_segments; i--; bbones_mat++) {
 +                              mul_m4_m4m4(bbones_mat->mat, bbones_mat->mat, s);
 +                              mul_m4_m4m4(bbones_mat->mat, bone_mat, bbones_mat->mat);
 +                      }
 +              }
 +              else {
 +                      mul_m4_m4m4(bbones_mat->mat, bone_mat, s);
 +              }
 +      }
 +      else {
 +              float (*bbones_mat)[4][4] = eBone->disp_bbone_mat;
 +
 +              if (bbone_segments > 1) {
 +                      ebone_spline_preview(eBone, bbones_mat);
 +
 +                      for (int i = bbone_segments; i--; bbones_mat++) {
 +                              mul_m4_m4m4(*bbones_mat, *bbones_mat, s);
 +                              mul_m4_m4m4(*bbones_mat, bone_mat, *bbones_mat);
 +                      }
 +              }
 +              else {
 +                      mul_m4_m4m4(*bbones_mat, bone_mat, s);
 +              }
 +      }
 +
 +      /* Grrr... We need default display matrix to draw end points, axes, etc. :( */
 +      draw_bone_update_disp_matrix_default(eBone, pchan);
 +}
 +
 +static void draw_bone_update_disp_matrix_custom(bPoseChannel *pchan)
 +{
 +      float s[4][4];
 +      float length;
 +      float (*bone_mat)[4];
 +      float (*disp_mat)[4];
 +      float (*disp_tail_mat)[4];
 +
 +      /* See TODO above */
 +      length = PCHAN_CUSTOM_DRAW_SIZE(pchan);
 +      bone_mat = pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat;
 +      disp_mat = pchan->disp_mat;
 +      disp_tail_mat = pchan->disp_tail_mat;
 +
 +      scale_m4_fl(s, length);
 +      mul_m4_m4m4(disp_mat, bone_mat, s);
 +      copy_m4_m4(disp_tail_mat, disp_mat);
 +      translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f);
 +}
 +
 +static void draw_axes(EditBone *eBone, bPoseChannel *pchan)
 +{
 +      float final_col[4];
 +      const float *col = (g_theme.const_color) ? g_theme.const_color :
 +                         (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? g_theme.text_hi_color : g_theme.text_color;
 +      copy_v4_v4(final_col, col);
 +      /* Mix with axes color. */
 +      final_col[3] = (g_theme.const_color) ? 1.0 : (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? 0.3 : 0.8;
 +      drw_shgroup_bone_axes(BONE_VAR(eBone, pchan, disp_mat), final_col);
 +}
 +
 +static void draw_points(
 +        const EditBone *eBone, const bPoseChannel *pchan, const bArmature *arm,
 +        const int boneflag, const short constflag,
 +        const int select_id)
 +{
 +      float col_solid_root[4], col_solid_tail[4], col_wire_root[4], col_wire_tail[4];
 +      float col_hint_root[4], col_hint_tail[4];
 +
 +      copy_v4_v4(col_solid_root, g_theme.bone_solid_color);
 +      copy_v4_v4(col_solid_tail, g_theme.bone_solid_color);
 +      copy_v4_v4(col_wire_root, (g_theme.const_color) ? g_theme.const_color : g_theme.vertex_color);
 +      copy_v4_v4(col_wire_tail, (g_theme.const_color) ? g_theme.const_color : g_theme.vertex_color);
 +
 +      const bool is_envelope_draw = (arm->drawtype == ARM_ENVELOPE);
 +      static const float envelope_ignore = -1.0f;
 +
 +      col_wire_tail[3] = col_wire_root[3] = get_bone_wire_thickness(boneflag);
 +
 +      /* Edit bone points can be selected */
 +      if (eBone) {
 +              if (eBone->flag & BONE_ROOTSEL) {
 +                      copy_v3_v3(col_wire_root, g_theme.vertex_select_color);
 +              }
 +              if (eBone->flag & BONE_TIPSEL) {
 +                      copy_v3_v3(col_wire_tail, g_theme.vertex_select_color);
 +              }
 +      }
 +      else if (arm->flag & ARM_POSEMODE) {
 +              const float *solid_color = get_bone_solid_color(eBone, pchan, arm, boneflag, constflag);
 +              const float *wire_color = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
 +              copy_v4_v4(col_wire_tail, wire_color);
 +              copy_v4_v4(col_wire_root, wire_color);
 +              copy_v4_v4(col_solid_tail, solid_color);
 +              copy_v4_v4(col_solid_root, solid_color);
 +      }
 +
 +      bone_hint_color_shade(col_hint_root, (g_theme.const_color) ? col_solid_root : col_wire_root);
 +      bone_hint_color_shade(col_hint_tail, (g_theme.const_color) ? col_solid_tail : col_wire_tail);
 +
 +      /*      Draw root point if we are not connected and parent are not hidden */
 +      if ((BONE_FLAG(eBone, pchan) & BONE_CONNECTED) == 0) {
 +              if (select_id != -1) {
 +                      DRW_select_load_id(select_id | BONESEL_ROOT);
 +              }
 +
 +              if (eBone) {
 +                      if (!((eBone->parent) && !EBONE_VISIBLE(arm, eBone->parent))) {
 +                              if (is_envelope_draw) {
 +                                      drw_shgroup_bone_envelope(
 +                                              eBone->disp_mat, col_solid_root, col_hint_root, col_wire_root,
 +                                              &eBone->rad_head, &envelope_ignore);
 +                              }
 +                              else {
 +                                      drw_shgroup_bone_point(eBone->disp_mat, col_solid_root, col_hint_root, col_wire_root);
 +                              }
 +                      }
 +              }
 +              else {
 +                      Bone *bone = pchan->bone;
 +                      if (!((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)))) {
 +                              if (is_envelope_draw) {
 +                                      drw_shgroup_bone_envelope(
 +                                              pchan->disp_mat, col_solid_root, col_hint_root, col_wire_root,
 +                                              &bone->rad_head, &envelope_ignore);
 +                              }
 +                              else {
 +                                      drw_shgroup_bone_point(pchan->disp_mat, col_solid_root, col_hint_root, col_wire_root);
 +                              }
 +                      }
 +              }
 +      }
 +
 +      /*      Draw tip point */
 +      if (select_id != -1) {
 +              DRW_select_load_id(select_id | BONESEL_TIP);
 +      }
 +
 +      if (is_envelope_draw) {
 +              const float *rad_tail = eBone ? &eBone->rad_tail : &pchan->bone->rad_tail;
 +              drw_shgroup_bone_envelope(
 +                      BONE_VAR(eBone, pchan, disp_mat), col_solid_tail, col_hint_tail, col_wire_tail,
 +                      &envelope_ignore, rad_tail);
 +      }
 +      else {
 +              drw_shgroup_bone_point(BONE_VAR(eBone, pchan, disp_tail_mat), col_solid_tail, col_hint_tail, col_wire_tail);
 +      }
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(-1);
 +      }
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Draw Bones
 + * \{ */
 +
 +static void draw_bone_custom_shape(
 +        EditBone *eBone, bPoseChannel *pchan, bArmature *arm,
 +        const int boneflag, const short constflag,
 +        const int select_id)
 +{
 +      const float *col_solid = get_bone_solid_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_wire = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_hint = get_bone_hint_color(eBone, pchan, arm, boneflag, constflag);
 +      const float (*disp_mat)[4] = pchan->disp_mat;
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(select_id | BONESEL_BONE);
 +      }
 +
 +      if ((boneflag & BONE_DRAWWIRE) == 0) {
 +              drw_shgroup_bone_custom_solid(disp_mat, col_solid, col_hint, col_wire, pchan->custom);
 +      }
 +      else {
 +              drw_shgroup_bone_custom_wire(disp_mat, col_wire, pchan->custom);
 +      }
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(-1);
 +      }
 +}
 +
 +static void draw_bone_envelope(
 +        EditBone *eBone, bPoseChannel *pchan, bArmature *arm,
 +        const int boneflag, const short constflag,
 +        const int select_id)
 +{
 +      const float *col_solid = get_bone_solid_with_consts_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_wire = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_hint = get_bone_hint_color(eBone, pchan, arm, boneflag, constflag);
 +
 +      float *rad_head, *rad_tail, *distance;
 +      if (eBone) {
 +              rad_tail = &eBone->rad_tail;
 +              distance = &eBone->dist;
 +              rad_head = (eBone->parent && (boneflag & BONE_CONNECTED)) ? &eBone->parent->rad_tail : &eBone->rad_head;
 +      }
 +      else {
 +              rad_tail = &pchan->bone->rad_tail;
 +              distance = &pchan->bone->dist;
 +              rad_head = (pchan->parent && (boneflag & BONE_CONNECTED)) ? &pchan->parent->bone->rad_tail : &pchan->bone->rad_head;
 +      }
 +
 +      if ((select_id == -1) &&
 +          (boneflag & BONE_NO_DEFORM) == 0 &&
 +          ((boneflag & BONE_SELECTED) || (eBone && (boneflag & (BONE_ROOTSEL | BONE_TIPSEL)))))
 +      {
 +              drw_shgroup_bone_envelope_distance(BONE_VAR(eBone, pchan, disp_mat), rad_head, rad_tail, distance);
 +      }
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(select_id | BONESEL_BONE);
 +      }
 +
 +      drw_shgroup_bone_envelope(
 +              BONE_VAR(eBone, pchan, disp_mat), col_solid, col_hint, col_wire,
 +              rad_head, rad_tail);
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(-1);
 +      }
 +
 +      draw_points(eBone, pchan, arm, boneflag, constflag, select_id);
 +}
 +
 +static void draw_bone_line(
 +        EditBone *eBone, bPoseChannel *pchan, bArmature *arm,
 +        const int boneflag, const short constflag, const int select_id)
 +{
 +      const float *col_bone = get_bone_solid_with_consts_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_wire = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
 +      const float no_display[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 +      const float *col_head = no_display;
 +      const float *col_tail = col_bone;
 +
 +      if (eBone) {
 +              if (eBone->flag & BONE_TIPSEL) {
 +                      col_tail = g_theme.vertex_select_color;
 +              }
 +              if (boneflag & BONE_SELECTED) {
 +                      col_bone = g_theme.edge_select_color;
 +              }
 +              col_wire = g_theme.wire_color;
 +      }
 +
 +      /*      Draw root point if we are not connected and parent are not hidden */
 +      if ((BONE_FLAG(eBone, pchan) & BONE_CONNECTED) == 0) {
 +              if (eBone && !(eBone->parent && !EBONE_VISIBLE(arm, eBone->parent))) {
 +                      col_head = (eBone->flag & BONE_ROOTSEL) ? g_theme.vertex_select_color : col_bone;
 +              }
 +              else if (pchan) {
 +                      Bone *bone = pchan->bone;
 +                      if (!(bone->parent && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)))) {
 +                              col_head = col_bone;
 +                      }
 +              }
 +      }
 +
 +      if (g_theme.const_color != NULL) {
 +              col_wire = no_display; /* actually shrink the display. */
 +              col_bone = col_head = col_tail = g_theme.const_color;
 +      }
 +
 +      if (select_id == -1) {
 +              /* Not in selection mode, draw everything at once. */
 +              drw_shgroup_bone_stick(BONE_VAR(eBone, pchan, disp_mat), col_wire, col_bone, col_head, col_tail);
 +      }
 +      else {
 +              /* In selection mode, draw bone, root and tip separately. */
 +              DRW_select_load_id(select_id | BONESEL_BONE);
 +              drw_shgroup_bone_stick(BONE_VAR(eBone, pchan, disp_mat), col_wire, col_bone, no_display, no_display);
 +
 +              if (col_head[3] > 0.0f) {
 +                      DRW_select_load_id(select_id | BONESEL_ROOT);
 +                      drw_shgroup_bone_stick(BONE_VAR(eBone, pchan, disp_mat), col_wire, no_display, col_head, no_display);
 +              }
 +
 +              DRW_select_load_id(select_id | BONESEL_TIP);
 +              drw_shgroup_bone_stick(BONE_VAR(eBone, pchan, disp_mat), col_wire, no_display, no_display, col_tail);
 +
 +              DRW_select_load_id(-1);
 +      }
 +}
 +
 +static void draw_bone_wire(
 +        EditBone *eBone, bPoseChannel *pchan, bArmature *arm,
 +        const int boneflag, const short constflag,
 +        const int select_id)
 +{
 +      const float *col_wire = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(select_id | BONESEL_BONE);
 +      }
 +
 +      if (pchan) {
 +              Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix;
 +              BLI_assert(bbones_mat != NULL);
 +
 +              for (int i = pchan->bone->segments; i--; bbones_mat++) {
 +                      drw_shgroup_bone_wire(bbones_mat->mat, col_wire);
 +              }
 +      }
 +      else if (eBone) {
 +              for (int i = 0; i < eBone->segments; i++) {
 +                      drw_shgroup_bone_wire(eBone->disp_bbone_mat[i], col_wire);
 +              }
 +      }
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(-1);
 +      }
 +
 +      if (eBone) {
 +              draw_points(eBone, pchan, arm, boneflag, constflag, select_id);
 +      }
 +}
 +
 +static void draw_bone_box(
 +        EditBone *eBone, bPoseChannel *pchan, bArmature *arm,
 +        const int boneflag, const short constflag,
 +        const int select_id)
 +{
 +      const float *col_solid = get_bone_solid_with_consts_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_wire = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_hint = get_bone_hint_color(eBone, pchan, arm, boneflag, constflag);
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(select_id | BONESEL_BONE);
 +      }
 +
 +      if (pchan) {
 +              Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix;
 +              BLI_assert(bbones_mat != NULL);
 +
 +              for (int i = pchan->bone->segments; i--; bbones_mat++) {
 +                      drw_shgroup_bone_box(bbones_mat->mat, col_solid, col_hint, col_wire);
 +              }
 +      }
 +      else if (eBone) {
 +              for (int i = 0; i < eBone->segments; i++) {
 +                      drw_shgroup_bone_box(eBone->disp_bbone_mat[i], col_solid, col_hint, col_wire);
 +              }
 +      }
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(-1);
 +      }
 +
 +      if (eBone) {
 +              draw_points(eBone, pchan, arm, boneflag, constflag, select_id);
 +      }
 +}
 +
 +static void draw_bone_octahedral(
 +        EditBone *eBone, bPoseChannel *pchan, bArmature *arm,
 +        const int boneflag, const short constflag,
 +        const int select_id)
 +{
 +      const float *col_solid = get_bone_solid_with_consts_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_wire = get_bone_wire_color(eBone, pchan, arm, boneflag, constflag);
 +      const float *col_hint = get_bone_hint_color(eBone, pchan, arm, boneflag, constflag);
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(select_id | BONESEL_BONE);
 +      }
 +
 +      drw_shgroup_bone_octahedral(BONE_VAR(eBone, pchan, disp_mat), col_solid, col_hint, col_wire);
 +
 +      if (select_id != -1) {
 +              DRW_select_load_id(-1);
 +      }
 +
 +      draw_points(eBone, pchan, arm, boneflag, constflag, select_id);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Draw Degrees of Freedom
 + * \{ */
 +
 +static void draw_bone_dofs(bPoseChannel *pchan)
 +{
 +      float final_bonemat[4][4], posetrans[4][4], mat[4][4];
 +      float amin[2], amax[2], xminmax[2], zminmax[2];
 +      float col_sphere[4] = {0.25f, 0.25f, 0.25f, 0.25f};
 +      float col_lines[4] = {0.0f, 0.0f, 0.0f, 1.0f};
 +      float col_xaxis[4] = {1.0f, 0.0f, 0.0f, 1.0f};
 +      float col_zaxis[4] = {0.0f, 0.0f, 1.0f, 1.0f};
 +
 +      if (g_data.passes.bone_envelope == NULL) {
 +              return;
 +      }
 +
 +      if (g_data.bone_dof_sphere == NULL) {
 +              g_data.bone_dof_lines = shgroup_instance_bone_dof(g_data.passes.bone_wire, DRW_cache_bone_dof_lines_get());
 +              g_data.bone_dof_sphere = shgroup_instance_bone_dof(g_data.passes.bone_envelope, DRW_cache_bone_dof_sphere_get());
 +              DRW_shgroup_state_enable(g_data.bone_dof_sphere, DRW_STATE_BLEND);
 +              DRW_shgroup_state_disable(g_data.bone_dof_sphere, DRW_STATE_CULL_FRONT);
 +      }
 +
 +      /* *0.5f here comes from M_PI/360.0f when rotations were still in degrees */
 +      xminmax[0] = sinf(pchan->limitmin[0] * 0.5f);
 +      xminmax[1] = sinf(pchan->limitmax[0] * 0.5f);
 +      zminmax[0] = sinf(pchan->limitmin[2] * 0.5f);
 +      zminmax[1] = sinf(pchan->limitmax[2] * 0.5f);
 +
 +      unit_m4(posetrans);
 +      translate_m4(posetrans, pchan->pose_mat[3][0], pchan->pose_mat[3][1], pchan->pose_mat[3][2]);
 +      /* in parent-bone pose space... */
 +      if (pchan->parent) {
 +              copy_m4_m4(mat, pchan->parent->pose_mat);
 +              mat[3][0] = mat[3][1] = mat[3][2] = 0.0f;
 +              mul_m4_m4m4(posetrans, posetrans, mat);
 +      }
 +      /* ... but own restspace */
 +      mul_m4_m4m3(posetrans, posetrans, pchan->bone->bone_mat);
 +
 +      float scale = pchan->bone->length * pchan->size[1];
 +      scale_m4_fl(mat, scale);
 +      mat[1][1] = -mat[1][1];
 +      mul_m4_m4m4(posetrans, posetrans, mat);
 +
 +      /* into world space. */
 +      mul_m4_m4m4(final_bonemat, g_data.ob->obmat, posetrans);
 +
 +      if ((pchan->ikflag & BONE_IK_XLIMIT) &&
 +          (pchan->ikflag & BONE_IK_ZLIMIT))
 +      {
 +              amin[0] = xminmax[0];
 +              amax[0] = xminmax[1];
 +              amin[1] = zminmax[0];
 +              amax[1] = zminmax[1];
 +              DRW_shgroup_call_dynamic_add(g_data.bone_dof_sphere, final_bonemat, col_sphere, amin, amax);
 +              DRW_shgroup_call_dynamic_add(g_data.bone_dof_lines, final_bonemat, col_lines, amin, amax);
 +      }
 +      if (pchan->ikflag & BONE_IK_XLIMIT) {
 +              amin[0] = xminmax[0];
 +              amax[0] = xminmax[1];
 +              amin[1] = amax[1] = 0.0f;
 +              DRW_shgroup_call_dynamic_add(g_data.bone_dof_lines, final_bonemat, col_xaxis, amin, amax);
 +      }
 +      if (pchan->ikflag & BONE_IK_ZLIMIT) {
 +              amin[1] = zminmax[0];
 +              amax[1] = zminmax[1];
 +              amin[0] = amax[0] = 0.0f;
 +              DRW_shgroup_call_dynamic_add(g_data.bone_dof_lines, final_bonemat, col_zaxis, amin, amax);
 +      }
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Draw Relationships
 + * \{ */
 +
 +static void pchan_draw_ik_lines(bPoseChannel *pchan, const bool only_temp, const int constflag)
 +{
 +      bConstraint *con;
 +      bPoseChannel *parchan;
 +      float *line_start = NULL, *line_end = NULL;
 +
 +      for (con = pchan->constraints.first; con; con = con->next) {
 +              if (con->enforce == 0.0f)
 +                      continue;
 +
 +              switch (con->type) {
 +                      case CONSTRAINT_TYPE_KINEMATIC:
 +                      {
 +                              bKinematicConstraint *data = (bKinematicConstraint *)con->data;
 +                              int segcount = 0;
 +
 +                              /* if only_temp, only draw if it is a temporary ik-chain */
 +                              if (only_temp && !(data->flag & CONSTRAINT_IK_TEMP))
 +                                      continue;
 +
 +                              /* exclude tip from chain? */
 +                              parchan = ((data->flag & CONSTRAINT_IK_TIP) == 0) ? pchan->parent : pchan;
 +                              line_start = parchan->pose_tail;
 +
 +                              /* Find the chain's root */
 +                              while (parchan->parent) {
 +                                      segcount++;
 +                                      if (segcount == data->rootbone || segcount > 255) {
 +                                              break;  /* 255 is weak */
 +                                      }
 +                                      parchan = parchan->parent;
 +                              }
 +
 +                              if (parchan) {
 +                                      line_end = parchan->pose_head;
 +
 +                                      if (constflag & PCHAN_HAS_TARGET)
 +                                              drw_shgroup_bone_ik_lines(line_start, line_end);
 +                                      else
 +                                              drw_shgroup_bone_ik_no_target_lines(line_start, line_end);
 +                              }
 +                              break;
 +                      }
 +                      case CONSTRAINT_TYPE_SPLINEIK:
 +                      {
 +                              bSplineIKConstraint *data = (bSplineIKConstraint *)con->data;
 +                              int segcount = 0;
 +
 +                              /* don't draw if only_temp, as Spline IK chains cannot be temporary */
 +                              if (only_temp)
 +                                      continue;
 +
 +                              parchan = pchan;
 +                              line_start = parchan->pose_tail;
 +
 +                              /* Find the chain's root */
 +                              while (parchan->parent) {
 +                                      segcount++;
 +                                      /* FIXME: revise the breaking conditions */
 +                                      if (segcount == data->chainlen || segcount > 255) break;  /* 255 is weak */
 +                                      parchan = parchan->parent;
 +                              }
 +                              /* Only draw line in case our chain is more than one bone long! */
 +                              if (parchan != pchan) { /* XXX revise the breaking conditions to only stop at the tail? */
 +                                      line_end = parchan->pose_head;
 +                                      drw_shgroup_bone_ik_spline_lines(line_start, line_end);
 +                              }
 +                              break;
 +                      }
 +              }
 +      }
 +}
 +
 +static void draw_bone_relations(
 +        EditBone *ebone, bPoseChannel *pchan, bArmature *arm,
 +        const int boneflag, const short constflag, const bool do_relations)
 +{
 +      if (g_data.passes.relationship_lines) {
 +              if (ebone && ebone->parent) {
 +                      if (do_relations) {
 +                              /* Always draw for unconnected bones, regardless of selection,
 +                               * since riggers will want to know about the links between bones
 +                               */
 +                              if ((boneflag & BONE_CONNECTED) == 0) {
 +                                      drw_shgroup_bone_relationship_lines(ebone->head, ebone->parent->tail);
 +                              }
 +                      }
 +              }
 +              else if (pchan && pchan->parent) {
 +                      if (do_relations) {
 +                              /* Only draw if bone or its parent is selected - reduces viewport complexity with complex rigs */
 +                              if ((boneflag & BONE_SELECTED) ||
 +                                  (pchan->parent->bone && (pchan->parent->bone->flag & BONE_SELECTED)))
 +                              {
 +                                      if ((boneflag & BONE_CONNECTED) == 0) {
 +                                              drw_shgroup_bone_relationship_lines(pchan->pose_head, pchan->parent->pose_tail);
 +                                      }
 +                              }
 +                      }
 +
 +                      /* Draw a line to IK root bone if bone is selected. */
 +                      if (arm->flag & ARM_POSEMODE) {
 +                              if (constflag & (PCHAN_HAS_IK | PCHAN_HAS_SPLINEIK)) {
 +                                      if (boneflag & BONE_SELECTED) {
 +                                              pchan_draw_ik_lines(pchan, !do_relations, constflag);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Main Draw Loops
 + * \{ */
 +
 +static void draw_armature_edit(Object *ob)
 +{
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      EditBone *eBone;
 +      bArmature *arm = ob->data;
 +      int index;
 +      const bool is_select = DRW_state_is_select();
 +
 +      update_color(ob, NULL);
 +      edbo_compute_bbone_child(arm);
 +
 +      const bool show_text = DRW_state_show_text();
 +      const bool show_relations = ((draw_ctx->v3d->flag & V3D_HIDE_HELPLINES) == 0);
 +
 +      for (eBone = arm->edbo->first, index = ob->select_color; eBone; eBone = eBone->next, index += 0x10000) {
 +              if (eBone->layer & arm->layer) {
 +                      if ((eBone->flag & BONE_HIDDEN_A) == 0) {
 +                              const int select_id = is_select ? index : (uint)-1;
 +
 +                              const short constflag = 0;
 +
 +                              /* catch exception for bone with hidden parent */
 +                              int boneflag = eBone->flag;
 +                              if ((eBone->parent) && !EBONE_VISIBLE(arm, eBone->parent)) {
 +                                      boneflag &= ~BONE_CONNECTED;
 +                              }
 +
 +                              /* set temporary flag for drawing bone as active, but only if selected */
 +                              if (eBone == arm->act_edbone) {
 +                                      boneflag |= BONE_DRAW_ACTIVE;
 +                              }
 +
 +                              draw_bone_relations(eBone, NULL, arm, boneflag, constflag, show_relations);
 +
 +                              if (arm->drawtype == ARM_ENVELOPE) {
 +                                      draw_bone_update_disp_matrix_default(eBone, NULL);
 +                                      draw_bone_envelope(eBone, NULL, arm, boneflag, constflag, select_id);
 +                              }
 +                              else if (arm->drawtype == ARM_LINE) {
 +                                      draw_bone_update_disp_matrix_default(eBone, NULL);
 +                                      draw_bone_line(eBone, NULL, arm, boneflag, constflag, select_id);
 +                              }
 +                              else if (arm->drawtype == ARM_WIRE) {
 +                                      draw_bone_update_disp_matrix_bbone(eBone, NULL);
 +                                      draw_bone_wire(eBone, NULL, arm, boneflag, constflag, select_id);
 +                              }
 +                              else if (arm->drawtype == ARM_B_BONE) {
 +                                      draw_bone_update_disp_matrix_bbone(eBone, NULL);
 +                                      draw_bone_box(eBone, NULL, arm, boneflag, constflag, select_id);
 +                              }
 +                              else {
 +                                      draw_bone_update_disp_matrix_default(eBone, NULL);
 +                                      draw_bone_octahedral(eBone, NULL, arm, boneflag, constflag, select_id);
 +                              }
 +
 +                              /* Draw names of bone */
 +                              if (show_text && (arm->flag & ARM_DRAWNAMES)) {
 +                                      uchar color[4];
 +                                      UI_GetThemeColor4ubv((eBone->flag & BONE_SELECTED) ? TH_TEXT_HI : TH_TEXT, color);
 +
 +                                      float vec[3];
 +                                      mid_v3_v3v3(vec, eBone->head, eBone->tail);
 +                                      mul_m4_v3(ob->obmat, vec);
 +
 +                                      struct DRWTextStore *dt = DRW_text_cache_ensure();
 +                                      DRW_text_cache_add(
 +                                              dt, vec, eBone->name, strlen(eBone->name),
 +                                              10, 0, DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, color);
 +                              }
 +
 +                              /*      Draw additional axes */
 +                              if (arm->flag & ARM_DRAWAXES) {
 +                                      draw_axes(eBone, NULL);
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +/* if const_color is NULL do pose mode coloring */
 +static void draw_armature_pose(Object *ob, const float const_color[4])
 +{
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      bArmature *arm = ob->data;
 +      bPoseChannel *pchan;
 +      int index = -1;
 +      Bone *bone;
 +
 +      update_color(ob, const_color);
 +
 +      /* We can't safely draw non-updated pose, might contain NULL bone pointers... */
 +      if (ob->pose->flag & POSE_RECALC) {
 +              return;
 +      }
 +
 +      // if (!(base->flag & OB_FROMDUPLI)) // TODO
 +      {
 +              if ((draw_ctx->object_mode & OB_MODE_POSE) || (ob == draw_ctx->object_pose)) {
 +                      arm->flag |= ARM_POSEMODE;
 +              }
 +
 +              if (arm->flag & ARM_POSEMODE) {
 +                      index = ob->select_color;
 +              }
 +      }
 +
 +      const bool is_pose_select = (arm->flag & ARM_POSEMODE) && DRW_state_is_select();
 +      const bool show_text = DRW_state_show_text();
 +      const bool show_relations = ((draw_ctx->v3d->flag & V3D_HIDE_HELPLINES) == 0);
 +
 +      /* being set below */
 +      for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
 +              bone = pchan->bone;
 +
 +              /* bone must be visible */
 +              if ((bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)) == 0) {
 +                      if (bone->layer & arm->layer) {
 +                              const int select_id = is_pose_select ? index : (uint)-1;
 +
 +                              const short constflag = pchan->constflag;
 +
 +                              pchan_draw_data_init(pchan);
 +
 +                              if (const_color) {
 +                                      /* keep color */
 +                              }
 +                              else {
 +                                      /* set color-set to use */
 +                                      set_pchan_colorset(ob, pchan);
 +                              }
 +
 +                              int boneflag = bone->flag;
 +                              /* catch exception for bone with hidden parent */
 +                              boneflag = bone->flag;
 +                              if ((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) {
 +                                      boneflag &= ~BONE_CONNECTED;
 +                              }
 +
 +                              /* set temporary flag for drawing bone as active, but only if selected */
 +                              if (bone == arm->act_bone)
 +                                      boneflag |= BONE_DRAW_ACTIVE;
 +
 +                              draw_bone_relations(NULL, pchan, arm, boneflag, constflag, show_relations);
 +
 +                              if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) {
 +                                      draw_bone_update_disp_matrix_custom(pchan);
 +                                      draw_bone_custom_shape(NULL, pchan, arm, boneflag, constflag, select_id);
 +                              }
 +                              else if (arm->drawtype == ARM_ENVELOPE) {
 +                                      draw_bone_update_disp_matrix_default(NULL, pchan);
 +                                      draw_bone_envelope(NULL, pchan, arm, boneflag, constflag, select_id);
 +                              }
 +                              else if (arm->drawtype == ARM_LINE) {
 +                                      draw_bone_update_disp_matrix_default(NULL, pchan);
 +                                      draw_bone_line(NULL, pchan, arm, boneflag, constflag, select_id);
 +                              }
 +                              else if (arm->drawtype == ARM_WIRE) {
 +                                      draw_bone_update_disp_matrix_bbone(NULL, pchan);
 +                                      draw_bone_wire(NULL, pchan, arm, boneflag, constflag, select_id);
 +                              }
 +                              else if (arm->drawtype == ARM_B_BONE) {
 +                                      draw_bone_update_disp_matrix_bbone(NULL, pchan);
 +                                      draw_bone_box(NULL, pchan, arm, boneflag, constflag, select_id);
 +                              }
 +                              else {
 +                                      draw_bone_update_disp_matrix_default(NULL, pchan);
 +                                      draw_bone_octahedral(NULL, pchan, arm, boneflag, constflag, select_id);
 +                              }
 +
 +                              if (!is_pose_select && show_relations &&
 +                                  (arm->flag & ARM_POSEMODE) &&
 +                                  (bone->flag & BONE_SELECTED) &&
 +                                  ((ob->base_flag & BASE_FROMDUPLI) == 0) &&
 +                                  (pchan->ikflag & (BONE_IK_XLIMIT | BONE_IK_ZLIMIT)))
 +                              {
 +                                      draw_bone_dofs(pchan);
 +                              }
 +
 +                              /* Draw names of bone */
 +                              if (show_text && (arm->flag & ARM_DRAWNAMES)) {
 +                                      uchar color[4];
 +                                      UI_GetThemeColor4ubv((arm->flag & ARM_POSEMODE) &&
 +                                                           (bone->flag & BONE_SELECTED) ? TH_TEXT_HI : TH_TEXT, color);
 +                                      float vec[3];
 +                                      mid_v3_v3v3(vec, pchan->pose_head, pchan->pose_tail);
 +                                      mul_m4_v3(ob->obmat, vec);
 +
 +                                      struct DRWTextStore *dt = DRW_text_cache_ensure();
 +                                      DRW_text_cache_add(
 +                                              dt, vec, pchan->name, strlen(pchan->name),
 +                                              10, 0, DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, color);
 +                              }
 +
 +                              /*      Draw additional axes */
 +                              if (arm->flag & ARM_DRAWAXES) {
 +                                      draw_axes(NULL, pchan);
 +                              }
 +                      }
 +              }
 +              if (is_pose_select) {
 +                      index += 0x10000;
 +              }
 +      }
 +
 +      arm->flag &= ~ARM_POSEMODE;
 +}
 +
 +/**
 + * This function set the object space to use for all subsequent `DRW_shgroup_bone_*` calls.
 + */
 +static void drw_shgroup_armature(Object *ob, DRWArmaturePasses passes, bool transp)
 +{
 +      memset(&g_data, 0x0, sizeof(g_data));
 +      g_data.ob = ob;
 +      g_data.passes = passes;
 +      g_data.transparent = transp;
 +      memset(&g_color, 0x0, sizeof(g_color));
 +}
 +
 +void DRW_shgroup_armature_object(Object *ob, ViewLayer *view_layer, DRWArmaturePasses passes)
 +{
 +      float *color;
 +      DRW_object_wire_theme_get(ob, view_layer, &color);
 +      passes.bone_envelope = NULL; /* Don't do envelope distance in object mode. */
 +      drw_shgroup_armature(ob, passes, false);
 +      draw_armature_pose(ob, color);
 +}
 +
 +void DRW_shgroup_armature_pose(Object *ob, DRWArmaturePasses passes, bool transp)
 +{
 +      drw_shgroup_armature(ob, passes, transp);
 +      draw_armature_pose(ob, NULL);
 +}
 +
 +void DRW_shgroup_armature_edit(Object *ob, DRWArmaturePasses passes, bool transp)
 +{
 +      drw_shgroup_armature(ob, passes, transp);
 +      draw_armature_edit(ob);
 +}
 +
 +/** \} */
@@@ -326,7 -322,7 +326,7 @@@ static void add_verts_to_dgroups
                                if ((par->pose) && (pchan = BKE_pose_channel_find_name(par->pose, bone->name))) {
                                        if (bone->segments > 1) {
                                                segments = bone->segments;
-                                               b_bone_spline_setup(pchan, true, bbone_array);
 -                                              BKE_pchan_bbone_spline_setup(pchan, 1, bbone_array);
++                                              BKE_pchan_bbone_spline_setup(pchan, true, bbone_array);
                                                bbone = bbone_array;
                                        }
                                }
index d69f64f,0000000..d629840
mode 100644,000000..100644
--- /dev/null
@@@ -1,678 -1,0 +1,678 @@@
-                                       b_bone_spline_setup(pchan, true, bbone_array);
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * The Original Code is Copyright (C) 2018, Blender Foundation
 + * This is a new part of Blender
 + *
 + * Contributor(s): Antonio Vazquez
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + *
 + * Operators for dealing with armatures and GP datablocks
 + */
 +
 +/** \file blender/editors/gpencil/gpencil_armature.c
 + *  \ingroup edgpencil
 + */
 +
 +
 +#include <stdio.h>
 +#include <string.h>
 +#include <stdlib.h>
 +#include <stddef.h>
 +#include <math.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_utildefines.h"
 +#include "BLI_ghash.h"
 +#include "BLI_math.h"
 +#include "BLI_string_utils.h"
 +
 +#include "BLT_translation.h"
 +
 +#include "DNA_armature_types.h"
 +#include "DNA_gpencil_types.h"
 +#include "DNA_meshdata_types.h"
 +#include "DNA_scene_types.h"
 +
 +#include "BKE_main.h"
 +#include "BKE_action.h"
 +#include "BKE_armature.h"
 +#include "BKE_context.h"
 +#include "BKE_deform.h"
 +#include "BKE_gpencil.h"
 +#include "BKE_gpencil_modifier.h"
 +#include "BKE_object_deform.h"
 +#include "BKE_report.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +
 +#include "RNA_access.h"
 +#include "RNA_define.h"
 +#include "RNA_enum_types.h"
 +
 +#include "ED_gpencil.h"
 +#include "ED_object.h"
 +#include "ED_mesh.h"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_query.h"
 +
 +#include "gpencil_intern.h"
 +
 +enum {
 +      GP_ARMATURE_NAME = 0,
 +      GP_ARMATURE_AUTO = 1
 +};
 +
 +#define DEFAULT_RATIO 0.10f
 +#define DEFAULT_DECAY 0.8f
 +
 +static int gpencil_bone_looper(
 +        Object *ob, Bone *bone, void *data,
 +        int(*bone_func)(Object *, Bone *, void *))
 +{
 +      /* We want to apply the function bone_func to every bone
 +       * in an armature -- feed bone_looper the first bone and
 +       * a pointer to the bone_func and watch it go!. The int count
 +       * can be useful for counting bones with a certain property
 +       * (e.g. skinnable)
 +       */
 +      int count = 0;
 +
 +      if (bone) {
 +              /* only do bone_func if the bone is non null */
 +              count += bone_func(ob, bone, data);
 +
 +              /* try to execute bone_func for the first child */
 +              count += gpencil_bone_looper(ob, bone->childbase.first, data, bone_func);
 +
 +              /* try to execute bone_func for the next bone at this
 +               * depth of the recursion.
 +               */
 +              count += gpencil_bone_looper(ob, bone->next, data, bone_func);
 +      }
 +
 +      return count;
 +}
 +
 +static int gpencil_bone_skinnable_cb(Object *UNUSED(ob), Bone *bone, void *datap)
 +{
 +      /* Bones that are deforming
 +       * are regarded to be "skinnable" and are eligible for
 +       * auto-skinning.
 +       *
 +       * This function performs 2 functions:
 +       *
 +       *   a) It returns 1 if the bone is skinnable.
 +       *      If we loop over all bones with this
 +       *      function, we can count the number of
 +       *      skinnable bones.
 +       *   b) If the pointer data is non null,
 +       *      it is treated like a handle to a
 +       *      bone pointer -- the bone pointer
 +       *      is set to point at this bone, and
 +       *      the pointer the handle points to
 +       *      is incremented to point to the
 +       *      next member of an array of pointers
 +       *      to bones. This way we can loop using
 +       *      this function to construct an array of
 +       *      pointers to bones that point to all
 +       *      skinnable bones.
 +       */
 +      Bone ***hbone;
 +      int a, segments;
 +      struct { Object *armob; void *list; int heat;} *data = datap;
 +
 +      if (!(bone->flag & BONE_HIDDEN_P)) {
 +              if (!(bone->flag & BONE_NO_DEFORM)) {
 +                      if (data->heat && data->armob->pose &&
 +                          BKE_pose_channel_find_name(data->armob->pose, bone->name))
 +                      {
 +                              segments = bone->segments;
 +                      }
 +                      else {
 +                              segments = 1;
 +                      }
 +
 +                      if (data->list != NULL) {
 +                              hbone = (Bone ***)&data->list;
 +
 +                              for (a = 0; a < segments; a++) {
 +                                      **hbone = bone;
 +                                      (*hbone)++;
 +                              }
 +                      }
 +                      return segments;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static int vgroup_add_unique_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr))
 +{
 +      /* This group creates a vertex group to ob that has the
 +       * same name as bone (provided the bone is skinnable).
 +       * If such a vertex group already exist the routine exits.
 +       */
 +      if (!(bone->flag & BONE_NO_DEFORM)) {
 +              if (!defgroup_find_name(ob, bone->name)) {
 +                      BKE_object_defgroup_add_name(ob, bone->name);
 +                      return 1;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap)
 +{
 +      /* Bones that are deforming
 +       * are regarded to be "skinnable" and are eligible for
 +       * auto-skinning.
 +       *
 +       * This function performs 2 functions:
 +       *
 +       *   a) If the bone is skinnable, it creates
 +       *      a vertex group for ob that has
 +       *      the name of the skinnable bone
 +       *      (if one doesn't exist already).
 +       *   b) If the pointer data is non null,
 +       *      it is treated like a handle to a
 +       *      bDeformGroup pointer -- the
 +       *      bDeformGroup pointer is set to point
 +       *      to the deform group with the bone's
 +       *      name, and the pointer the handle
 +       *      points to is incremented to point to the
 +       *      next member of an array of pointers
 +       *      to bDeformGroups. This way we can loop using
 +       *      this function to construct an array of
 +       *      pointers to bDeformGroups, all with names
 +       *      of skinnable bones.
 +       */
 +      bDeformGroup ***hgroup, *defgroup = NULL;
 +      int a, segments;
 +      struct { Object *armob; void *list; int heat; } *data = datap;
 +      bArmature *arm = data->armob->data;
 +
 +      if (!(bone->flag & BONE_HIDDEN_P)) {
 +              if (!(bone->flag & BONE_NO_DEFORM)) {
 +                      if (data->heat && data->armob->pose &&
 +                          BKE_pose_channel_find_name(data->armob->pose, bone->name))
 +                      {
 +                              segments = bone->segments;
 +                      }
 +                      else {
 +                              segments = 1;
 +                      }
 +
 +                      if (arm->layer & bone->layer) {
 +                              if (!(defgroup = defgroup_find_name(ob, bone->name))) {
 +                                      defgroup = BKE_object_defgroup_add_name(ob, bone->name);
 +                              }
 +                              else if (defgroup->flag & DG_LOCK_WEIGHT) {
 +                                      /* In case vgroup already exists and is locked, do not modify it here. See T43814. */
 +                                      defgroup = NULL;
 +                              }
 +                      }
 +
 +                      if (data->list != NULL) {
 +                              hgroup = (bDeformGroup ***)&data->list;
 +
 +                              for (a = 0; a < segments; a++) {
 +                                      **hgroup = defgroup;
 +                                      (*hgroup)++;
 +                              }
 +                      }
 +                      return segments;
 +              }
 +      }
 +      return 0;
 +}
 +
 +/* get weight value depending of distance and decay value */
 +static float get_weight(float dist, float decay_rad, float dif_rad)
 +{
 +      float weight = 1.0f;
 +      if (dist < decay_rad) {
 +              weight = 1.0f;
 +      }
 +      else {
 +              weight = interpf(0.0f, 0.9f, (dist - decay_rad) / dif_rad);
 +      }
 +
 +      return weight;
 +}
 +
 +/* This functions implements the automatic computation of vertex group weights */
 +static void gpencil_add_verts_to_dgroups(
 +        const bContext *C, Object *ob, Object *ob_arm, const float ratio, const float decay)
 +{
 +      bArmature *arm = ob_arm->data;
 +      Bone **bonelist, *bone;
 +      bDeformGroup **dgrouplist;
 +      bPoseChannel *pchan;
 +      bGPdata *gpd = (bGPdata *)ob->data;
 +      const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
 +
 +      Mat4 bbone_array[MAX_BBONE_SUBDIV], *bbone = NULL;
 +      float(*root)[3], (*tip)[3], (*verts)[3];
 +      float *radsqr;
 +      int *selected;
 +      float weight;
 +      int numbones, i, j, segments = 0;
 +      struct { Object *armob; void *list; int heat; } looper_data;
 +
 +      looper_data.armob = ob_arm;
 +      looper_data.heat = true;
 +      looper_data.list = NULL;
 +
 +      /* count the number of skinnable bones */
 +      numbones = gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, gpencil_bone_skinnable_cb);
 +
 +      if (numbones == 0)
 +              return;
 +
 +      /* create an array of pointer to bones that are skinnable
 +       * and fill it with all of the skinnable bones */
 +      bonelist = MEM_callocN(numbones * sizeof(Bone *), "bonelist");
 +      looper_data.list = bonelist;
 +      gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, gpencil_bone_skinnable_cb);
 +
 +      /* create an array of pointers to the deform groups that
 +       * correspond to the skinnable bones (creating them
 +       * as necessary. */
 +      dgrouplist = MEM_callocN(numbones * sizeof(bDeformGroup *), "dgrouplist");
 +
 +      looper_data.list = dgrouplist;
 +      gpencil_bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable_cb);
 +
 +      /* create an array of root and tip positions transformed into
 +       * global coords */
 +      root = MEM_callocN(numbones * sizeof(float) * 3, "root");
 +      tip = MEM_callocN(numbones * sizeof(float) * 3, "tip");
 +      selected = MEM_callocN(numbones * sizeof(int), "selected");
 +      radsqr = MEM_callocN(numbones * sizeof(float), "radsqr");
 +
 +      for (j = 0; j < numbones; j++) {
 +              bone = bonelist[j];
 +
 +              /* handle bbone */
 +              if (segments == 0) {
 +                      segments = 1;
 +                      bbone = NULL;
 +
 +                      if ((ob_arm->pose) &&
 +                          (pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name)))
 +                      {
 +                              if (bone->segments > 1) {
 +                                      segments = bone->segments;
++                                      BKE_pchan_bbone_spline_setup(pchan, true, bbone_array);
 +                                      bbone = bbone_array;
 +                              }
 +                      }
 +              }
 +
 +              segments--;
 +
 +              /* compute root and tip */
 +              if (bbone) {
 +                      mul_v3_m4v3(root[j], bone->arm_mat, bbone[segments].mat[3]);
 +                      if ((segments + 1) < bone->segments) {
 +                              mul_v3_m4v3(tip[j], bone->arm_mat, bbone[segments + 1].mat[3]);
 +                      }
 +                      else {
 +                              copy_v3_v3(tip[j], bone->arm_tail);
 +                      }
 +              }
 +              else {
 +                      copy_v3_v3(root[j], bone->arm_head);
 +                      copy_v3_v3(tip[j], bone->arm_tail);
 +              }
 +
 +              mul_m4_v3(ob_arm->obmat, root[j]);
 +              mul_m4_v3(ob_arm->obmat, tip[j]);
 +
 +              selected[j] = 1;
 +
 +              /* calculate radius squared */
 +              radsqr[j] = len_squared_v3v3(root[j], tip[j]) * ratio;
 +      }
 +
 +      /* loop all strokes */
 +      for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 +              bGPDframe *init_gpf = gpl->actframe;
 +              bGPDspoint *pt = NULL;
 +
 +              if (is_multiedit) {
 +                      init_gpf = gpl->frames.first;
 +              }
 +
 +              for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
 +                      if ((gpf == gpl->actframe) ||
 +                          ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit)))
 +                      {
 +
 +                              if (gpf == NULL)
 +                                      continue;
 +
 +                              for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
 +                                      /* skip strokes that are invalid for current view */
 +                                      if (ED_gpencil_stroke_can_use(C, gps) == false)
 +                                              continue;
 +
 +                                      BKE_gpencil_dvert_ensure(gps);
 +
 +                                      /* create verts array */
 +                                      verts = MEM_callocN(gps->totpoints * sizeof(*verts), __func__);
 +
 +                                      /* transform stroke points to global space */
 +                                      for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
 +                                              copy_v3_v3(verts[i], &pt->x);
 +                                              mul_m4_v3(ob->obmat, verts[i]);
 +                                      }
 +
 +                                      /* loop groups and assign weight */
 +                                      for (j = 0; j < numbones; j++) {
 +                                              int def_nr = BLI_findindex(&ob->defbase, dgrouplist[j]);
 +                                              if (def_nr < 0) {
 +                                                      continue;
 +                                              }
 +
 +                                              float decay_rad = radsqr[j] - (radsqr[j] * decay);
 +                                              float dif_rad = radsqr[j] - decay_rad;
 +
 +                                              for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
 +                                                      MDeformVert *dvert = &gps->dvert[i];
 +                                                      float dist = dist_squared_to_line_segment_v3(verts[i], root[j], tip[j]);
 +                                                      if (dist > radsqr[j]) {
 +                                                              /* if not in cylinder, check if inside extreme spheres */
 +                                                              weight = 0.0f;
 +                                                              dist = len_squared_v3v3(root[j], verts[i]);
 +                                                              if (dist < radsqr[j]) {
 +                                                                      weight = get_weight(dist, decay_rad, dif_rad);
 +                                                              }
 +                                                              else {
 +                                                                      dist = len_squared_v3v3(tip[j], verts[i]);
 +                                                                      if (dist < radsqr[j]) {
 +                                                                              weight = get_weight(dist, decay_rad, dif_rad);
 +                                                                      }
 +                                                              }
 +                                                      }
 +                                                      else {
 +                                                              /* inside bone cylinder */
 +                                                              weight = get_weight(dist, decay_rad, dif_rad);
 +                                                      }
 +
 +                                                      /* assign weight */
 +                                                      MDeformWeight *dw = defvert_verify_index(dvert, def_nr);
 +                                                      if (dw) {
 +                                                              dw->weight = weight;
 +                                                      }
 +                                              }
 +                                      }
 +                                      MEM_SAFE_FREE(verts);
 +                              }
 +                      }
 +
 +                      /* if not multiedit, exit loop*/
 +                      if (!is_multiedit) {
 +                              break;
 +                      }
 +              }
 +      }
 +
 +      /* free the memory allocated */
 +      MEM_SAFE_FREE(bonelist);
 +      MEM_SAFE_FREE(dgrouplist);
 +      MEM_SAFE_FREE(root);
 +      MEM_SAFE_FREE(tip);
 +      MEM_SAFE_FREE(radsqr);
 +      MEM_SAFE_FREE(selected);
 +}
 +
 +static void gpencil_object_vgroup_calc_from_armature(
 +        const bContext *C,
 +        Object *ob, Object *ob_arm,
 +        const int mode, const float ratio, const float decay)
 +{
 +      /* Lets try to create some vertex groups
 +       * based on the bones of the parent armature.
 +       */
 +      bArmature *arm = ob_arm->data;
 +
 +      /* always create groups */
 +      const int defbase_tot = BLI_listbase_count(&ob->defbase);
 +      int defbase_add;
 +      /* Traverse the bone list, trying to create empty vertex
 +       * groups corresponding to the bone.
 +       */
 +      defbase_add = gpencil_bone_looper(
 +              ob, arm->bonebase.first, NULL,
 +              vgroup_add_unique_bone_cb);
 +
 +      if (defbase_add) {
 +              /* its possible there are DWeight's outside the range of the current
 +               * objects deform groups, in this case the new groups wont be empty */
 +              ED_vgroup_data_clamp_range(ob->data, defbase_tot);
 +      }
 +
 +      if (mode == GP_ARMATURE_AUTO) {
 +              /* Traverse the bone list, trying to fill vertex groups
 +               * with the corresponding vertice weights for which the
 +               * bone is closest.
 +               */
 +              gpencil_add_verts_to_dgroups(
 +                      C, ob, ob_arm,
 +                      ratio, decay);
 +      }
 +}
 +
 +bool ED_gpencil_add_armature_weights(
 +        const bContext *C, ReportList *reports,
 +        Object *ob, Object *ob_arm, int mode)
 +{
 +      Main *bmain = CTX_data_main(C);
 +      Scene *scene = CTX_data_scene(C);
 +
 +      /* if no armature modifier, add a new one */
 +      GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob, eGpencilModifierType_Armature);
 +      if (md == NULL) {
 +              md = ED_object_gpencil_modifier_add(
 +                      reports, bmain, scene,
 +                      ob, "Armature", eGpencilModifierType_Armature);
 +              if (md == NULL) {
 +                      BKE_report(reports, RPT_ERROR,
 +                                 "Unable to add a new Armature modifier to object");
 +                      return false;
 +              }
 +              DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);
 +      }
 +
 +      /* verify armature */
 +      ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md;
 +      if (mmd->object == NULL) {
 +              mmd->object = ob_arm;
 +      }
 +      else {
 +              if (ob_arm != mmd->object) {
 +                      BKE_report(reports, RPT_ERROR,
 +                                 "The existing Armature modifier is already using a different Armature object");
 +                      return false;
 +              }
 +      }
 +
 +      /* add weights */
 +      gpencil_object_vgroup_calc_from_armature(C, ob, ob_arm, mode, DEFAULT_RATIO, DEFAULT_DECAY);
 +
 +      return true;
 +}
 +/* ***************** Generate armature weights ************************** */
 +static bool gpencil_generate_weights_poll(bContext *C)
 +{
 +      Object *ob = CTX_data_active_object(C);
 +
 +      if (ob->type != OB_GPENCIL) {
 +              return false;
 +      }
 +
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      bGPdata *gpd = (bGPdata *)ob->data;
 +
 +      if (BLI_listbase_count(&gpd->layers) == 0) {
 +              return false;
 +      }
 +
 +      /* need some armature in the view layer */
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              if (base->object->type == OB_ARMATURE) {
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +static int gpencil_generate_weights_exec(bContext *C, wmOperator *op)
 +{
 +      Depsgraph *depsgraph = CTX_data_depsgraph(C);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      Object *ob = CTX_data_active_object(C);
 +      Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
 +      bGPdata *gpd = (bGPdata *)ob->data;
 +      Object *ob_arm = NULL;
 +
 +      const int mode = RNA_enum_get(op->ptr, "mode");
 +      const float ratio = RNA_float_get(op->ptr, "ratio");
 +      const float decay = RNA_float_get(op->ptr, "decay");
 +
 +      /* sanity checks */
 +      if (ELEM(NULL, ob, gpd))
 +              return OPERATOR_CANCELLED;
 +
 +      /* get armature */
 +      const int arm_idx = RNA_enum_get(op->ptr, "armature");
 +      if (arm_idx > 0) {
 +              Base *base = BLI_findlink(&view_layer->object_bases, arm_idx - 1);
 +              ob_arm = base->object;
 +      }
 +      else {
 +              /* get armature from modifier */
 +              GpencilModifierData *md = BKE_gpencil_modifiers_findByType(ob_eval, eGpencilModifierType_Armature);
 +              if (md == NULL) {
 +                      BKE_report(op->reports, RPT_ERROR,
 +                              "The grease pencil object need an Armature modifier");
 +                      return OPERATOR_CANCELLED;
 +              }
 +
 +              ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md;
 +              if (mmd->object == NULL) {
 +                      BKE_report(op->reports, RPT_ERROR,
 +                              "Armature modifier is not valid or wrong defined");
 +                      return OPERATOR_CANCELLED;
 +              }
 +
 +              ob_arm = mmd->object;
 +      }
 +
 +      if (ob_arm == NULL) {
 +              BKE_report(op->reports, RPT_ERROR,
 +                         "No Armature object in the view layer");
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      gpencil_object_vgroup_calc_from_armature(C, ob, ob_arm, mode, ratio, decay);
 +
 +      /* notifiers */
 +      DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA);
 +      WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +/* Dynamically populate an enum of Armatures */
 +static const EnumPropertyItem *gpencil_armatures_enum_itemf(
 +        bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
 +{
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      EnumPropertyItem *item = NULL, item_tmp = { 0 };
 +      int totitem = 0;
 +      int i = 0;
 +
 +      if (C == NULL) {
 +              return DummyRNA_DEFAULT_items;
 +      }
 +
 +      /* add default */
 +      item_tmp.identifier = "DEFAULT";
 +      item_tmp.name = "Default";
 +      item_tmp.value = 0;
 +      RNA_enum_item_add(&item, &totitem, &item_tmp);
 +      i++;
 +
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              Object *ob = base->object;
 +              if (ob->type == OB_ARMATURE) {
 +                      item_tmp.identifier = item_tmp.name = ob->id.name + 2;
 +                      item_tmp.value = i;
 +                      RNA_enum_item_add(&item, &totitem, &item_tmp);
 +              }
 +              i++;
 +      }
 +
 +      RNA_enum_item_end(&item, &totitem);
 +      *r_free = true;
 +
 +      return item;
 +}
 +
 +void GPENCIL_OT_generate_weights(wmOperatorType *ot)
 +{
 +      static const EnumPropertyItem mode_type[] = {
 +              {GP_ARMATURE_NAME, "NAME", 0, "Empty Groups", ""},
 +              {GP_ARMATURE_AUTO, "AUTO", 0, "Automatic Weights", ""},
 +              {0, NULL, 0, NULL, NULL}
 +      };
 +
 +      PropertyRNA *prop;
 +
 +      /* identifiers */
 +      ot->name = "Generate Automatic Weights";
 +      ot->idname = "GPENCIL_OT_generate_weights";
 +      ot->description = "Generate automatic weights for armatures (requires armature modifier)";
 +
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +
 +      /* callbacks */
 +      ot->exec = gpencil_generate_weights_exec;
 +      ot->poll = gpencil_generate_weights_poll;
 +
 +      ot->prop = RNA_def_enum(ot->srna, "mode", mode_type, 0, "Mode", "");
 +
 +      prop = RNA_def_enum(ot->srna, "armature", DummyRNA_DEFAULT_items, 0, "Armature", "Armature to use");
 +      RNA_def_enum_funcs(prop, gpencil_armatures_enum_itemf);
 +
 +      RNA_def_float(
 +              ot->srna, "ratio", DEFAULT_RATIO, 0.0f, 2.0f, "Ratio",
 +              "Ratio between bone length and influence radius", 0.001f, 1.0f);
 +
 +      RNA_def_float(
 +              ot->srna, "decay", DEFAULT_DECAY, 0.0f, 1.0f, "Decay",
 +              "Factor to reduce influence depending of distance to bone axis", 0.0f, 1.0f);
 +}
index 4e91712,0000000..0a3d209
mode 100644,000000..100644
--- /dev/null
@@@ -1,229 -1,0 +1,229 @@@
-  * First last matrices from 'b_bone_spline_setup' are close but also not quite accurate
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/editors/space_view3d/view3d_gizmo_armature.c
 + *  \ingroup spview3d
 + */
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_math.h"
 +#include "BLI_utildefines.h"
 +
 +#include "BKE_armature.h"
 +#include "BKE_action.h"
 +#include "BKE_context.h"
 +#include "BKE_object.h"
 +
 +#include "DNA_object_types.h"
 +#include "DNA_armature_types.h"
 +
 +#include "ED_armature.h"
 +#include "ED_screen.h"
 +#include "ED_gizmo_library.h"
 +
 +#include "UI_resources.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "RNA_access.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +
 +#include "view3d_intern.h"  /* own include */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Armature Spline Gizmo
 + *
 + * \{ */
 +
 +/*
 + * TODO(campbell): Current conversion is a approximation (usable not correct),
 + * we'll need to take the next/previous bones into account to get the tangent directions.
++ * First last matrices from 'BKE_pchan_bbone_spline_setup' are close but also not quite accurate
 + * since they're not at either end-points on the curve.
 + *
 + * Likely we'll need a function especially to get the first/last orientations.
 + */
 +
 +#define BBONE_SCALE_Y 3.0f
 +
 +struct BoneSplineHandle {
 +      wmGizmo *gizmo;
 +      bPoseChannel *pchan;
 +      /* We could remove, keep since at the moment for checking the conversion. */
 +      float co[3];
 +      int index;
 +};
 +
 +struct BoneSplineWidgetGroup {
 +      struct BoneSplineHandle handles[2];
 +};
 +
 +static void gizmo_bbone_offset_get(
 +        const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop,
 +        void *value_p)
 +{
 +      struct BoneSplineHandle *bh = gz_prop->custom_func.user_data;
 +      bPoseChannel *pchan = bh->pchan;
 +
 +      float *value = value_p;
 +      BLI_assert(gz_prop->type->array_length == 3);
 +
 +      if (bh->index == 0) {
 +              bh->co[1] = pchan->bone->ease1 / BBONE_SCALE_Y;
 +              bh->co[0] = pchan->curveInX;
 +              bh->co[2] = pchan->curveInY;
 +      }
 +      else {
 +              bh->co[1] = -pchan->bone->ease2 / BBONE_SCALE_Y;
 +              bh->co[0] = pchan->curveOutX;
 +              bh->co[2] = pchan->curveOutY;
 +      }
 +      copy_v3_v3(value, bh->co);
 +}
 +
 +static void gizmo_bbone_offset_set(
 +        const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop,
 +        const void *value_p)
 +{
 +      struct BoneSplineHandle *bh = gz_prop->custom_func.user_data;
 +      bPoseChannel *pchan = bh->pchan;
 +
 +      const float *value = value_p;
 +
 +      BLI_assert(gz_prop->type->array_length == 3);
 +      copy_v3_v3(bh->co, value);
 +
 +      if (bh->index == 0) {
 +              pchan->bone->ease1 = max_ff(0.0f, bh->co[1] * BBONE_SCALE_Y);
 +              pchan->curveInX = bh->co[0];
 +              pchan->curveInY = bh->co[2];
 +      }
 +      else {
 +              pchan->bone->ease2 = max_ff(0.0f, -bh->co[1] * BBONE_SCALE_Y);
 +              pchan->curveOutX = bh->co[0];
 +              pchan->curveOutY = bh->co[2];
 +      }
 +
 +}
 +
 +static bool WIDGETGROUP_armature_spline_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
 +{
 +      Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
 +      if (ob != NULL) {
 +              const bArmature *arm = ob->data;
 +              if (arm->drawtype == ARM_B_BONE) {
 +                      bPoseChannel *pchan = BKE_pose_channel_active(ob);
 +                      if (pchan && pchan->bone->segments > 1) {
 +                              View3D *v3d = CTX_wm_view3d(C);
 +                              if ((v3d->flag2 & V3D_RENDER_OVERRIDE) ||
 +                                  (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)))
 +                              {
 +                                      /* pass */
 +                              }
 +                              else {
 +                                      return true;
 +                              }
 +                      }
 +              }
 +      }
 +      return false;
 +}
 +
 +
 +static void WIDGETGROUP_armature_spline_setup(const bContext *C, wmGizmoGroup *gzgroup)
 +{
 +      Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
 +      bPoseChannel *pchan = BKE_pose_channel_active(ob);
 +
 +      const wmGizmoType *gzt_move = WM_gizmotype_find("GIZMO_GT_move_3d", true);
 +
 +      struct BoneSplineWidgetGroup *bspline_group = MEM_callocN(sizeof(struct BoneSplineWidgetGroup), __func__);
 +      gzgroup->customdata = bspline_group;
 +
 +      /* Handles */
 +      for (int i = 0; i < ARRAY_SIZE(bspline_group->handles); i++) {
 +              wmGizmo *gz;
 +              gz = bspline_group->handles[i].gizmo = WM_gizmo_new_ptr(gzt_move, gzgroup, NULL);
 +              RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_MOVE_STYLE_RING_2D);
 +              RNA_enum_set(gz->ptr, "draw_options",
 +                           ED_GIZMO_MOVE_DRAW_FLAG_FILL | ED_GIZMO_MOVE_DRAW_FLAG_ALIGN_VIEW);
 +              WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_VALUE, true);
 +
 +              UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, gz->color);
 +              UI_GetThemeColor3fv(TH_GIZMO_HI, gz->color_hi);
 +
 +              gz->scale_basis = 0.06f;
 +
 +              if (i == 0) {
 +                      copy_v3_v3(gz->matrix_basis[3], pchan->loc);
 +              }
 +      }
 +}
 +
 +static void WIDGETGROUP_armature_spline_refresh(const bContext *C, wmGizmoGroup *gzgroup)
 +{
 +      Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
 +
 +      if (!gzgroup->customdata)
 +              return;
 +
 +      struct BoneSplineWidgetGroup *bspline_group = gzgroup->customdata;
 +      bPoseChannel *pchan = BKE_pose_channel_active(ob);
 +
 +      /* Handles */
 +      for (int i = 0; i < ARRAY_SIZE(bspline_group->handles); i++) {
 +              wmGizmo *gz = bspline_group->handles[i].gizmo;
 +              bspline_group->handles[i].pchan = pchan;
 +              bspline_group->handles[i].index = i;
 +
 +              float mat[4][4];
 +              mul_m4_m4m4(mat, ob->obmat, (i == 0) ? pchan->disp_mat : pchan->disp_tail_mat);
 +              copy_m4_m4(gz->matrix_space, mat);
 +
 +              /* need to set property here for undo. TODO would prefer to do this in _init */
 +              WM_gizmo_target_property_def_func(
 +                      gz, "offset",
 +                      &(const struct wmGizmoPropertyFnParams) {
 +                          .value_get_fn = gizmo_bbone_offset_get,
 +                          .value_set_fn = gizmo_bbone_offset_set,
 +                          .range_get_fn = NULL,
 +                          .user_data = &bspline_group->handles[i],
 +                      });
 +      }
 +}
 +
 +void VIEW3D_GGT_armature_spline(wmGizmoGroupType *gzgt)
 +{
 +      gzgt->name = "Armature Spline Widgets";
 +      gzgt->idname = "VIEW3D_GGT_armature_spline";
 +
 +      gzgt->flag = (WM_GIZMOGROUPTYPE_PERSISTENT |
 +                   WM_GIZMOGROUPTYPE_3D);
 +
 +      gzgt->poll = WIDGETGROUP_armature_spline_poll;
 +      gzgt->setup = WIDGETGROUP_armature_spline_setup;
 +      gzgt->refresh = WIDGETGROUP_armature_spline_refresh;
 +}
 +
 +/** \} */
@@@ -59,45 -59,6 +59,45 @@@ static float rna_PoseBone_do_envelope(b
        return distfactor_to_bone(vec, chan->pose_head, chan->pose_tail, bone->rad_head * scale,
                                  bone->rad_tail * scale, bone->dist * scale);
  }
-       BKE_pchan_get_bbone_spline_parameters(pchan, rest, &params);
-       BKE_compute_b_bone_handles(&params, ret_h1, ret_roll1, ret_h2, ret_roll2, ease || offsets, offsets);
 +
 +static void rna_PoseBone_bbone_segment_matrix(bPoseChannel *pchan, ReportList *reports, float mat_ret[16], int index, bool rest)
 +{
 +      if (!pchan->bone || pchan->bone->segments <= 1) {
 +              BKE_reportf(reports, RPT_ERROR, "Bone '%s' is not a B-Bone!", pchan->name);
 +              return;
 +      }
 +      if (pchan->runtime.bbone_segments != pchan->bone->segments) {
 +              BKE_reportf(reports, RPT_ERROR, "Bone '%s' has out of date B-Bone segment data!", pchan->name);
 +              return;
 +      }
 +      if (index < 0 || index >= pchan->runtime.bbone_segments) {
 +              BKE_reportf(reports, RPT_ERROR, "Invalid index %d for B-Bone segments of '%s'!", index, pchan->name);
 +              return;
 +      }
 +
 +      if (rest) {
 +              copy_m4_m4((float (*)[4])mat_ret, pchan->runtime.bbone_rest_mats[index].mat);
 +      }
 +      else {
 +              copy_m4_m4((float (*)[4])mat_ret, pchan->runtime.bbone_pose_mats[index].mat);
 +      }
 +}
 +
 +static void rna_PoseBone_compute_bbone_handles(
 +        bPoseChannel *pchan, ReportList *reports,
 +        float ret_h1[3], float *ret_roll1, float ret_h2[3], float *ret_roll2,
 +        bool rest, bool ease, bool offsets)
 +{
 +      if (!pchan->bone || pchan->bone->segments <= 1) {
 +              BKE_reportf(reports, RPT_ERROR, "Bone '%s' is not a B-Bone!", pchan->name);
 +              return;
 +      }
 +
 +      BBoneSplineParameters params;
 +
++      BKE_pchan_bbone_spline_params_get(pchan, rest, &params);
++      BKE_pchan_bbone_handles_compute(&params, ret_h1, ret_roll1, ret_h2, ret_roll2, ease || offsets, offsets);
 +}
  #else
  
  void RNA_api_pose(StructRNA *UNUSED(srna))