Constraints: introduce wrapper functions to access target lists.
Instead of directly accessing constraint-specific callbacks in code all over blender, introduce two wrappers to retrieve and free the target list. This incidentally revealed a place within the Collada exporter in BCAnimationSampler.cpp that didn't clean up after retrieving the targets, resulting in a small memory leak. Fixing this should be the only functional change in this commit. This was split off from D9732. Differential Revision: https://developer.blender.org/D13844
This commit is contained in:
parent
284a3431ae
commit
d040e1da4f
|
@ -307,6 +307,25 @@ void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,
|
|||
void *ownerdata,
|
||||
float mat[4][4],
|
||||
float ctime);
|
||||
|
||||
/**
|
||||
* Retrieves the list of all constraint targets, including the custom space target.
|
||||
* Must be followed by a call to BKE_constraint_targets_flush to free memory.
|
||||
*
|
||||
* \param r_targets Pointer to the list to be initialized with target data.
|
||||
* \returns the number of targets stored in the list.
|
||||
*/
|
||||
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets);
|
||||
|
||||
/**
|
||||
* Copies changed data from the list produced by BKE_constraint_targets_get back to the constraint
|
||||
* data structures and frees memory.
|
||||
*
|
||||
* \param targets List of targets filled by BKE_constraint_targets_get.
|
||||
* \param no_copy Only free memory without copying changes (read-only mode).
|
||||
*/
|
||||
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy);
|
||||
|
||||
/**
|
||||
* Get the list of targets required for solving a constraint.
|
||||
*/
|
||||
|
|
|
@ -980,13 +980,10 @@ void BKE_pose_channels_remove(Object *ob,
|
|||
else {
|
||||
/* Maybe something the bone references is being removed instead? */
|
||||
for (con = pchan->constraints.first; con; con = con->next) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
if (ct->tar == ob) {
|
||||
if (ct->subtarget[0]) {
|
||||
|
@ -998,9 +995,7 @@ void BKE_pose_channels_remove(Object *ob,
|
|||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, 0);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6191,6 +6191,40 @@ bool BKE_constraint_is_nonlocal_in_liboverride(const Object *ob, const bConstrai
|
|||
|
||||
/* -------- Target-Matrix Stuff ------- */
|
||||
|
||||
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *r_targets)
|
||||
{
|
||||
BLI_listbase_clear(r_targets);
|
||||
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
|
||||
if (!cti) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
/* Constraint-specific targets. */
|
||||
if (cti->get_constraint_targets) {
|
||||
count = cti->get_constraint_targets(con, r_targets);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *targets, bool no_copy)
|
||||
{
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
|
||||
if (!cti) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Release the constraint-specific targets. */
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, targets, no_copy);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,
|
||||
Scene *scene,
|
||||
bConstraint *con,
|
||||
|
|
|
@ -2483,21 +2483,16 @@ static void copy_object_pose(Object *obn, const Object *ob, const int flag)
|
|||
* BKE_library_remap stuff, but...
|
||||
* the flush_constraint_targets callback am not sure about, so will delay that for now. */
|
||||
LISTBASE_FOREACH (bConstraint *, con, &chan->constraints) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
|
||||
if (ct->tar == ob) {
|
||||
ct->tar = obn;
|
||||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, false);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5488,11 +5483,9 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
|
|||
|
||||
/* also update constraint targets */
|
||||
LISTBASE_FOREACH (bConstraint *, con, &ob->constraints) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
|
||||
if (ct->tar) {
|
||||
BKE_object_modifier_update_subframe(
|
||||
|
@ -5500,9 +5493,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
|
|||
}
|
||||
}
|
||||
/* free temp targets */
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, false);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1137,6 +1137,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
|
|||
/* Add dependencies for each constraint in turn. */
|
||||
for (bConstraint *con = (bConstraint *)constraints->first; con; con = con->next) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
/* Invalid constraint type. */
|
||||
if (cti == nullptr) {
|
||||
continue;
|
||||
|
@ -1188,9 +1189,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
|
|||
add_relation(cache_key, constraint_op_key, cti->name);
|
||||
}
|
||||
}
|
||||
else if (cti->get_constraint_targets) {
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
else if (BKE_constraint_targets_get(con, &targets)) {
|
||||
LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
|
||||
if (ct->tar == nullptr) {
|
||||
continue;
|
||||
|
@ -1300,9 +1299,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
|
|||
add_relation(target_transform_key, constraint_op_key, cti->name);
|
||||
}
|
||||
}
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, true);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1310,12 +1310,11 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
|
|||
}
|
||||
else {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
|
||||
ListBase targets = {NULL, NULL};
|
||||
|
||||
if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag & (1 << 0))) {
|
||||
ListBase targets = {NULL, NULL};
|
||||
if ((curcon->ui_expand_flag & (1 << 0)) && BKE_constraint_targets_get(curcon, &targets)) {
|
||||
bConstraintTarget *ct;
|
||||
|
||||
cti->get_constraint_targets(curcon, &targets);
|
||||
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
/* calculate target's matrix */
|
||||
|
@ -1328,9 +1327,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
|
|||
OVERLAY_extra_line_dashed(cb, ct->matrix[3], ob->obmat[3], constraint_color);
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(curcon, &targets, 1);
|
||||
}
|
||||
BKE_constraint_targets_flush(curcon, &targets, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -375,13 +375,10 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
|
|||
/* does this constraint have a subtarget in
|
||||
* this armature?
|
||||
*/
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(curcon, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(curcon, &targets)) {
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
if ((ct->tar == ob) && (ct->subtarget[0])) {
|
||||
oldtarget = get_named_editbone(editbones, ct->subtarget);
|
||||
|
@ -409,9 +406,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
|
|||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(curcon, &targets, 0);
|
||||
}
|
||||
BKE_constraint_targets_flush(curcon, &targets, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,13 +109,10 @@ static void constraint_bone_name_fix(Object *ob,
|
|||
bConstraintTarget *ct;
|
||||
|
||||
for (curcon = conlist->first; curcon; curcon = curcon->next) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
|
||||
ListBase targets = {NULL, NULL};
|
||||
|
||||
/* constraint targets */
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(curcon, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(curcon, &targets)) {
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
if (ct->tar == ob) {
|
||||
if (STREQ(ct->subtarget, oldname)) {
|
||||
|
@ -124,9 +121,7 @@ static void constraint_bone_name_fix(Object *ob,
|
|||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(curcon, &targets, 0);
|
||||
}
|
||||
BKE_constraint_targets_flush(curcon, &targets, 0);
|
||||
}
|
||||
|
||||
/* action constraints */
|
||||
|
|
|
@ -68,14 +68,11 @@ static void joined_armature_fix_links_constraints(Main *bmain,
|
|||
bool changed = false;
|
||||
|
||||
for (con = lb->first; con; con = con->next) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
|
||||
/* constraint targets */
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
if (ct->tar == srcArm) {
|
||||
if (ct->subtarget[0] == '\0') {
|
||||
|
@ -90,9 +87,7 @@ static void joined_armature_fix_links_constraints(Main *bmain,
|
|||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, 0);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, 0);
|
||||
}
|
||||
|
||||
/* action constraint? (pose constraints only) */
|
||||
|
@ -459,14 +454,11 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
|
|||
if (ob->type == OB_ARMATURE) {
|
||||
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
|
||||
for (con = pchan->constraints.first; con; con = con->next) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
|
||||
/* constraint targets */
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
/* Any targets which point to original armature
|
||||
* are redirected to the new one only if:
|
||||
|
@ -487,9 +479,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
|
|||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, 0);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -498,14 +488,11 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
|
|||
/* fix object-level constraints */
|
||||
if (ob != origArm) {
|
||||
for (con = ob->constraints.first; con; con = con->next) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
|
||||
/* constraint targets */
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
/* any targets which point to original armature are redirected to the new one only if:
|
||||
* - the target isn't origArm/newArm itself
|
||||
|
@ -525,9 +512,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
|
|||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, 0);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -680,13 +680,10 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op
|
|||
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) {
|
||||
if (pchan->bone->flag & BONE_SELECTED) {
|
||||
for (con = pchan->constraints.first; con; con = con->next) {
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
Object *ob = ct->tar;
|
||||
|
||||
|
@ -702,9 +699,7 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op
|
|||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, 1);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -245,13 +245,11 @@ static void set_constraint_nth_target(bConstraint *con,
|
|||
const char subtarget[],
|
||||
int index)
|
||||
{
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
int num_targets, i;
|
||||
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
num_targets = BLI_listbase_count(&targets);
|
||||
|
||||
if (index < 0) {
|
||||
|
@ -274,9 +272,7 @@ static void set_constraint_nth_target(bConstraint *con,
|
|||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, 0);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,7 +285,6 @@ static void set_constraint_nth_target(bConstraint *con,
|
|||
static void test_constraint(
|
||||
Main *bmain, Object *owner, bPoseChannel *pchan, bConstraint *con, int type)
|
||||
{
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
bool check_targets = true;
|
||||
|
@ -465,14 +460,7 @@ static void test_constraint(
|
|||
}
|
||||
|
||||
/* Check targets for constraints */
|
||||
if (check_targets && cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
/* constraints with empty target list that actually require targets */
|
||||
if (!targets.first && ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) {
|
||||
con->flag |= CONSTRAINT_DISABLE;
|
||||
}
|
||||
|
||||
if (check_targets && BKE_constraint_targets_get(con, &targets)) {
|
||||
/* disable and clear constraints targets that are incorrect */
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
/* general validity checks (for those constraints that need this) */
|
||||
|
@ -543,8 +531,12 @@ static void test_constraint(
|
|||
}
|
||||
|
||||
/* free any temporary targets */
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, 0);
|
||||
BKE_constraint_targets_flush(con, &targets, 0);
|
||||
}
|
||||
else if (check_targets) {
|
||||
/* constraints with empty target list that actually require targets */
|
||||
if (ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) {
|
||||
con->flag |= CONSTRAINT_DISABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,24 +225,26 @@ bool BCAnimationSampler::is_animated_by_constraint(Object *ob,
|
|||
for (con = (bConstraint *)conlist->first; con; con = con->next) {
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
|
||||
if (!bc_validateConstraints(con)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
bConstraintTarget *ct;
|
||||
Object *obtar;
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
bool found = false;
|
||||
|
||||
for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
|
||||
obtar = ct->tar;
|
||||
if (obtar) {
|
||||
if (animated_objects.find(obtar) != animated_objects.end()) {
|
||||
return true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, true);
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -191,24 +191,19 @@ void SceneExporter::writeNode(Object *ob)
|
|||
/* not ideal: add the target object name as another parameter.
|
||||
* No real mapping in the `.dae`.
|
||||
* Need support for multiple target objects also. */
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
|
||||
ListBase targets = {nullptr, nullptr};
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
bConstraintTarget *ct;
|
||||
Object *obtar;
|
||||
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
|
||||
obtar = ct->tar;
|
||||
std::string tar_id((obtar) ? id_name(obtar) : "");
|
||||
colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "target_id", tar_id);
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, true);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, true);
|
||||
}
|
||||
|
||||
con = con->next;
|
||||
|
|
|
@ -598,22 +598,17 @@ static const EnumPropertyItem *rna_Constraint_target_space_itemf(bContext *UNUSE
|
|||
bool *UNUSED(r_free))
|
||||
{
|
||||
bConstraint *con = (bConstraint *)ptr->data;
|
||||
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
|
||||
ListBase targets = {NULL, NULL};
|
||||
bConstraintTarget *ct;
|
||||
|
||||
if (cti && cti->get_constraint_targets) {
|
||||
cti->get_constraint_targets(con, &targets);
|
||||
|
||||
if (BKE_constraint_targets_get(con, &targets)) {
|
||||
for (ct = targets.first; ct; ct = ct->next) {
|
||||
if (ct->tar && ct->tar->type == OB_ARMATURE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cti->flush_constraint_targets) {
|
||||
cti->flush_constraint_targets(con, &targets, 1);
|
||||
}
|
||||
BKE_constraint_targets_flush(con, &targets, 1);
|
||||
|
||||
if (ct) {
|
||||
return target_space_pchan_items;
|
||||
|
|
Loading…
Reference in New Issue