Armature: apply new DualQuat scale handling to constraints and crazyspace. #111759

Merged
Alexander Gavrilov merged 1 commits from angavrilov/blender:armature-dualquat-pivot into main 2023-09-04 14:49:36 +02:00
4 changed files with 102 additions and 46 deletions

View File

@ -54,10 +54,11 @@ static CLG_LogRef LOG = {"bke.armature_deform"};
static void pchan_deform_accumulate(const DualQuat *deform_dq,
const float deform_mat[4][4],
const float co_in[3],
float weight,
const float weight,
float co_accum[3],
DualQuat *dq_accum,
float mat_accum[3][3])
float mat_accum[3][3],
const bool full_deform)
{
if (weight == 0.0f) {
return;
@ -66,22 +67,7 @@ static void pchan_deform_accumulate(const DualQuat *deform_dq,
if (dq_accum) {
BLI_assert(!co_accum);
nathanvegdahl marked this conversation as resolved Outdated

It's not clear to me why the last parameter here is based on whether mat_accum is null or not, especially since mat_accum doesn't seem to be used at all if we're in this branch. Can you add a comment documenting that?

It's not clear to me why the last parameter here is based on whether `mat_accum` is null or not, especially since `mat_accum` doesn't seem to be used at all if we're in this branch. Can you add a comment documenting that?

From blender.chat:

AlexanderGavrilov
smat is mat_accum - the code for non-preserve volume is using the pointer as a flag, so I did the same, even though it's a bit weird in this case
A more clear alternative may be to introduce an explicit boolean parameter to all the functions
So do I make it an explicit bool parameter instead of the weird implicit use of non-null pointer?

Nathan Vegdahl
I think it makes sense to change it to be a separate parameter, yeah. It made sense as a possibly-null pointer to the matrix when the thing it was controlling was whether or not that matrix got filled in, but now that it's affecting more than that I think a separate bool is a lot clearer.

From blender.chat: > **AlexanderGavrilov** > smat is mat_accum - the code for non-preserve volume is using the pointer as a flag, so I did the same, even though it's a bit weird in this case > A more clear alternative may be to introduce an explicit boolean parameter to all the functions > So do I make it an explicit bool parameter instead of the weird implicit use of non-null pointer? > **Nathan Vegdahl** > I think it makes sense to change it to be a separate parameter, yeah. It made sense as a possibly-null pointer to the matrix when the thing it was controlling was whether or not that matrix got filled in, but now that it's affecting more than that I think a separate bool is a lot clearer.
if (deform_dq->scale_weight) {
/* FIX #32022. */
DualQuat mdq = *deform_dq;
float dst[3];
mul_v3_m4v3(dst, mdq.scale, co_in);
sub_v3_v3(dst, co_in);
mdq.trans[0] -= .5f * (mdq.quat[1] * dst[0] + mdq.quat[2] * dst[1] + mdq.quat[3] * dst[2]);
mdq.trans[1] += .5f * (mdq.quat[0] * dst[0] + mdq.quat[2] * dst[2] - mdq.quat[3] * dst[1]);
mdq.trans[2] += .5f * (mdq.quat[0] * dst[1] + mdq.quat[3] * dst[0] - mdq.quat[1] * dst[2]);
mdq.trans[3] += .5f * (mdq.quat[0] * dst[2] + mdq.quat[1] * dst[1] - mdq.quat[2] * dst[0]);
mdq.scale_weight = 0.0f;
add_weighted_dq_dq(dq_accum, &mdq, weight);
}
else {
add_weighted_dq_dq(dq_accum, deform_dq, weight);
}
add_weighted_dq_dq_pivot(dq_accum, deform_dq, co_in, weight, full_deform);
}
else {
float tmp[3];
@ -90,7 +76,7 @@ static void pchan_deform_accumulate(const DualQuat *deform_dq,
sub_v3_v3(tmp, co_in);
madd_v3_v3fl(co_accum, tmp, weight);
if (mat_accum) {
if (full_deform) {
float tmpmat[3][3];
copy_m3_m4(tmpmat, deform_mat);
@ -101,10 +87,11 @@ static void pchan_deform_accumulate(const DualQuat *deform_dq,
static void b_bone_deform(const bPoseChannel *pchan,
const float co[3],
float weight,
const float weight,
float vec[3],
DualQuat *dq,
float defmat[3][3])
float defmat[3][3],
const bool full_deform)
{
const DualQuat *quats = pchan->runtime.bbone_dual_quats;
const Mat4 *mats = pchan->runtime.bbone_deform_mats;
@ -114,10 +101,16 @@ static void b_bone_deform(const bPoseChannel *pchan,
/* Calculate the indices of the 2 affecting b_bone segments. */
BKE_pchan_bbone_deform_segment_index(pchan, co, &index, &blend);
pchan_deform_accumulate(&quats[index],
mats[index + 1].mat,
nathanvegdahl marked this conversation as resolved Outdated

According to the style guide, float literals are supposed to be written 1.0f as it was before. I don't feel strongly about this myself, but since it was already that way, and that adheres to the style guide, I think it's best to leave it as it was.

According to the style guide, float literals are supposed to be written `1.0f` as it was before. I don't feel strongly about this myself, but since it was already that way, and that adheres to the style guide, I think it's best to leave it as it was.
co,
weight * (1.0f - blend),
vec,
dq,
defmat,
full_deform);
pchan_deform_accumulate(
&quats[index], mats[index + 1].mat, co, weight * (1.0f - blend), vec, dq, defmat);
pchan_deform_accumulate(
&quats[index + 1], mats[index + 2].mat, co, weight * blend, vec, dq, defmat);
&quats[index + 1], mats[index + 2].mat, co, weight * blend, vec, dq, defmat, full_deform);
}
float distfactor_to_bone(
@ -173,8 +166,12 @@ float distfactor_to_bone(
return 1.0f - (a * a) / (rdist * rdist);
}
static float dist_bone_deform(
const bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3])
static float dist_bone_deform(const bPoseChannel *pchan,
float vec[3],
DualQuat *dq,
float mat[3][3],
const float co[3],
const bool full_deform)
{
const Bone *bone = pchan->bone;
float fac, contrib = 0.0;
@ -191,11 +188,11 @@ static float dist_bone_deform(
contrib = fac;
if (contrib > 0.0f) {
if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
b_bone_deform(pchan, co, fac, vec, dq, mat);
b_bone_deform(pchan, co, fac, vec, dq, mat, full_deform);
}
else {
pchan_deform_accumulate(
&pchan->runtime.deform_dual_quat, pchan->chan_mat, co, fac, vec, dq, mat);
&pchan->runtime.deform_dual_quat, pchan->chan_mat, co, fac, vec, dq, mat, full_deform);
}
}
}
@ -204,11 +201,12 @@ static float dist_bone_deform(
}
static void pchan_bone_deform(const bPoseChannel *pchan,
float weight,
const float weight,
float vec[3],
DualQuat *dq,
float mat[3][3],
const float co[3],
const bool full_deform,
float *contrib)
{
const Bone *bone = pchan->bone;
@ -218,11 +216,11 @@ static void pchan_bone_deform(const bPoseChannel *pchan,
}
if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
b_bone_deform(pchan, co, weight, vec, dq, mat);
b_bone_deform(pchan, co, weight, vec, dq, mat, full_deform);
}
else {
pchan_deform_accumulate(
&pchan->runtime.deform_dual_quat, pchan->chan_mat, co, weight, vec, dq, mat);
&pchan->runtime.deform_dual_quat, pchan->chan_mat, co, weight, vec, dq, mat, full_deform);
}
(*contrib) += weight;
@ -286,6 +284,8 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
float armature_weight = 1.0f; /* default to 1 if no overall def group */
float prevco_weight = 0.0f; /* weight for optional cached vertexcos */
const bool full_deform = vert_deform_mats != nullptr;
if (use_quaternion) {
memset(&sumdq, 0, sizeof(DualQuat));
dq = &sumdq;
@ -294,7 +294,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
zero_v3(sumvec);
vec = sumvec;
if (vert_deform_mats) {
if (full_deform) {
zero_m3(summat);
smat = summat;
}
@ -354,7 +354,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
co, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
}
pchan_bone_deform(pchan, weight, vec, dq, smat, co, &contrib);
pchan_bone_deform(pchan, weight, vec, dq, smat, co, full_deform, &contrib);
}
}
/* If there are vertex-groups but not groups with bones (like for soft-body groups). */
@ -363,7 +363,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
pchan = pchan->next)
{
if (!(pchan->bone->flag & BONE_NO_DEFORM)) {
contrib += dist_bone_deform(pchan, vec, dq, smat, co);
contrib += dist_bone_deform(pchan, vec, dq, smat, co, full_deform);
}
}
}
@ -373,7 +373,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
pchan = pchan->next)
{
if (!(pchan->bone->flag & BONE_NO_DEFORM)) {
contrib += dist_bone_deform(pchan, vec, dq, smat, co);
contrib += dist_bone_deform(pchan, vec, dq, smat, co, full_deform);
}
}
}
@ -385,13 +385,13 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
if (armature_weight != 1.0f) {
copy_v3_v3(dco, co);
mul_v3m3_dq(dco, (vert_deform_mats) ? summat : nullptr, dq);
mul_v3m3_dq(dco, full_deform ? summat : nullptr, dq);
sub_v3_v3(dco, co);
mul_v3_fl(dco, armature_weight);
add_v3_v3(co, dco);
}
else {
mul_v3m3_dq(co, (vert_deform_mats) ? summat : nullptr, dq);
mul_v3m3_dq(co, full_deform ? summat : nullptr, dq);
}
smat = summat;
@ -401,7 +401,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
add_v3_v3v3(co, vec, co);
}
if (vert_deform_mats) {
if (full_deform) {
float pre[3][3], post[3][3], tmpmat[3][3];
copy_m3_m4(pre, data->premat);

View File

@ -2578,7 +2578,8 @@ static void armdef_accumulate_matrix(const float obmat[4][4],
const float iobmat[4][4],
const float basemat[4][4],
const float bonemat[4][4],
float weight,
const float pivot[3],
const float weight,
float r_sum_mat[4][4],
DualQuat *r_sum_dq)
{
@ -2600,7 +2601,7 @@ static void armdef_accumulate_matrix(const float obmat[4][4],
orthogonalize_m4_stable(basemat_world, 1, true);
mat4_to_dquat(&tmpdq, basemat_world, mat);
add_weighted_dq_dq(r_sum_dq, &tmpdq, weight);
add_weighted_dq_dq_pivot(r_sum_dq, &tmpdq, pivot, weight, true);
}
else {
madd_m4_m4m4fl(r_sum_mat, r_sum_mat, mat, weight);
@ -2608,16 +2609,16 @@ static void armdef_accumulate_matrix(const float obmat[4][4],
}
/* Compute and accumulate transformation for a single target bone. */
static void armdef_accumulate_bone(bConstraintTarget *ct,
bPoseChannel *pchan,
static void armdef_accumulate_bone(const bConstraintTarget *ct,
const bPoseChannel *pchan,
const float wco[3],
bool force_envelope,
const bool force_envelope,
float *r_totweight,
float r_sum_mat[4][4],
DualQuat *r_sum_dq)
{
float iobmat[4][4], co[3];
Bone *bone = pchan->bone;
const Bone *bone = pchan->bone;
float weight = ct->weight;
/* Our object's location in target pose space. */
@ -2632,8 +2633,8 @@ static void armdef_accumulate_bone(bConstraintTarget *ct,
/* Find the correct bone transform matrix in world space. */
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;
const Mat4 *b_bone_mats = pchan->runtime.bbone_deform_mats;
const Mat4 *b_bone_rest_mats = pchan->runtime.bbone_rest_mats;
float basemat[4][4];
/* Blend the matrix. */
@ -2650,6 +2651,7 @@ static void armdef_accumulate_bone(bConstraintTarget *ct,
iobmat,
basemat,
b_bone_mats[index + 1].mat,
wco,
weight * (1.0f - blend),
r_sum_mat,
r_sum_dq);
@ -2663,6 +2665,7 @@ static void armdef_accumulate_bone(bConstraintTarget *ct,
iobmat,
basemat,
b_bone_mats[index + 2].mat,
wco,
weight * blend,
r_sum_mat,
r_sum_dq);
@ -2673,6 +2676,7 @@ static void armdef_accumulate_bone(bConstraintTarget *ct,
iobmat,
bone->arm_mat,
pchan->chan_mat,
wco,
weight,
r_sum_mat,
r_sum_dq);

View File

@ -411,6 +411,11 @@ void rotate_eulO(float beul[3], short order, char axis, float angle);
void copy_dq_dq(DualQuat *r, const DualQuat *dq);
void normalize_dq(DualQuat *dq, float totweight);
void add_weighted_dq_dq(DualQuat *dq_sum, const DualQuat *dq, float weight);
void add_weighted_dq_dq_pivot(DualQuat *dq_sum,
const DualQuat *dq,
const float pivot[3],
float weight,
bool compute_scale_matrix);
void mul_v3m3_dq(float r[3], float R[3][3], DualQuat *dq);
void mat4_to_dquat(DualQuat *dq, const float basemat[4][4], const float mat[4][4]);

View File

@ -2088,6 +2088,53 @@ void add_weighted_dq_dq(DualQuat *dq_sum, const DualQuat *dq, float weight)
}
}
/**
* Add the transformation defined by the given dual quaternion to the accumulator,
* using the specified pivot point for combining scale transformations.
*
* If the resulting dual quaternion would only be used to transform the pivot point itself,
* this function can avoid fully computing the combined scale matrix to get a performance
* boost without affecting the result.
*/
void add_weighted_dq_dq_pivot(DualQuat *dq_sum,
const DualQuat *dq,
const float pivot[3],
const float weight,
const bool compute_scale_matrix)
{
/* FIX #32022, #43188, #100373 - bad deformation when combining scaling and rotation. */
if (dq->scale_weight) {
DualQuat mdq = *dq;
/* Compute the translation induced by scale at the pivot point. */
float dst[3];
mul_v3_m4v3(dst, mdq.scale, pivot);
sub_v3_v3(dst, pivot);
/* Apply the scale translation to the translation part of the DualQuat. */
mdq.trans[0] -= .5f * (mdq.quat[1] * dst[0] + mdq.quat[2] * dst[1] + mdq.quat[3] * dst[2]);
mdq.trans[1] += .5f * (mdq.quat[0] * dst[0] + mdq.quat[2] * dst[2] - mdq.quat[3] * dst[1]);
mdq.trans[2] += .5f * (mdq.quat[0] * dst[1] + mdq.quat[3] * dst[0] - mdq.quat[1] * dst[2]);
mdq.trans[3] += .5f * (mdq.quat[0] * dst[2] + mdq.quat[1] * dst[1] - mdq.quat[2] * dst[0]);
/* Neutralize the scale matrix at the pivot point. */
if (compute_scale_matrix) {
/* This translates the matrix to transform the pivot point to itself. */
sub_v3_v3(mdq.scale[3], dst);
}
else {
/* This completely discards the scale matrix - if the resulting DualQuat
* is converted to a matrix, it would have no scale or shear. */
mdq.scale_weight = 0.0f;
}
add_weighted_dq_dq(dq_sum, &mdq, weight);
}
else {
add_weighted_dq_dq(dq_sum, dq, weight);
}
}
void normalize_dq(DualQuat *dq, float totweight)
{
const float scale = 1.0f / totweight;