Python API: expose the math mapping vertex positions to B-Bone segments #105419
|
@ -0,0 +1,34 @@
|
|||
"""
|
||||
This example shows how to use B-Bone segment matrices to emulate deformation
|
||||
produced by the Armature modifier or constraint when assigned to the given bone
|
||||
(without Preserve Volume). The coordinates are processed in armature Pose space:
|
||||
"""
|
||||
|
||||
def bbone_deform_matrix(pose_bone, point):
|
||||
index, blend_next = pose_bone.bbone_segment_index(point)
|
||||
|
||||
rest1 = pose_bone.bbone_segment_matrix(index, rest=True)
|
||||
pose1 = pose_bone.bbone_segment_matrix(index, rest=False)
|
||||
deform1 = pose1 @ rest1.inverted()
|
||||
|
||||
# bbone_segment_index ensures that index + 1 is always valid
|
||||
rest2 = pose_bone.bbone_segment_matrix(index + 1, rest=True)
|
||||
pose2 = pose_bone.bbone_segment_matrix(index + 1, rest=False)
|
||||
deform2 = pose2 @ rest2.inverted()
|
||||
|
||||
deform = deform1 * (1 - blend_next) + deform2 * blend_next
|
||||
|
||||
return pose_bone.matrix @ deform @ pose_bone.bone.matrix_local.inverted()
|
||||
|
||||
# Armature modifier deforming vertices:
|
||||
mesh = bpy.data.objects["Mesh"]
|
||||
pose_bone = bpy.data.objects["Armature"].pose.bones["Bone"]
|
||||
|
||||
for vertex in mesh.data.vertices:
|
||||
vertex.co = bbone_deform_matrix(pose_bone, vertex.co) @ vertex.co
|
||||
|
||||
# Armature constraint modifying an object transform:
|
||||
empty = bpy.data.objects["Empty"]
|
||||
matrix = empty.matrix_world
|
||||
|
||||
empty.matrix_world = bbone_deform_matrix(pose_bone, matrix.translation) @ matrix
|
|
@ -539,10 +539,29 @@ void BKE_pchan_bbone_segments_cache_copy(struct bPoseChannel *pchan,
|
|||
|
||||
/**
|
||||
* Calculate index and blend factor for the two B-Bone segment nodes
|
||||
* affecting the point at 0 <= pos <= 1.
|
||||
* affecting the specified point along the bone.
|
||||
*
|
||||
* \param pchan: Pose channel.
|
||||
* \param head_tail: head-tail position along the bone (auto-clamped between 0 and 1).
|
||||
* \param r_index: OUTPUT index of the first segment joint affecting the point.
|
||||
* \param r_blend_next: OUTPUT blend factor between the first and the second segment in [0..1]
|
||||
*/
|
||||
void BKE_pchan_bbone_deform_clamp_segment_index(const struct bPoseChannel *pchan,
|
||||
float head_tail,
|
||||
int *r_index,
|
||||
float *r_blend_next);
|
||||
|
||||
/**
|
||||
* Calculate index and blend factor for the two B-Bone segment nodes
|
||||
* affecting the specified point in object (pose) space.
|
||||
*
|
||||
* \param pchan: Pose channel.
|
||||
* \param co: Pose space coordinates of the point being deformed.
|
||||
* \param r_index: OUTPUT index of the first segment joint affecting the point.
|
||||
* \param r_blend_next: OUTPUT blend factor between the first and the second segment in [0..1]
|
||||
*/
|
||||
void BKE_pchan_bbone_deform_segment_index(const struct bPoseChannel *pchan,
|
||||
float pos,
|
||||
const float *co,
|
||||
int *r_index,
|
||||
float *r_blend_next);
|
||||
|
||||
|
|
|
@ -1565,20 +1565,20 @@ void BKE_pchan_bbone_segments_cache_copy(bPoseChannel *pchan, bPoseChannel *pcha
|
|||
}
|
||||
}
|
||||
|
||||
void BKE_pchan_bbone_deform_segment_index(const bPoseChannel *pchan,
|
||||
float pos,
|
||||
int *r_index,
|
||||
float *r_blend_next)
|
||||
void BKE_pchan_bbone_deform_clamp_segment_index(const bPoseChannel *pchan,
|
||||
float head_tail,
|
||||
int *r_index,
|
||||
float *r_blend_next)
|
||||
{
|
||||
int segments = pchan->bone->segments;
|
||||
|
||||
CLAMP(pos, 0.0f, 1.0f);
|
||||
CLAMP(head_tail, 0.0f, 1.0f);
|
||||
|
||||
/* Calculate the indices of the 2 affecting b_bone segments.
|
||||
* Integer part is the first segment's index.
|
||||
* Integer part plus 1 is the second segment's index.
|
||||
* Fractional part is the blend factor. */
|
||||
float pre_blend = pos * float(segments);
|
||||
float pre_blend = head_tail * float(segments);
|
||||
|
||||
int index = int(floorf(pre_blend));
|
||||
CLAMP(index, 0, segments - 1);
|
||||
|
@ -1590,6 +1590,22 @@ void BKE_pchan_bbone_deform_segment_index(const bPoseChannel *pchan,
|
|||
*r_blend_next = blend;
|
||||
}
|
||||
|
||||
void BKE_pchan_bbone_deform_segment_index(const bPoseChannel *pchan,
|
||||
const float *co,
|
||||
int *r_index,
|
||||
float *r_blend_next)
|
||||
{
|
||||
const Mat4 *mats = pchan->runtime.bbone_deform_mats;
|
||||
const float(*mat)[4] = mats[0].mat;
|
||||
|
||||
/* Transform co to bone space and get its y component. */
|
||||
const float y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1];
|
||||
|
||||
/* Calculate the indices of the 2 affecting b_bone segments. */
|
||||
BKE_pchan_bbone_deform_clamp_segment_index(
|
||||
pchan, y / pchan->bone->length, r_index, r_blend_next);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -106,15 +106,11 @@ static void b_bone_deform(const bPoseChannel *pchan,
|
|||
{
|
||||
const DualQuat *quats = pchan->runtime.bbone_dual_quats;
|
||||
const Mat4 *mats = pchan->runtime.bbone_deform_mats;
|
||||
const float(*mat)[4] = mats[0].mat;
|
||||
float blend, y;
|
||||
float blend;
|
||||
int index;
|
||||
|
||||
/* Transform co to bone space and get its y component. */
|
||||
y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1];
|
||||
|
||||
/* Calculate the indices of the 2 affecting b_bone segments. */
|
||||
BKE_pchan_bbone_deform_segment_index(pchan, y / pchan->bone->length, &index, &blend);
|
||||
BKE_pchan_bbone_deform_segment_index(pchan, co, &index, &blend);
|
||||
|
||||
pchan_deform_accumulate(
|
||||
&quats[index], mats[index + 1].mat, co, weight * (1.0f - blend), vec, dq, defmat);
|
||||
|
|
|
@ -729,7 +729,7 @@ static void constraint_target_to_mat4(Object *ob,
|
|||
int index;
|
||||
|
||||
/* figure out which segment(s) the headtail value falls in */
|
||||
BKE_pchan_bbone_deform_segment_index(pchan, headtail, &index, &fac);
|
||||
BKE_pchan_bbone_deform_clamp_segment_index(pchan, headtail, &index, &fac);
|
||||
|
||||
/* apply full transformation of the segment if requested */
|
||||
if (full_bbone) {
|
||||
|
@ -2631,18 +2631,12 @@ static void armdef_accumulate_bone(bConstraintTarget *ct,
|
|||
if (bone->segments > 1 && bone->segments == pchan->runtime.bbone_segments) {
|
||||
Mat4 *b_bone_mats = pchan->runtime.bbone_deform_mats;
|
||||
Mat4 *b_bone_rest_mats = pchan->runtime.bbone_rest_mats;
|
||||
float(*iamat)[4] = b_bone_mats[0].mat;
|
||||
float basemat[4][4];
|
||||
|
||||
/* The target is a B-Bone:
|
||||
* FIRST: find the segment (see b_bone_deform in `armature.cc`)
|
||||
* Need to transform co back to bone-space, only need y. */
|
||||
float y = iamat[0][1] * co[0] + iamat[1][1] * co[1] + iamat[2][1] * co[2] + iamat[3][1];
|
||||
|
||||
/* Blend the matrix. */
|
||||
int index;
|
||||
float blend;
|
||||
BKE_pchan_bbone_deform_segment_index(pchan, y / bone->length, &index, &blend);
|
||||
BKE_pchan_bbone_deform_segment_index(pchan, co, &index, &blend);
|
||||
|
||||
if (r_sum_dq != nullptr) {
|
||||
/* Compute the object space rest matrix of the segment. */
|
||||
|
|
|
@ -47,6 +47,24 @@ static float rna_PoseBone_do_envelope(bPoseChannel *chan, const float vec[3])
|
|||
bone->dist * scale);
|
||||
}
|
||||
|
||||
static void rna_PoseBone_bbone_segment_index(
|
||||
bPoseChannel *pchan, ReportList *reports, const float pt[3], int *r_index, float *r_blend_next)
|
||||
{
|
||||
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 - depsgraph update required!",
|
||||
pchan->name);
|
||||
return;
|
||||
}
|
||||
|
||||
BKE_pchan_bbone_deform_segment_index(pchan, pt, r_index, r_blend_next);
|
||||
}
|
||||
|
||||
static void rna_PoseBone_bbone_segment_matrix(
|
||||
bPoseChannel *pchan, ReportList *reports, float mat_ret[16], int index, bool rest)
|
||||
{
|
||||
|
@ -55,7 +73,10 @@ static void rna_PoseBone_bbone_segment_matrix(
|
|||
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);
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
"Bone '%s' has out of date B-Bone segment data - depsgraph update required!",
|
||||
pchan->name);
|
||||
return;
|
||||
}
|
||||
if (index < 0 || index > pchan->runtime.bbone_segments) {
|
||||
|
@ -271,6 +292,30 @@ void RNA_api_pose_channel(StructRNA *srna)
|
|||
func, "factor", 0, -FLT_MAX, FLT_MAX, "Factor", "Envelope factor", -FLT_MAX, FLT_MAX);
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
/* B-Bone segment index from point */
|
||||
func = RNA_def_function(srna, "bbone_segment_index", "rna_PoseBone_bbone_segment_index");
|
||||
RNA_def_function_flag(func, FUNC_USE_REPORTS);
|
||||
RNA_def_function_ui_description(
|
||||
func, "Retrieve the index and blend factor of the B-Bone segments based on vertex position");
|
||||
parm = RNA_def_float_vector_xyz(func,
|
||||
"point",
|
||||
3,
|
||||
nullptr,
|
||||
-FLT_MAX,
|
||||
FLT_MAX,
|
||||
"Point",
|
||||
"Vertex position in armature pose space",
|
||||
-FLT_MAX,
|
||||
FLT_MAX);
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
/* outputs */
|
||||
parm = RNA_def_property(func, "index", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(parm, "", "The index of the first segment joint affecting the point");
|
||||
RNA_def_function_output(func, parm);
|
||||
parm = RNA_def_property(func, "blend_next", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_ui_text(parm, "", "The blend factor between the given and the following joint");
|
||||
RNA_def_function_output(func, parm);
|
||||
|
||||
/* B-Bone segment matrices */
|
||||
func = RNA_def_function(srna, "bbone_segment_matrix", "rna_PoseBone_bbone_segment_matrix");
|
||||
RNA_def_function_ui_description(
|
||||
|
|
Loading…
Reference in New Issue