Fix #111553: Double IK constraint not working
It seems that the code only ever converted the first IK constraint found into a `PoseTree`. Before #110417 constraints with zero influence were skipped so it worked, except for the bug with the depsgraph. After this patch the logic is the following: From top to bottom of the constraint stack, keep adding constraints until the first constraint with influence is added. This ensures, that if there is only one disabled constraint, it still gets converted into a `PoseTree`, which is needed for the depsgraph to work correctly. The resulting behavior from adding the first active constraint is that the higher in the stack the constraint is, the higher its priority. To achieve that I split off a helper function `find_ik_constraints` that populates a vector of `bConstraint` pointers. The code to create the `PoseTree` is still largely the same, except I moved a few variables closer to where they are used. Pull Request: https://projects.blender.org/blender/blender/pulls/113056
This commit is contained in:
committed by
Christoph Lendenfeld
parent
780bb012ce
commit
0c2afa7c17
@@ -14,6 +14,7 @@
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_constraint.h"
|
||||
@@ -32,24 +33,15 @@
|
||||
|
||||
/* ********************** THE IK SOLVER ******************* */
|
||||
|
||||
/* allocates PoseTree, and links that to root bone/channel */
|
||||
/* NOTE: detecting the IK chain is duplicate code...
|
||||
* in drawarmature.c and in transform_conversions.c */
|
||||
static void initialize_posetree(Object * /*ob*/, bPoseChannel *pchan_tip)
|
||||
static void find_ik_constraints(ListBase *constraints,
|
||||
blender::Vector<bConstraint *> &ik_constraints)
|
||||
{
|
||||
bPoseChannel *curchan, *pchan_root = nullptr, *chanlist[256], **oldchan;
|
||||
PoseTree *tree;
|
||||
PoseTarget *target;
|
||||
bConstraint *con;
|
||||
bKinematicConstraint *data;
|
||||
int a, t, segcount = 0, size, newsize, *oldparent, parent;
|
||||
|
||||
/* find IK constraint, and validate it */
|
||||
for (con = static_cast<bConstraint *>(pchan_tip->constraints.first); con; con = con->next) {
|
||||
LISTBASE_FOREACH (bConstraint *, con, constraints) {
|
||||
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
|
||||
data = (bKinematicConstraint *)con->data;
|
||||
bKinematicConstraint *data = (bKinematicConstraint *)con->data;
|
||||
if (data->flag & CONSTRAINT_IK_AUTO) {
|
||||
break;
|
||||
ik_constraints.append(con);
|
||||
continue;
|
||||
}
|
||||
if (data->tar == nullptr) {
|
||||
continue;
|
||||
@@ -57,149 +49,175 @@ static void initialize_posetree(Object * /*ob*/, bPoseChannel *pchan_tip)
|
||||
if (data->tar->type == OB_ARMATURE && data->subtarget[0] == 0) {
|
||||
continue;
|
||||
}
|
||||
if ((con->flag & CONSTRAINT_DISABLE) == 0) {
|
||||
break;
|
||||
if (con->flag & CONSTRAINT_DISABLE) {
|
||||
continue;
|
||||
}
|
||||
ik_constraints.append(con);
|
||||
}
|
||||
}
|
||||
if (con == nullptr) {
|
||||
}
|
||||
|
||||
/* allocates PoseTree, and links that to root bone/channel */
|
||||
/* NOTE: detecting the IK chain is duplicate code...
|
||||
* in drawarmature.c and in transform_conversions.c */
|
||||
static void initialize_posetree(Object * /*ob*/, bPoseChannel *pchan_tip)
|
||||
{
|
||||
blender::Vector<bConstraint *> ik_constraints;
|
||||
find_ik_constraints(&pchan_tip->constraints, ik_constraints);
|
||||
|
||||
if (ik_constraints.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* exclude tip from chain? */
|
||||
if (!(data->flag & CONSTRAINT_IK_TIP)) {
|
||||
pchan_tip = pchan_tip->parent;
|
||||
}
|
||||
|
||||
/* Find the chain's root & count the segments needed */
|
||||
for (curchan = pchan_tip; curchan; curchan = curchan->parent) {
|
||||
pchan_root = curchan;
|
||||
|
||||
curchan->flag |= POSE_CHAIN; /* don't forget to clear this */
|
||||
chanlist[segcount] = curchan;
|
||||
segcount++;
|
||||
|
||||
if (segcount == data->rootbone || segcount > 255) {
|
||||
break; /* 255 is weak */
|
||||
}
|
||||
}
|
||||
if (!segcount) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup the chain data */
|
||||
|
||||
/* we make tree-IK, unless all existing targets are in this chain */
|
||||
for (tree = static_cast<PoseTree *>(pchan_root->iktree.first); tree; tree = tree->next) {
|
||||
for (target = static_cast<PoseTarget *>(tree->targets.first); target; target = target->next) {
|
||||
curchan = tree->pchan[target->tip];
|
||||
if (curchan->flag & POSE_CHAIN) {
|
||||
curchan->flag &= ~POSE_CHAIN;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* create a target */
|
||||
target = static_cast<PoseTarget *>(MEM_callocN(sizeof(PoseTarget), "posetarget"));
|
||||
target->con = con;
|
||||
pchan_tip->flag &= ~POSE_CHAIN;
|
||||
|
||||
if (tree == nullptr) {
|
||||
/* make new tree */
|
||||
tree = static_cast<PoseTree *>(MEM_callocN(sizeof(PoseTree), "posetree"));
|
||||
|
||||
tree->type = CONSTRAINT_TYPE_KINEMATIC;
|
||||
|
||||
tree->iterations = data->iterations;
|
||||
tree->totchannel = segcount;
|
||||
tree->stretch = (data->flag & CONSTRAINT_IK_STRETCH);
|
||||
|
||||
tree->pchan = static_cast<bPoseChannel **>(
|
||||
MEM_callocN(segcount * sizeof(void *), "ik tree pchan"));
|
||||
tree->parent = static_cast<int *>(MEM_callocN(segcount * sizeof(int), "ik tree parent"));
|
||||
for (a = 0; a < segcount; a++) {
|
||||
tree->pchan[a] = chanlist[segcount - a - 1];
|
||||
tree->parent[a] = a - 1;
|
||||
}
|
||||
target->tip = segcount - 1;
|
||||
|
||||
/* AND! link the tree to the root */
|
||||
BLI_addtail(&pchan_root->iktree, tree);
|
||||
}
|
||||
else {
|
||||
tree->iterations = MAX2(data->iterations, tree->iterations);
|
||||
tree->stretch = tree->stretch && !(data->flag & CONSTRAINT_IK_STRETCH);
|
||||
|
||||
/* Skip common pose channels and add remaining. */
|
||||
size = MIN2(segcount, tree->totchannel);
|
||||
a = t = 0;
|
||||
while (a < size && t < tree->totchannel) {
|
||||
/* locate first matching channel */
|
||||
for (; t < tree->totchannel && tree->pchan[t] != chanlist[segcount - a - 1]; t++) {
|
||||
/* pass */
|
||||
}
|
||||
if (t >= tree->totchannel) {
|
||||
break;
|
||||
}
|
||||
for (; a < size && t < tree->totchannel && tree->pchan[t] == chanlist[segcount - a - 1];
|
||||
a++, t++) {
|
||||
/* pass */
|
||||
}
|
||||
for (bConstraint *constraint : ik_constraints) {
|
||||
bPoseChannel *curchan, *pchan_root = nullptr, *chanlist[256], **oldchan;
|
||||
int segcount = 0;
|
||||
PoseTarget *target;
|
||||
PoseTree *tree;
|
||||
bKinematicConstraint *data = (bKinematicConstraint *)constraint->data;
|
||||
/* exclude tip from chain? */
|
||||
if (!(data->flag & CONSTRAINT_IK_TIP)) {
|
||||
pchan_tip = pchan_tip->parent;
|
||||
}
|
||||
|
||||
segcount = segcount - a;
|
||||
target->tip = tree->totchannel + segcount - 1;
|
||||
/* Find the chain's root & count the segments needed */
|
||||
for (curchan = pchan_tip; curchan; curchan = curchan->parent) {
|
||||
pchan_root = curchan;
|
||||
|
||||
if (segcount > 0) {
|
||||
for (parent = a - 1; parent < tree->totchannel; parent++) {
|
||||
if (tree->pchan[parent] == chanlist[segcount - 1]->parent) {
|
||||
curchan->flag |= POSE_CHAIN; /* don't forget to clear this */
|
||||
chanlist[segcount] = curchan;
|
||||
segcount++;
|
||||
|
||||
if (segcount == data->rootbone || segcount > 255) {
|
||||
break; /* 255 is weak */
|
||||
}
|
||||
}
|
||||
if (!segcount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* setup the chain data */
|
||||
/* we make tree-IK, unless all existing targets are in this chain */
|
||||
for (tree = static_cast<PoseTree *>(pchan_root->iktree.first); tree; tree = tree->next) {
|
||||
for (target = static_cast<PoseTarget *>(tree->targets.first); target; target = target->next)
|
||||
{
|
||||
curchan = tree->pchan[target->tip];
|
||||
if (curchan->flag & POSE_CHAIN) {
|
||||
curchan->flag &= ~POSE_CHAIN;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* shouldn't happen, but could with dependency cycles */
|
||||
if (parent == tree->totchannel) {
|
||||
parent = a - 1;
|
||||
if (target) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* resize array */
|
||||
newsize = tree->totchannel + segcount;
|
||||
oldchan = tree->pchan;
|
||||
oldparent = tree->parent;
|
||||
|
||||
tree->pchan = static_cast<bPoseChannel **>(
|
||||
MEM_callocN(newsize * sizeof(void *), "ik tree pchan"));
|
||||
tree->parent = static_cast<int *>(MEM_callocN(newsize * sizeof(int), "ik tree parent"));
|
||||
memcpy(tree->pchan, oldchan, sizeof(void *) * tree->totchannel);
|
||||
memcpy(tree->parent, oldparent, sizeof(int) * tree->totchannel);
|
||||
MEM_freeN(oldchan);
|
||||
MEM_freeN(oldparent);
|
||||
|
||||
/* add new pose channels at the end, in reverse order */
|
||||
for (a = 0; a < segcount; a++) {
|
||||
tree->pchan[tree->totchannel + a] = chanlist[segcount - a - 1];
|
||||
tree->parent[tree->totchannel + a] = tree->totchannel + a - 1;
|
||||
}
|
||||
tree->parent[tree->totchannel] = parent;
|
||||
|
||||
tree->totchannel = newsize;
|
||||
}
|
||||
|
||||
/* move tree to end of list, for correct evaluation order */
|
||||
BLI_remlink(&pchan_root->iktree, tree);
|
||||
BLI_addtail(&pchan_root->iktree, tree);
|
||||
}
|
||||
/* create a target */
|
||||
target = static_cast<PoseTarget *>(MEM_callocN(sizeof(PoseTarget), "posetarget"));
|
||||
target->con = constraint;
|
||||
pchan_tip->flag &= ~POSE_CHAIN;
|
||||
|
||||
/* add target to the tree */
|
||||
BLI_addtail(&tree->targets, target);
|
||||
/* mark root channel having an IK tree */
|
||||
pchan_root->flag |= POSE_IKTREE;
|
||||
if (tree == nullptr) {
|
||||
/* make new tree */
|
||||
tree = static_cast<PoseTree *>(MEM_callocN(sizeof(PoseTree), "posetree"));
|
||||
|
||||
tree->type = CONSTRAINT_TYPE_KINEMATIC;
|
||||
|
||||
tree->iterations = data->iterations;
|
||||
tree->totchannel = segcount;
|
||||
tree->stretch = (data->flag & CONSTRAINT_IK_STRETCH);
|
||||
|
||||
tree->pchan = static_cast<bPoseChannel **>(
|
||||
MEM_callocN(segcount * sizeof(void *), "ik tree pchan"));
|
||||
tree->parent = static_cast<int *>(MEM_callocN(segcount * sizeof(int), "ik tree parent"));
|
||||
for (int a = 0; a < segcount; a++) {
|
||||
tree->pchan[a] = chanlist[segcount - a - 1];
|
||||
tree->parent[a] = a - 1;
|
||||
}
|
||||
target->tip = segcount - 1;
|
||||
|
||||
/* AND! link the tree to the root */
|
||||
BLI_addtail(&pchan_root->iktree, tree);
|
||||
}
|
||||
else {
|
||||
tree->iterations = MAX2(data->iterations, tree->iterations);
|
||||
tree->stretch = tree->stretch && !(data->flag & CONSTRAINT_IK_STRETCH);
|
||||
|
||||
/* Skip common pose channels and add remaining. */
|
||||
const int size = MIN2(segcount, tree->totchannel);
|
||||
int a, t;
|
||||
a = t = 0;
|
||||
while (a < size && t < tree->totchannel) {
|
||||
/* locate first matching channel */
|
||||
for (; t < tree->totchannel && tree->pchan[t] != chanlist[segcount - a - 1]; t++) {
|
||||
/* pass */
|
||||
}
|
||||
if (t >= tree->totchannel) {
|
||||
break;
|
||||
}
|
||||
for (; a < size && t < tree->totchannel && tree->pchan[t] == chanlist[segcount - a - 1];
|
||||
a++, t++) {
|
||||
/* pass */
|
||||
}
|
||||
}
|
||||
|
||||
segcount = segcount - a;
|
||||
target->tip = tree->totchannel + segcount - 1;
|
||||
|
||||
if (segcount > 0) {
|
||||
int parent;
|
||||
for (parent = a - 1; parent < tree->totchannel; parent++) {
|
||||
if (tree->pchan[parent] == chanlist[segcount - 1]->parent) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* shouldn't happen, but could with dependency cycles */
|
||||
if (parent == tree->totchannel) {
|
||||
parent = a - 1;
|
||||
}
|
||||
|
||||
/* resize array */
|
||||
const int newsize = tree->totchannel + segcount;
|
||||
oldchan = tree->pchan;
|
||||
int *oldparent = tree->parent;
|
||||
|
||||
tree->pchan = static_cast<bPoseChannel **>(
|
||||
MEM_callocN(newsize * sizeof(void *), "ik tree pchan"));
|
||||
tree->parent = static_cast<int *>(MEM_callocN(newsize * sizeof(int), "ik tree parent"));
|
||||
memcpy(tree->pchan, oldchan, sizeof(void *) * tree->totchannel);
|
||||
memcpy(tree->parent, oldparent, sizeof(int) * tree->totchannel);
|
||||
MEM_freeN(oldchan);
|
||||
MEM_freeN(oldparent);
|
||||
|
||||
/* add new pose channels at the end, in reverse order */
|
||||
for (a = 0; a < segcount; a++) {
|
||||
tree->pchan[tree->totchannel + a] = chanlist[segcount - a - 1];
|
||||
tree->parent[tree->totchannel + a] = tree->totchannel + a - 1;
|
||||
}
|
||||
tree->parent[tree->totchannel] = parent;
|
||||
|
||||
tree->totchannel = newsize;
|
||||
}
|
||||
|
||||
/* move tree to end of list, for correct evaluation order */
|
||||
BLI_remlink(&pchan_root->iktree, tree);
|
||||
BLI_addtail(&pchan_root->iktree, tree);
|
||||
}
|
||||
|
||||
/* add target to the tree */
|
||||
BLI_addtail(&tree->targets, target);
|
||||
/* mark root channel having an IK tree */
|
||||
pchan_root->flag |= POSE_IKTREE;
|
||||
|
||||
/* Per bone only one active IK constraint is supported. Inactive constraints still need to be
|
||||
* added for the depsgraph to evaluate properly.*/
|
||||
if (constraint->enforce != 0.0 && !(constraint->flag & CONSTRAINT_OFF)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* transform from bone(b) to bone(b+1), store in chan_mat */
|
||||
|
||||
Reference in New Issue
Block a user