Fix #111553: Double IK constraint not working #113056

Merged
Christoph Lendenfeld merged 5 commits from ChrisLend/blender:fix_double_ik_constraint into blender-v4.0-release 2023-10-10 09:06:37 +02:00
1 changed files with 160 additions and 142 deletions

View File

@ -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 */