diff --git a/source/blender/ikplugin/intern/iksolver_plugin.cc b/source/blender/ikplugin/intern/iksolver_plugin.cc index c2e82aa81cc..8ff63dfcf9a 100644 --- a/source/blender/ikplugin/intern/iksolver_plugin.cc +++ b/source/blender/ikplugin/intern/iksolver_plugin.cc @@ -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 &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(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 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(pchan_root->iktree.first); tree; tree = tree->next) { - for (target = static_cast(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(MEM_callocN(sizeof(PoseTarget), "posetarget")); - target->con = con; - pchan_tip->flag &= ~POSE_CHAIN; - - if (tree == nullptr) { - /* make new tree */ - tree = static_cast(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( - MEM_callocN(segcount * sizeof(void *), "ik tree pchan")); - tree->parent = static_cast(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(pchan_root->iktree.first); tree; tree = tree->next) { + for (target = static_cast(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( - MEM_callocN(newsize * sizeof(void *), "ik tree pchan")); - tree->parent = static_cast(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(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(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( + MEM_callocN(segcount * sizeof(void *), "ik tree pchan")); + tree->parent = static_cast(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( + MEM_callocN(newsize * sizeof(void *), "ik tree pchan")); + tree->parent = static_cast(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 */