RNA: expose access to B-Bone shape data from Python.

B-Bone shape is a non-trivial computation, so access to
the results would be useful for Python scripts working with
B-Bones, e.g. rig generation.

This exposes both final segment matrices, and the tangent
vectors computed from the custom handle bones.

Since the handle tangents use the axis+roll orientation math
of edit bones, add matrix conversion static methods to Bone.

Reviewers: campbellbarton

Differential Revision: https://developer.blender.org/D3983
This commit is contained in:
Alexander Gavrilov 2018-11-24 15:12:24 +03:00
parent fe65867c3d
commit a58f0eea4f
5 changed files with 206 additions and 55 deletions

View File

@ -165,9 +165,11 @@ typedef struct BBoneSplineParameters {
} 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);

View File

@ -462,20 +462,18 @@ void BKE_pchan_get_bbone_handles(bPoseChannel *pchan, bPoseChannel **r_prev, bPo
}
}
/* 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])
/* 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)
{
bPoseChannel *next, *prev;
Bone *bone = pchan->bone;
BBoneSplineParameters param;
float imat[4][4], posemat[4][4];
float delta[3];
memset(&param, 0, sizeof(param));
memset(param, 0, sizeof(*param));
param.segments = bone->segments;
param.length = bone->length;
param->segments = bone->segments;
param->length = bone->length;
if (!rest) {
float scale[3];
@ -484,8 +482,8 @@ void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array
mat4_to_size(scale, pchan->pose_mat);
if (fabsf(scale[0] - scale[1]) > 1e-6f || fabsf(scale[1] - scale[2]) > 1e-6f) {
param.do_scale = true;
copy_v3_v3(param.scale, scale);
param->do_scale = true;
copy_v3_v3(param->scale, scale);
}
}
@ -497,7 +495,7 @@ void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array
if (rest) {
invert_m4_m4(imat, pchan->bone->arm_mat);
}
else if (param.do_scale) {
else if (param->do_scale) {
copy_m4_m4(posemat, pchan->pose_mat);
normalize_m4(posemat);
invert_m4_m4(imat, posemat);
@ -510,14 +508,14 @@ void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array
float h1[3];
bool done = false;
param.use_prev = true;
param->use_prev = true;
/* Transform previous point inside this bone space. */
if (bone->bbone_prev_type == BBONE_HANDLE_RELATIVE) {
/* Use delta movement (from restpose), and apply this relative to the current bone's head. */
if (rest) {
/* In restpose, arm_head == pose_head */
zero_v3(param.prev_h);
zero_v3(param->prev_h);
done = true;
}
else {
@ -538,19 +536,19 @@ void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array
}
else {
/* Apply special handling for smoothly joining B-Bone chains */
param.prev_bbone = (prev->bone->segments > 1);
param->prev_bbone = (prev->bone->segments > 1);
/* Use bone head as absolute position. */
copy_v3_v3(h1, rest ? prev->bone->arm_head : prev->pose_head);
}
if (!done) {
mul_v3_m4v3(param.prev_h, imat, h1);
mul_v3_m4v3(param->prev_h, imat, h1);
}
if (!param.prev_bbone) {
if (!param->prev_bbone) {
/* Find the previous roll to interpolate. */
mul_m4_m4m4(param.prev_mat, imat, rest ? prev->bone->arm_mat : prev->pose_mat);
mul_m4_m4m4(param->prev_mat, imat, rest ? prev->bone->arm_mat : prev->pose_mat);
}
}
@ -558,14 +556,14 @@ void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array
float h2[3];
bool done = false;
param.use_next = true;
param->use_next = true;
/* Transform next point inside this bone space. */
if (bone->bbone_next_type == BBONE_HANDLE_RELATIVE) {
/* Use delta movement (from restpose), and apply this relative to the current bone's tail. */
if (rest) {
/* In restpose, arm_head == pose_head */
copy_v3_fl3(param.next_h, 0.0f, param.length, 0.0);
copy_v3_fl3(param->next_h, 0.0f, param->length, 0.0);
done = true;
}
else {
@ -586,18 +584,18 @@ void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array
}
else {
/* Apply special handling for smoothly joining B-Bone chains */
param.next_bbone = (next->bone->segments > 1);
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);
}
if (!done) {
mul_v3_m4v3(param.next_h, imat, h2);
mul_v3_m4v3(param->next_h, imat, h2);
}
/* Find the next roll to interpolate as well. */
mul_m4_m4m4(param.next_mat, imat, rest ? next->bone->arm_mat : next->pose_mat);
mul_m4_m4m4(param->next_mat, imat, rest ? next->bone->arm_mat : next->pose_mat);
}
/* Add effects from bbone properties over the top
@ -615,64 +613,65 @@ void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array
* end up animating
*/
{
param.ease1 = bone->ease1 + (!rest ? pchan->ease1 : 0.0f);
param.ease2 = bone->ease2 + (!rest ? pchan->ease2 : 0.0f);
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);
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;
param->roll1 += prev->bone->roll2;
}
if (!rest) {
param.roll1 += prev->roll2;
param->roll1 += prev->roll2;
}
}
}
param.scaleIn = bone->scaleIn * (!rest ? pchan->scaleIn : 1.0f);
param.scaleOut = bone->scaleOut * (!rest ? pchan->scaleOut : 1.0f);
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->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);
param->curveOutX = bone->curveOutX + (!rest ? pchan->curveOutX : 0.0f);
param->curveOutY = bone->curveOutY + (!rest ? pchan->curveOutY : 0.0f);
}
bone->segments = BKE_compute_b_bone_spline(&param, result_array);
}
/* 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])
void b_bone_spline_setup(bPoseChannel *pchan, const bool rest, 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;
int a;
BBoneSplineParameters param;
BKE_pchan_get_bbone_spline_parameters(pchan, rest, &param);
pchan->bone->segments = BKE_compute_b_bone_spline(&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)
{
float mat3[3][3];
float length = param->length;
if (param->do_scale) {
size_to_mat4(scalemat, param->scale);
invert_m4_m4(iscalemat, scalemat);
length *= param->scale[1];
}
*r_roll1 = *r_roll2 = 0.0f;
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;
roll1 = 0.0f;
}
normalize_v3(h1);
@ -681,12 +680,11 @@ int BKE_compute_b_bone_spline(BBoneSplineParameters *param, Mat4 result_array[MA
if (!param->prev_bbone) {
/* Find the previous roll to interpolate. */
copy_m3_m4(mat3, param->prev_mat);
mat3_vec_to_roll(mat3, h1, &roll1);
mat3_vec_to_roll(mat3, h1, r_roll1);
}
}
else {
h1[0] = 0.0f; h1[1] = 1.0; h1[2] = 0.0f;
roll1 = 0.0f;
}
if (param->use_next) {
@ -704,14 +702,13 @@ int BKE_compute_b_bone_spline(BBoneSplineParameters *param, Mat4 result_array[MA
/* Find the next roll to interpolate as well. */
copy_m3_m4(mat3, param->next_mat);
mat3_vec_to_roll(mat3, h2, &roll2);
mat3_vec_to_roll(mat3, h2, r_roll2);
}
else {
h2[0] = 0.0f; h2[1] = 1.0f; h2[2] = 0.0f;
roll2 = 0.0;
}
{
if (ease) {
const float circle_factor = length * (cubic_tangent_factor_circle_v3(h1, h2) / 0.75f);
const float hlength1 = param->ease1 * circle_factor;
@ -736,10 +733,10 @@ int BKE_compute_b_bone_spline(BBoneSplineParameters *param, Mat4 result_array[MA
* - The "pchan" level offsets are the ones that animators actually
* end up animating
*/
{
if (offsets) {
/* Add extra rolls. */
roll1 += param->roll1;
roll2 += param->roll2;
*r_roll1 += param->roll1;
*r_roll2 += param->roll2;
/* Extra curve x / y */
/* NOTE: Scale correction factors here are to compensate for some random floating-point glitches
@ -755,6 +752,27 @@ int BKE_compute_b_bone_spline(BBoneSplineParameters *param, Mat4 result_array[MA
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])
{
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;
if (param->do_scale) {
size_to_mat4(scalemat, param->scale);
invert_m4_m4(iscalemat, scalemat);
length *= param->scale[1];
}
BKE_compute_b_bone_handles(param, h1, &roll1, h2, &roll2, true, true);
/* Make curve. */
CLAMP_MAX(param->segments, MAX_BBONE_SUBDIV);

View File

@ -715,17 +715,24 @@ void BKE_pose_bone_done(struct Depsgraph *depsgraph,
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)
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_cache_bbone_segments(pchan);
bArmature *arm = (bArmature *)ob->data;
if (DEG_is_active(depsgraph) && arm->edbo == NULL) {
BKE_pchan_copy_bbone_segments_cache(pchan->orig_pchan, pchan);
}
}
}

View File

@ -42,6 +42,7 @@
#include <stddef.h>
#include "BLI_math_vector.h"
#include "BKE_armature.h"
static void rna_EditBone_align_roll(EditBone *ebo, float no[3])
@ -56,6 +57,24 @@ static float rna_Bone_do_envelope(Bone *bone, float *vec)
bone->rad_tail * scale, bone->dist * scale);
}
static void rna_Bone_MatrixFromAxisRoll(float *axis, float roll, float *r_matrix)
{
vec_roll_to_mat3(axis, roll, (float (*)[3])r_matrix);
}
static void rna_Bone_AxisRollFromMatrix(float *matrix, float *axis_override, float *r_axis, float *r_roll)
{
float mat[3][3];
normalize_m3_m3(mat, (float (*)[3])matrix);
if (normalize_v3_v3(r_axis, axis_override) != 0.0f) {
mat3_vec_to_roll(mat, r_axis, r_roll);
}
else {
mat3_to_vec_roll(mat, r_axis, r_roll);
}
}
#else
void RNA_api_armature_edit_bone(StructRNA *srna)
@ -83,6 +102,40 @@ void RNA_api_bone(StructRNA *srna)
/* return value */
parm = RNA_def_float(func, "factor", 0, -FLT_MAX, FLT_MAX, "Factor", "Envelope factor", -FLT_MAX, FLT_MAX);
RNA_def_function_return(func, parm);
/* Conversions between Matrix and Axis + Roll representations. */
func = RNA_def_function(srna, "MatrixFromAxisRoll", "rna_Bone_MatrixFromAxisRoll");
RNA_def_function_ui_description(func, "Convert the axis + roll representation to a matrix");
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_property(func, "axis", PROP_FLOAT, PROP_XYZ);
RNA_def_property_array(parm, 3);
RNA_def_property_ui_text(parm, "", "The main axis of the bone (tail - head)");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_property(func, "roll", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(parm, "", "The roll of the bone");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_property(func, "result_matrix", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_3x3);
RNA_def_property_ui_text(parm, "", "The resulting orientation matrix");
RNA_def_function_output(func, parm);
func = RNA_def_function(srna, "AxisRollFromMatrix", "rna_Bone_AxisRollFromMatrix");
RNA_def_function_ui_description(func, "Convert a rotational matrix to the axis + roll representation");
RNA_def_function_flag(func, FUNC_NO_SELF);
parm = RNA_def_property(func, "matrix", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_3x3);
RNA_def_property_ui_text(parm, "", "The orientation matrix of the bone");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_property(func, "axis", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_array(parm, 3);
RNA_def_property_ui_text(parm, "", "The optional override for the axis (finds closest approximation for the matrix)");
parm = RNA_def_property(func, "result_axis", PROP_FLOAT, PROP_XYZ);
RNA_def_property_array(parm, 3);
RNA_def_property_ui_text(parm, "", "The main axis of the bone");
RNA_def_function_output(func, parm);
parm = RNA_def_property(func, "result_roll", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(parm, "", "The roll of the bone");
RNA_def_function_output(func, parm);
}
#endif

View File

@ -59,6 +59,45 @@ static float rna_PoseBone_do_envelope(bPoseChannel *chan, float *vec)
return distfactor_to_bone(vec, chan->pose_head, chan->pose_tail, bone->rad_head * scale,
bone->rad_tail * scale, bone->dist * scale);
}
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_get_bbone_spline_parameters(pchan, rest, &params);
BKE_compute_b_bone_handles(&params, ret_h1, ret_roll1, ret_h2, ret_roll2, ease || offsets, offsets);
}
#else
void RNA_api_pose(StructRNA *UNUSED(srna))
@ -80,6 +119,38 @@ void RNA_api_pose_channel(StructRNA *srna)
/* return value */
parm = RNA_def_float(func, "factor", 0, -FLT_MAX, FLT_MAX, "Factor", "Envelope factor", -FLT_MAX, FLT_MAX);
RNA_def_function_return(func, parm);
/* B-Bone segment matrices */
func = RNA_def_function(srna, "bbone_segment_matrix", "rna_PoseBone_bbone_segment_matrix");
RNA_def_function_ui_description(func, "Retrieve the matrix of the B-Bone segment if available");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_property(func, "matrix_return", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
RNA_def_property_ui_text(parm, "", "The resulting matrix in bone local space");
RNA_def_function_output(func, parm);
parm = RNA_def_int(func, "index", 0, 0, INT_MAX, "", "Index of the segment", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_boolean(func, "rest", false, "", "Return the rest pose matrix");
/* B-Bone custom handle positions */
func = RNA_def_function(srna, "compute_bbone_handles", "rna_PoseBone_compute_bbone_handles");
RNA_def_function_ui_description(func, "Retrieve the vectors and rolls coming from B-Bone custom handles");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_property(func, "handle1", PROP_FLOAT, PROP_XYZ);
RNA_def_property_array(parm, 3);
RNA_def_property_ui_text(parm, "", "The direction vector of the start handle in bone local space");
RNA_def_function_output(func, parm);
parm = RNA_def_float(func, "roll1", 0, -FLT_MAX, FLT_MAX, "", "Roll of the start handle", -FLT_MAX, FLT_MAX);
RNA_def_function_output(func, parm);
parm = RNA_def_property(func, "handle2", PROP_FLOAT, PROP_XYZ);
RNA_def_property_array(parm, 3);
RNA_def_property_ui_text(parm, "", "The direction vector of the end handle in bone local space");
RNA_def_function_output(func, parm);
parm = RNA_def_float(func, "roll2", 0, -FLT_MAX, FLT_MAX, "", "Roll of the end handle", -FLT_MAX, FLT_MAX);
RNA_def_function_output(func, parm);
parm = RNA_def_boolean(func, "rest", false, "", "Return the rest pose state");
parm = RNA_def_boolean(func, "ease", false, "", "Apply scale from ease values");
parm = RNA_def_boolean(func, "offsets", false, "", "Apply roll and curve offsets from bone properties");
}