This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/object/object_constraint.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2491 lines
72 KiB
C
Raw Normal View History

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup edobj
2011-02-27 20:29:51 +00:00
*/
#include <stdio.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_dynstr.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_text_types.h"
#include "BIK_api.h"
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_report.h"
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
#include "BKE_tracking.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
#endif
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "ED_keyframing.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "object_intern.h"
/* ------------------------------------------------------------------- */
/** \name Constraint Data Accessors
* \{ */
2020-09-11 14:45:42 +10:00
/**
* If object is in pose-mode, return active bone constraints, else object constraints.
* No constraints are returned for a bone on an inactive bone-layer.
*/
ListBase *ED_object_constraint_active_list(Object *ob)
{
2019-04-22 09:19:45 +10:00
if (ob == NULL) {
return NULL;
2019-04-22 09:19:45 +10:00
}
if (ob->mode & OB_MODE_POSE) {
bPoseChannel *pchan;
pchan = BKE_pose_channel_active(ob);
2019-04-22 09:19:45 +10:00
if (pchan) {
return &pchan->constraints;
2019-04-22 09:19:45 +10:00
}
}
2019-04-22 09:19:45 +10:00
else {
return &ob->constraints;
2019-04-22 09:19:45 +10:00
}
return NULL;
}
2020-09-11 14:45:42 +10:00
/**
* Get the constraints for the active pose bone. Bone may be on an inactive bone-layer
* (unlike #ED_object_constraint_active_list, such constraints are not excluded here).
*/
ListBase *ED_object_pose_constraint_list(const bContext *C)
{
bPoseChannel *pose_bone = CTX_data_pointer_get(C, "pose_bone").data;
if (pose_bone == NULL) {
pose_bone = CTX_data_pointer_get(C, "active_pose_bone").data;
if (pose_bone == NULL) {
return NULL;
}
}
return &pose_bone->constraints;
}
/* Find the list that a given constraint belongs to,
* and/or also get the posechannel this is from (if applicable) */
ListBase *ED_object_constraint_list_from_constraint(Object *ob,
bConstraint *con,
bPoseChannel **r_pchan)
{
2019-04-22 09:19:45 +10:00
if (r_pchan) {
*r_pchan = NULL;
2019-04-22 09:19:45 +10:00
}
2019-04-22 09:19:45 +10:00
if (ELEM(NULL, ob, con)) {
return NULL;
2019-04-22 09:19:45 +10:00
}
/* try object constraints first */
if ((BLI_findindex(&ob->constraints, con) != -1)) {
return &ob->constraints;
}
/* if armature, try pose bones too */
if (ob->pose) {
bPoseChannel *pchan;
/* try each bone in order
* NOTE: it's not possible to directly look up the active bone yet, so this will have to do
*/
2012-04-28 15:42:27 +00:00
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
if ((BLI_findindex(&pchan->constraints, con) != -1)) {
2019-04-22 09:19:45 +10:00
if (r_pchan) {
*r_pchan = pchan;
2019-04-22 09:19:45 +10:00
}
return &pchan->constraints;
}
}
}
/* done */
return NULL;
}
/* single constraint */
bConstraint *ED_object_constraint_active_get(Object *ob)
{
return BKE_constraints_active_get(ED_object_constraint_active_list(ob));
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name PyConstraints (Unused)
* \{ */
#ifdef WITH_PYTHON
/* this callback sets the text-file to be used for selected menu item */
2018-06-06 15:50:24 +02:00
static void validate_pyconstraint_cb(Main *bmain, void *arg1, void *arg2)
{
bPythonConstraint *data = arg1;
2012-04-28 15:42:27 +00:00
Text *text = NULL;
int index = *((int *)arg2);
int i;
/* exception for no script */
if (index) {
/* innovative use of a for...loop to search */
2019-04-22 09:19:45 +10:00
for (text = bmain->texts.first, i = 1; text && index != i; i++, text = text->id.next) {
/* pass */
2019-04-22 09:19:45 +10:00
}
}
data->text = text;
}
/* this returns a string for the list of usable pyconstraint script names */
2018-06-06 15:50:24 +02:00
static char *buildmenu_pyconstraints(Main *bmain, Text *con_text, int *pyconindex)
{
2012-04-28 15:42:27 +00:00
DynStr *pupds = BLI_dynstr_new();
Text *text;
char *str;
char buf[64];
int i;
/* add title first */
sprintf(buf, "Scripts: %%t|[None]%%x0|");
BLI_dynstr_append(pupds, buf);
/* init active-index first */
2019-04-22 09:19:45 +10:00
if (con_text == NULL) {
2012-04-28 15:42:27 +00:00
*pyconindex = 0;
2019-04-22 09:19:45 +10:00
}
/* loop through markers, adding them */
for (text = bmain->texts.first, i = 1; text; i++, text = text->id.next) {
/* this is important to ensure that right script is shown as active */
2019-04-22 09:19:45 +10:00
if (text == con_text) {
*pyconindex = i;
2019-04-22 09:19:45 +10:00
}
/* only include valid pyconstraint scripts */
if (BPY_is_pyconstraint(text)) {
2012-04-28 15:42:27 +00:00
BLI_dynstr_append(pupds, text->id.name + 2);
sprintf(buf, "%%x%d", i);
BLI_dynstr_append(pupds, buf);
2019-04-22 09:19:45 +10:00
if (text->id.next) {
BLI_dynstr_append(pupds, "|");
2019-04-22 09:19:45 +10:00
}
}
}
/* convert to normal MEM_malloc'd string */
2012-04-28 15:42:27 +00:00
str = BLI_dynstr_get_cstring(pupds);
BLI_dynstr_free(pupds);
return str;
}
#endif /* WITH_PYTHON */
#if 0 /* UNUSED, until pyconstraints are added back. */
/* this callback gets called when the 'refresh' button of a pyconstraint gets pressed */
2012-04-28 15:42:27 +00:00
static void update_pyconstraint_cb(void *arg1, void *arg2)
{
# ifndef WITH_PYTHON
2010-10-23 16:03:31 +00:00
(void)arg1; /* unused */
(void)arg2; /* unused */
# else
2012-04-28 15:42:27 +00:00
Object *owner = (Object *)arg1;
bConstraint *con = (bConstraint *)arg2;
if (owner && con) {
BPY_pyconstraint_update(owner, con);
}
# endif
}
#endif /* UNUSED */
/** \} */
/* ------------------------------------------------------------------- */
/** \name Add Constraint Utilities
* \{ */
/* helper function for add_constriant - sets the last target for the active constraint */
2012-04-28 15:42:27 +00:00
static void set_constraint_nth_target(bConstraint *con,
Object *target,
const char subtarget[],
int index)
{
2015-03-30 21:17:07 +11:00
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);
num_targets = BLI_listbase_count(&targets);
if (index < 0) {
2019-04-22 09:19:45 +10:00
if (abs(index) < num_targets) {
2012-04-28 15:42:27 +00:00
index = num_targets - abs(index);
2019-04-22 09:19:45 +10:00
}
else {
2012-04-28 15:42:27 +00:00
index = num_targets - 1;
2019-04-22 09:19:45 +10:00
}
}
else if (index >= num_targets) {
2012-04-28 15:42:27 +00:00
index = num_targets - 1;
}
2012-04-28 15:42:27 +00:00
for (ct = targets.first, i = 0; ct; ct = ct->next, i++) {
if (i == index) {
2012-04-28 15:42:27 +00:00
ct->tar = target;
BLI_strncpy(ct->subtarget, subtarget, sizeof(ct->subtarget));
break;
}
}
2019-04-22 09:19:45 +10:00
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
2019-04-22 09:19:45 +10:00
}
}
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Constraint Sanity Testing
* \{ */
static void test_constraint(
Main *bmain, Object *owner, bPoseChannel *pchan, bConstraint *con, int type)
{
2015-03-30 21:17:07 +11:00
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
bool check_targets = true;
/* clear disabled-flag first */
con->flag &= ~CONSTRAINT_DISABLE;
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = con->data;
/* bad: we need a separate set of checks here as poletarget is
2018-11-14 12:53:15 +11:00
* optional... otherwise poletarget must exist too or else
* the constraint is deemed invalid
*/
/* default IK check ... */
if (BKE_object_exists_check(bmain, data->tar) == 0) {
data->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (data->tar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
con->flag |= CONSTRAINT_DISABLE;
}
}
if (data->poletar) {
if (BKE_object_exists_check(bmain, data->poletar) == 0) {
data->poletar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (data->poletar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) {
con->flag |= CONSTRAINT_DISABLE;
}
}
}
/* ... can be overwritten here */
BIK_test_constraint(owner, con);
/* targets have already been checked for this */
check_targets = false;
}
else if (con->type == CONSTRAINT_TYPE_PIVOT) {
bPivotConstraint *data = con->data;
/* target doesn't have to exist, but if it is non-null, it must exist! */
if (data->tar && BKE_object_exists_check(bmain, data->tar) == 0) {
data->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (data->tar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
con->flag |= CONSTRAINT_DISABLE;
}
}
/* targets have already been checked for this */
check_targets = false;
}
else if (con->type == CONSTRAINT_TYPE_ACTION) {
bActionConstraint *data = con->data;
/* validate action */
if (data->act == NULL) {
/* must have action */
con->flag |= CONSTRAINT_DISABLE;
}
else if (data->act->idroot != ID_OB) {
/* only object-rooted actions can be used */
data->act = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
/* Skip target checking if we're not using it */
if (data->flag & ACTCON_USE_EVAL_TIME) {
check_targets = false;
}
}
else if (con->type == CONSTRAINT_TYPE_FOLLOWPATH) {
bFollowPathConstraint *data = con->data;
/* don't allow track/up axes to be the same */
2019-04-22 09:19:45 +10:00
if (data->upflag == data->trackflag) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
if (data->upflag + 3 == data->trackflag) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
}
else if (con->type == CONSTRAINT_TYPE_TRACKTO) {
bTrackToConstraint *data = con->data;
/* don't allow track/up axes to be the same */
2019-04-22 09:19:45 +10:00
if (data->reserved2 == data->reserved1) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
if (data->reserved2 + 3 == data->reserved1) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
}
else if (con->type == CONSTRAINT_TYPE_LOCKTRACK) {
bLockTrackConstraint *data = con->data;
2019-04-22 09:19:45 +10:00
if (data->lockflag == data->trackflag) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
if (data->lockflag + 3 == data->trackflag) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
}
else if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
bSplineIKConstraint *data = con->data;
/* if the number of points does not match the amount required by the chain length,
* free the points array and request a rebind...
*/
if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) {
/* free the points array */
if (data->points) {
MEM_freeN(data->points);
data->points = NULL;
}
/* clear the bound flag, forcing a rebind next time this is evaluated */
data->flag &= ~CONSTRAINT_SPLINEIK_BOUND;
}
}
else if (con->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
bFollowTrackConstraint *data = con->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) {
if (data->clip != NULL && data->track[0]) {
MovieTracking *tracking = &data->clip->tracking;
MovieTrackingObject *tracking_object;
2019-04-22 09:19:45 +10:00
if (data->object[0]) {
tracking_object = BKE_tracking_object_get_named(tracking, data->object);
2019-04-22 09:19:45 +10:00
}
else {
tracking_object = BKE_tracking_object_get_camera(tracking);
2019-04-22 09:19:45 +10:00
}
if (!tracking_object) {
con->flag |= CONSTRAINT_DISABLE;
}
else {
2019-04-22 09:19:45 +10:00
if (!BKE_tracking_track_get_named(tracking, tracking_object, data->track)) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
}
}
else {
con->flag |= CONSTRAINT_DISABLE;
}
}
}
else if (con->type == CONSTRAINT_TYPE_CAMERASOLVER) {
bCameraSolverConstraint *data = con->data;
2019-04-22 09:19:45 +10:00
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
}
else if (con->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
bObjectSolverConstraint *data = con->data;
2019-04-22 09:19:45 +10:00
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) {
con->flag |= CONSTRAINT_DISABLE;
2019-04-22 09:19:45 +10:00
}
}
else if (con->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) {
bTransformCacheConstraint *data = con->data;
if ((data->cache_file == NULL) || (data->object_path[0] == '\0')) {
con->flag |= CONSTRAINT_DISABLE;
}
}
/* 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;
}
/* 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) */
if (BKE_object_exists_check(bmain, ct->tar) == 0) {
/* object doesn't exist, but constraint requires target */
ct->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (ct->tar == owner) {
if (type == CONSTRAINT_OBTYPE_BONE) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) {
/* bone must exist in armature... */
/* TODO: clear subtarget? */
con->flag |= CONSTRAINT_DISABLE;
}
else if (STREQ(pchan->name, ct->subtarget)) {
/* cannot target self */
ct->subtarget[0] = '\0';
con->flag |= CONSTRAINT_DISABLE;
}
}
else {
/* cannot use self as target */
ct->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
}
/* target checks for specific constraints */
if (ELEM(con->type,
CONSTRAINT_TYPE_FOLLOWPATH,
CONSTRAINT_TYPE_CLAMPTO,
CONSTRAINT_TYPE_SPLINEIK)) {
if (ct->tar) {
/* The object type check is only needed here in case we have a placeholder
* object assigned (because the library containing the curve is missing).
*
2019-08-31 01:19:22 +10:00
* In other cases it should be impossible to have a type mismatch.
*/
if (ct->tar->type != OB_CURVE) {
con->flag |= CONSTRAINT_DISABLE;
}
else {
Curve *cu = ct->tar->data;
/* auto-set 'Path' setting on curve so this works */
cu->flag |= CU_PATH;
}
}
}
else if (con->type == CONSTRAINT_TYPE_ARMATURE) {
if (ct->tar) {
/* The object type check is only needed here in case we have a placeholder
* object assigned (because the library containing the armature is missing).
*
2019-08-31 01:19:22 +10:00
* In other cases it should be impossible to have a type mismatch.
*/
if (ct->tar->type != OB_ARMATURE) {
con->flag |= CONSTRAINT_DISABLE;
}
else if (!BKE_armature_find_bone_name(BKE_armature_from_object(ct->tar),
ct->subtarget)) {
/* bone must exist in armature... */
con->flag |= CONSTRAINT_DISABLE;
}
}
}
}
/* free any temporary targets */
2019-04-22 09:19:45 +10:00
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
2019-04-22 09:19:45 +10:00
}
}
}
static int constraint_type_get(Object *owner, bPoseChannel *pchan)
{
int type;
/* Check parents */
if (pchan) {
switch (owner->type) {
case OB_ARMATURE:
type = CONSTRAINT_OBTYPE_BONE;
break;
default:
type = CONSTRAINT_OBTYPE_OBJECT;
break;
}
}
2019-04-22 09:19:45 +10:00
else {
type = CONSTRAINT_OBTYPE_OBJECT;
2019-04-22 09:19:45 +10:00
}
return type;
}
/* checks validity of object pointers, and NULLs,
* if Bone doesn't exist it sets the CONSTRAINT_DISABLE flag.
*/
static void test_constraints(Main *bmain, Object *ob, bPoseChannel *pchan)
{
bConstraint *curcon;
ListBase *conlist = NULL;
int type;
if (ob == NULL) {
return;
2019-04-22 09:19:45 +10:00
}
type = constraint_type_get(ob, pchan);
/* Get the constraint list for this object */
switch (type) {
case CONSTRAINT_OBTYPE_OBJECT:
conlist = &ob->constraints;
break;
case CONSTRAINT_OBTYPE_BONE:
conlist = &pchan->constraints;
break;
}
/* Check all constraints - is constraint valid? */
if (conlist) {
2012-04-28 15:42:27 +00:00
for (curcon = conlist->first; curcon; curcon = curcon->next) {
test_constraint(bmain, ob, pchan, curcon, type);
}
}
}
void object_test_constraints(Main *bmain, Object *ob)
{
if (ob->constraints.first) {
test_constraints(bmain, ob, NULL);
2019-04-22 09:19:45 +10:00
}
if (ob->type == OB_ARMATURE && ob->pose) {
bPoseChannel *pchan;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
2019-04-22 09:19:45 +10:00
if (pchan->constraints.first) {
test_constraints(bmain, ob, pchan);
2019-04-22 09:19:45 +10:00
}
}
}
}
static void object_test_constraint(Main *bmain, Object *ob, bConstraint *con)
{
if (ob->type == OB_ARMATURE && ob->pose) {
if (BLI_findindex(&ob->constraints, con) != -1) {
test_constraint(bmain, ob, NULL, con, CONSTRAINT_OBTYPE_OBJECT);
}
else {
bPoseChannel *pchan;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
if (BLI_findindex(&pchan->constraints, con) != -1) {
test_constraint(bmain, ob, pchan, con, CONSTRAINT_OBTYPE_BONE);
break;
}
}
}
}
else {
test_constraint(bmain, ob, NULL, con, CONSTRAINT_OBTYPE_OBJECT);
}
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Generic Functions for Operators Using Constraint Names and Data Context
* \{ */
2012-04-28 15:42:27 +00:00
#define EDIT_CONSTRAINT_OWNER_OBJECT 0
#define EDIT_CONSTRAINT_OWNER_BONE 1
static const EnumPropertyItem constraint_owner_items[] = {
{EDIT_CONSTRAINT_OWNER_OBJECT,
"OBJECT",
0,
"Object",
"Edit a constraint on the active object"},
{EDIT_CONSTRAINT_OWNER_BONE, "BONE", 0, "Bone", "Edit a constraint on the active bone"},
{0, NULL, 0, NULL, NULL},
};
static bool edit_constraint_poll_generic(bContext *C,
StructRNA *rna_type,
const bool is_liboverride_allowed)
{
2012-04-28 15:42:27 +00:00
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", rna_type);
Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
bConstraint *con = ptr.data;
if (!ob) {
CTX_wm_operator_poll_msg_set(C, "Context missing active object");
2020-07-06 16:07:54 -04:00
return false;
}
if (ID_IS_LINKED(ob) || (ptr.owner_id && ID_IS_LINKED(ptr.owner_id))) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit library data");
2020-07-06 16:07:54 -04:00
return false;
}
if (ID_IS_OVERRIDE_LIBRARY(ob) && !is_liboverride_allowed) {
if ((con == NULL) || (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) == 0) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit constraints coming from library override");
return false;
}
}
2020-07-06 16:07:54 -04:00
return true;
}
2018-07-02 11:47:00 +02:00
static bool edit_constraint_poll(bContext *C)
{
return edit_constraint_poll_generic(C, &RNA_Constraint, false);
}
/* Used by operators performing actions allowed also on constraints from the overridden linked
* object (not only from added 'local' ones). */
static bool edit_constraint_liboverride_allowed_poll(bContext *C)
{
return edit_constraint_poll_generic(C, &RNA_Constraint, true);
}
static void edit_constraint_properties(wmOperatorType *ot)
{
PropertyRNA *prop;
prop = RNA_def_string(
ot->srna, "constraint", NULL, MAX_NAME, "Constraint", "Name of the constraint to edit");
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_enum(
ot->srna, "owner", constraint_owner_items, 0, "Owner", "The owner of this constraint");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
static void edit_constraint_report_property(wmOperatorType *ot)
{
PropertyRNA *prop = RNA_def_boolean(
ot->srna, "report", false, "Report", "Create a notification after the operation");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
static bool edit_constraint_invoke_properties(bContext *C,
wmOperator *op,
const wmEvent *event,
int *r_retval)
{
2012-04-28 15:42:27 +00:00
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
bConstraint *con;
ListBase *list;
if (RNA_struct_property_is_set(op->ptr, "constraint") &&
2019-04-22 09:19:45 +10:00
RNA_struct_property_is_set(op->ptr, "owner")) {
2020-07-06 16:07:54 -04:00
return true;
2019-04-22 09:19:45 +10:00
}
if (ptr.data) {
con = ptr.data;
RNA_string_set(op->ptr, "constraint", con->name);
list = ED_object_constraint_list_from_constraint(ob, con, NULL);
2019-04-22 09:19:45 +10:00
if (&ob->constraints == list) {
RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_OBJECT);
2019-04-22 09:19:45 +10:00
}
else {
RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_BONE);
2019-04-22 09:19:45 +10:00
}
2020-07-06 16:07:54 -04:00
return true;
}
/* Check the custom data of panels under the mouse for a modifier. */
if (event != NULL) {
PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) {
if (RNA_struct_is_a(panel_ptr->type, &RNA_Constraint)) {
con = panel_ptr->data;
RNA_string_set(op->ptr, "constraint", con->name);
list = ED_object_constraint_list_from_constraint(ob, con, NULL);
RNA_enum_set(op->ptr,
"owner",
(&ob->constraints == list) ? EDIT_CONSTRAINT_OWNER_OBJECT :
EDIT_CONSTRAINT_OWNER_BONE);
return true;
}
BLI_assert(r_retval != NULL); /* We need the return value in this case. */
if (r_retval != NULL) {
*r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
}
return false;
}
}
2020-07-06 16:07:54 -04:00
return false;
}
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
static bConstraint *edit_constraint_property_get(bContext *C, wmOperator *op, Object *ob, int type)
{
char constraint_name[MAX_NAME];
int owner = RNA_enum_get(op->ptr, "owner");
bConstraint *con;
2012-04-28 15:42:27 +00:00
ListBase *list = NULL;
RNA_string_get(op->ptr, "constraint", constraint_name);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
if (owner == EDIT_CONSTRAINT_OWNER_BONE) {
list = ED_object_pose_constraint_list(C);
}
else {
list = &ob->constraints;
}
con = BKE_constraints_find_name(list, constraint_name);
#if 0
if (G.debug & G_DEBUG) {
printf("constraint found = %p, %s\n", (void *)con, (con) ? con->name : "<Not found>");
}
#endif
2019-04-22 09:19:45 +10:00
if (con && (type != 0) && (con->type != type)) {
con = NULL;
2019-04-22 09:19:45 +10:00
}
return con;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Stretch-To/Limit-Distance Constraint (Reset Original Length Operator)
*
* For Stretch-To & Limit-Distance constraints.
* \{ */
2012-04-28 15:42:27 +00:00
static int stretchto_reset_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, CONSTRAINT_TYPE_STRETCHTO);
2012-04-28 15:42:27 +00:00
bStretchToConstraint *data = (con) ? (bStretchToConstraint *)con->data : NULL;
/* despite 3 layers of checks, we may still not be able to find a constraint */
2019-04-22 09:19:45 +10:00
if (data == NULL) {
return OPERATOR_CANCELLED;
2019-04-22 09:19:45 +10:00
}
/* just set original length to 0.0, which will cause a reset on next recalc */
data->orglength = 0.0f;
ED_object_constraint_update(bmain, ob);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL);
return OPERATOR_FINISHED;
}
static int stretchto_reset_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return stretchto_reset_exec(C, op);
2019-04-22 09:19:45 +10:00
}
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_stretchto_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset Original Length";
ot->idname = "CONSTRAINT_OT_stretchto_reset";
ot->description = "Reset original length of bone for Stretch To Constraint";
/* callbacks */
ot->invoke = stretchto_reset_invoke;
ot->exec = stretchto_reset_exec;
ot->poll = edit_constraint_liboverride_allowed_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Distance Constraint (Reset Operator)
*
* For Limit-Distance constraint.
* \{ */
2012-04-28 15:42:27 +00:00
static int limitdistance_reset_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, CONSTRAINT_TYPE_DISTLIMIT);
2012-04-28 15:42:27 +00:00
bDistLimitConstraint *data = (con) ? (bDistLimitConstraint *)con->data : NULL;
/* despite 3 layers of checks, we may still not be able to find a constraint */
2019-04-22 09:19:45 +10:00
if (data == NULL) {
return OPERATOR_CANCELLED;
2019-04-22 09:19:45 +10:00
}
/* just set original length to 0.0, which will cause a reset on next recalc */
data->dist = 0.0f;
ED_object_constraint_update(bmain, ob);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL);
return OPERATOR_FINISHED;
}
static int limitdistance_reset_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return limitdistance_reset_exec(C, op);
2019-04-22 09:19:45 +10:00
}
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_limitdistance_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset Distance";
ot->idname = "CONSTRAINT_OT_limitdistance_reset";
ot->description = "Reset limiting distance for Limit Distance Constraint";
/* callbacks */
ot->invoke = limitdistance_reset_invoke;
ot->exec = limitdistance_reset_exec;
ot->poll = edit_constraint_liboverride_allowed_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Child-Of Constraint (Set Inverse Operator)
* \{ */
Constraints: replace 'Set Inverse' operator with an eval-time update This fixes {T70269}. Before this commit there was complicated code to try and compute the correct parent inverse matrix for the 'Child Of' and 'Object Solver' constraints outside the constraint evaluation. This was done mostly correctly, but did have some issues. The Set Inverse operator now defers this computation to be performed during constraint evaluation by just setting a flag. If the constraint is disabled, and thus tagging it for update in the depsgraph is not enough to trigger immediate evaluation, evaluation is forced by temporarily enabling it. This fix changes the way how the inverse matrix works when some of the channels of the constraint are disabled. Before this commit, the channel flags were used to filter both the parent and the inverse matrix. This meant that it was impossible to make an inverse matrix that would actually fully neutralize the effect of the constraint. Now only the parent matrix is filtered, while inverse is applied fully. As a result, pressing the 'Set Inverse' matrix produces the same transformation as disabling the constraint. This is also reflected in the changed values in the 'Child Of' unit test. This change is not backward compatible, but it should be OK because the old way was effectively unusable, so it is unlikely anybody relied on it. The change in matrix for the Object Solver constraint is due to a different method of computing it, which caused a slightly different floating point error that was slightly bigger than allowed by the test, so I updated the matrix values there as well. This patch was original written by @angavrilov and subsequently updated by me. Differential Revision: https://developer.blender.org/D6091
2020-02-27 10:24:11 +01:00
/* Force evaluation so that the 'set inverse' flag is handled.
* No-op when the constraint is enabled, as in such cases the evaluation will happen anyway.
*/
static void force_evaluation_if_constraint_disabled(bContext *C, Object *ob, bConstraint *con)
{
Constraints: replace 'Set Inverse' operator with an eval-time update This fixes {T70269}. Before this commit there was complicated code to try and compute the correct parent inverse matrix for the 'Child Of' and 'Object Solver' constraints outside the constraint evaluation. This was done mostly correctly, but did have some issues. The Set Inverse operator now defers this computation to be performed during constraint evaluation by just setting a flag. If the constraint is disabled, and thus tagging it for update in the depsgraph is not enough to trigger immediate evaluation, evaluation is forced by temporarily enabling it. This fix changes the way how the inverse matrix works when some of the channels of the constraint are disabled. Before this commit, the channel flags were used to filter both the parent and the inverse matrix. This meant that it was impossible to make an inverse matrix that would actually fully neutralize the effect of the constraint. Now only the parent matrix is filtered, while inverse is applied fully. As a result, pressing the 'Set Inverse' matrix produces the same transformation as disabling the constraint. This is also reflected in the changed values in the 'Child Of' unit test. This change is not backward compatible, but it should be OK because the old way was effectively unusable, so it is unlikely anybody relied on it. The change in matrix for the Object Solver constraint is due to a different method of computing it, which caused a slightly different floating point error that was slightly bigger than allowed by the test, so I updated the matrix values there as well. This patch was original written by @angavrilov and subsequently updated by me. Differential Revision: https://developer.blender.org/D6091
2020-02-27 10:24:11 +01:00
if ((con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) == 0) {
return;
}
Constraints: replace 'Set Inverse' operator with an eval-time update This fixes {T70269}. Before this commit there was complicated code to try and compute the correct parent inverse matrix for the 'Child Of' and 'Object Solver' constraints outside the constraint evaluation. This was done mostly correctly, but did have some issues. The Set Inverse operator now defers this computation to be performed during constraint evaluation by just setting a flag. If the constraint is disabled, and thus tagging it for update in the depsgraph is not enough to trigger immediate evaluation, evaluation is forced by temporarily enabling it. This fix changes the way how the inverse matrix works when some of the channels of the constraint are disabled. Before this commit, the channel flags were used to filter both the parent and the inverse matrix. This meant that it was impossible to make an inverse matrix that would actually fully neutralize the effect of the constraint. Now only the parent matrix is filtered, while inverse is applied fully. As a result, pressing the 'Set Inverse' matrix produces the same transformation as disabling the constraint. This is also reflected in the changed values in the 'Child Of' unit test. This change is not backward compatible, but it should be OK because the old way was effectively unusable, so it is unlikely anybody relied on it. The change in matrix for the Object Solver constraint is due to a different method of computing it, which caused a slightly different floating point error that was slightly bigger than allowed by the test, so I updated the matrix values there as well. This patch was original written by @angavrilov and subsequently updated by me. Differential Revision: https://developer.blender.org/D6091
2020-02-27 10:24:11 +01:00
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = DEG_get_evaluated_scene(depsgraph);
Constraints: replace 'Set Inverse' operator with an eval-time update This fixes {T70269}. Before this commit there was complicated code to try and compute the correct parent inverse matrix for the 'Child Of' and 'Object Solver' constraints outside the constraint evaluation. This was done mostly correctly, but did have some issues. The Set Inverse operator now defers this computation to be performed during constraint evaluation by just setting a flag. If the constraint is disabled, and thus tagging it for update in the depsgraph is not enough to trigger immediate evaluation, evaluation is forced by temporarily enabling it. This fix changes the way how the inverse matrix works when some of the channels of the constraint are disabled. Before this commit, the channel flags were used to filter both the parent and the inverse matrix. This meant that it was impossible to make an inverse matrix that would actually fully neutralize the effect of the constraint. Now only the parent matrix is filtered, while inverse is applied fully. As a result, pressing the 'Set Inverse' matrix produces the same transformation as disabling the constraint. This is also reflected in the changed values in the 'Child Of' unit test. This change is not backward compatible, but it should be OK because the old way was effectively unusable, so it is unlikely anybody relied on it. The change in matrix for the Object Solver constraint is due to a different method of computing it, which caused a slightly different floating point error that was slightly bigger than allowed by the test, so I updated the matrix values there as well. This patch was original written by @angavrilov and subsequently updated by me. Differential Revision: https://developer.blender.org/D6091
2020-02-27 10:24:11 +01:00
short flag_backup = con->flag;
con->flag &= ~(CONSTRAINT_DISABLE | CONSTRAINT_OFF);
BKE_object_eval_constraints(depsgraph, scene, ob);
con->flag = flag_backup;
}
/* ChildOf Constraint - set inverse callback */
2012-04-28 15:42:27 +00:00
static int childof_set_inverse_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, CONSTRAINT_TYPE_CHILDOF);
2012-04-28 15:42:27 +00:00
bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : NULL;
/* despite 3 layers of checks, we may still not be able to find a constraint */
if (data == NULL) {
2012-04-28 15:42:27 +00:00
printf("DEBUG: Child-Of Set Inverse - object = '%s'\n", (ob) ? ob->id.name + 2 : "<None>");
BKE_report(op->reports, RPT_ERROR, "Could not find constraint data for Child-Of Set Inverse");
return OPERATOR_CANCELLED;
}
Constraints: replace 'Set Inverse' operator with an eval-time update This fixes {T70269}. Before this commit there was complicated code to try and compute the correct parent inverse matrix for the 'Child Of' and 'Object Solver' constraints outside the constraint evaluation. This was done mostly correctly, but did have some issues. The Set Inverse operator now defers this computation to be performed during constraint evaluation by just setting a flag. If the constraint is disabled, and thus tagging it for update in the depsgraph is not enough to trigger immediate evaluation, evaluation is forced by temporarily enabling it. This fix changes the way how the inverse matrix works when some of the channels of the constraint are disabled. Before this commit, the channel flags were used to filter both the parent and the inverse matrix. This meant that it was impossible to make an inverse matrix that would actually fully neutralize the effect of the constraint. Now only the parent matrix is filtered, while inverse is applied fully. As a result, pressing the 'Set Inverse' matrix produces the same transformation as disabling the constraint. This is also reflected in the changed values in the 'Child Of' unit test. This change is not backward compatible, but it should be OK because the old way was effectively unusable, so it is unlikely anybody relied on it. The change in matrix for the Object Solver constraint is due to a different method of computing it, which caused a slightly different floating point error that was slightly bigger than allowed by the test, so I updated the matrix values there as well. This patch was original written by @angavrilov and subsequently updated by me. Differential Revision: https://developer.blender.org/D6091
2020-02-27 10:24:11 +01:00
/* Set a flag to request recalculation on next update. */
data->flag |= CHILDOF_SET_INVERSE;
/* Force constraint to run, it will perform the recalculation. */
force_evaluation_if_constraint_disabled(C, ob, con);
ED_object_constraint_update(bmain, ob);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int childof_set_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return childof_set_inverse_exec(C, op);
2019-04-22 09:19:45 +10:00
}
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_childof_set_inverse(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Inverse";
ot->idname = "CONSTRAINT_OT_childof_set_inverse";
ot->description = "Set inverse correction for ChildOf constraint";
/* callbacks */
ot->invoke = childof_set_inverse_invoke;
ot->exec = childof_set_inverse_exec;
ot->poll = edit_constraint_liboverride_allowed_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/* ChildOf Constraint - clear inverse callback */
2012-04-28 15:42:27 +00:00
static int childof_clear_inverse_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, CONSTRAINT_TYPE_CHILDOF);
2012-04-28 15:42:27 +00:00
bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : NULL;
2012-04-28 15:42:27 +00:00
if (data == NULL) {
2012-10-13 13:40:05 +00:00
BKE_report(op->reports, RPT_ERROR, "Child Of constraint not found");
return OPERATOR_CANCELLED;
}
/* simply clear the matrix */
unit_m4(data->invmat);
ED_object_constraint_update(bmain, ob);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int childof_clear_inverse_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return childof_clear_inverse_exec(C, op);
2019-04-22 09:19:45 +10:00
}
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_childof_clear_inverse(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Inverse";
ot->idname = "CONSTRAINT_OT_childof_clear_inverse";
ot->description = "Clear inverse correction for ChildOf constraint";
/* callbacks */
ot->invoke = childof_clear_inverse_invoke;
ot->exec = childof_clear_inverse_exec;
ot->poll = edit_constraint_liboverride_allowed_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Follow Path Constraint (Auto Animate Path Operator)
* \{ */
static int followpath_path_animate_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, CONSTRAINT_TYPE_FOLLOWPATH);
bFollowPathConstraint *data = (con) ? (bFollowPathConstraint *)con->data : NULL;
bAction *act = NULL;
FCurve *fcu = NULL;
int sfra = RNA_int_get(op->ptr, "frame_start");
int len = RNA_int_get(op->ptr, "length");
float standardRange = 1.0;
/* nearly impossible sanity check */
if (data == NULL) {
BKE_report(op->reports, RPT_ERROR, "Follow Path constraint not found");
return OPERATOR_CANCELLED;
}
/* add F-Curve as appropriate */
if (data->tar) {
Curve *cu = (Curve *)data->tar->data;
2014-01-12 22:05:24 +11:00
if (ELEM(NULL, cu->adt, cu->adt->action) ||
2020-06-05 09:30:15 +02:00
(BKE_fcurve_find(&cu->adt->action->curves, "eval_time", 0) == NULL)) {
/* create F-Curve for path animation */
act = ED_id_action_ensure(bmain, &cu->id);
fcu = ED_action_fcurve_ensure(bmain, act, NULL, NULL, "eval_time", 0);
/* standard vertical range - 1:1 = 100 frames */
standardRange = 100.0f;
}
else {
/* path anim exists already - abort for now as this may well be what was intended */
BKE_report(op->reports, RPT_WARNING, "Path is already animated");
return OPERATOR_CANCELLED;
}
}
else {
/* animate constraint's "fixed offset" */
PointerRNA ptr;
PropertyRNA *prop;
char *path;
/* get RNA pointer to constraint's "offset_factor" property - to build RNA path */
RNA_pointer_create(&ob->id, &RNA_FollowPathConstraint, con, &ptr);
prop = RNA_struct_find_property(&ptr, "offset_factor");
path = RNA_path_from_ID_to_property(&ptr, prop);
/* create F-Curve for constraint */
act = ED_id_action_ensure(bmain, &ob->id);
fcu = ED_action_fcurve_ensure(bmain, act, NULL, NULL, path, 0);
/* standard vertical range - 0.0 to 1.0 */
standardRange = 1.0f;
/* enable "Use Fixed Position" so that animating this has effect */
data->followflag |= FOLLOWPATH_STATIC;
/* path needs to be freed */
2019-04-22 09:19:45 +10:00
if (path) {
MEM_freeN(path);
2019-04-22 09:19:45 +10:00
}
}
/* setup dummy 'generator' modifier here to get 1-1 correspondence still working
* and define basic slope of this curve based on the properties
*/
if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) {
FModifier *fcm = add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR, fcu);
FMod_Generator *gen = fcm->data;
/* Assume that we have the following equation:
* y = Ax + B
* 1 0 <-- coefficients array indices
*/
float A = standardRange / (float)(len);
float B = (float)(-sfra) * A;
gen->coefficients[1] = A;
gen->coefficients[0] = B;
}
/* updates... */
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int followpath_path_animate_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
/* hook up invoke properties for figuring out which constraint we're dealing with */
if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return followpath_path_animate_exec(C, op);
}
return OPERATOR_CANCELLED;
}
void CONSTRAINT_OT_followpath_path_animate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Auto Animate Path";
ot->idname = "CONSTRAINT_OT_followpath_path_animate";
ot->description =
"Add default animation for path used by constraint if it isn't animated already";
/* callbacks */
ot->invoke = followpath_path_animate_invoke;
ot->exec = followpath_path_animate_exec;
ot->poll = edit_constraint_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
edit_constraint_properties(ot);
RNA_def_int(ot->srna,
"frame_start",
1,
MINAFRAME,
MAXFRAME,
"Start Frame",
"First frame of path animation",
MINAFRAME,
MAXFRAME);
RNA_def_int(ot->srna,
"length",
100,
0,
MAXFRAME,
"Length",
"Number of frames that path animation should take",
0,
MAXFRAME);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Object Solver Constraint (Set Inverse Operator)
* \{ */
2012-04-28 15:42:27 +00:00
static int objectsolver_set_inverse_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
2012-04-28 15:42:27 +00:00
bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : NULL;
/* despite 3 layers of checks, we may still not be able to find a constraint */
if (data == NULL) {
printf("DEBUG: ObjectSolver Set Inverse - object = '%s'\n", (ob) ? ob->id.name + 2 : "<None>");
BKE_report(
op->reports, RPT_ERROR, "Could not find constraint data for ObjectSolver Set Inverse");
return OPERATOR_CANCELLED;
}
Constraints: replace 'Set Inverse' operator with an eval-time update This fixes {T70269}. Before this commit there was complicated code to try and compute the correct parent inverse matrix for the 'Child Of' and 'Object Solver' constraints outside the constraint evaluation. This was done mostly correctly, but did have some issues. The Set Inverse operator now defers this computation to be performed during constraint evaluation by just setting a flag. If the constraint is disabled, and thus tagging it for update in the depsgraph is not enough to trigger immediate evaluation, evaluation is forced by temporarily enabling it. This fix changes the way how the inverse matrix works when some of the channels of the constraint are disabled. Before this commit, the channel flags were used to filter both the parent and the inverse matrix. This meant that it was impossible to make an inverse matrix that would actually fully neutralize the effect of the constraint. Now only the parent matrix is filtered, while inverse is applied fully. As a result, pressing the 'Set Inverse' matrix produces the same transformation as disabling the constraint. This is also reflected in the changed values in the 'Child Of' unit test. This change is not backward compatible, but it should be OK because the old way was effectively unusable, so it is unlikely anybody relied on it. The change in matrix for the Object Solver constraint is due to a different method of computing it, which caused a slightly different floating point error that was slightly bigger than allowed by the test, so I updated the matrix values there as well. This patch was original written by @angavrilov and subsequently updated by me. Differential Revision: https://developer.blender.org/D6091
2020-02-27 10:24:11 +01:00
/* Set a flag to request recalculation on next update. */
data->flag |= OBJECTSOLVER_SET_INVERSE;
/* Force constraint to run, it will perform the recalculation. */
force_evaluation_if_constraint_disabled(C, ob, con);
ED_object_constraint_update(bmain, ob);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int objectsolver_set_inverse_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return objectsolver_set_inverse_exec(C, op);
2019-04-22 09:19:45 +10:00
}
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_objectsolver_set_inverse(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Inverse";
ot->idname = "CONSTRAINT_OT_objectsolver_set_inverse";
ot->description = "Set inverse correction for ObjectSolver constraint";
/* callbacks */
ot->invoke = objectsolver_set_inverse_invoke;
ot->exec = objectsolver_set_inverse_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Object Solver Constraint (Clear Inverse Operator)
* \{ */
2012-04-28 15:42:27 +00:00
static int objectsolver_clear_inverse_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
2012-04-28 15:42:27 +00:00
bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : NULL;
2012-04-28 15:42:27 +00:00
if (data == NULL) {
2012-10-13 13:40:05 +00:00
BKE_report(op->reports, RPT_ERROR, "Child Of constraint not found");
return OPERATOR_CANCELLED;
}
/* simply clear the matrix */
unit_m4(data->invmat);
ED_object_constraint_update(bmain, ob);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
static int objectsolver_clear_inverse_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
if (edit_constraint_invoke_properties(C, op, NULL, NULL)) {
return objectsolver_clear_inverse_exec(C, op);
2019-04-22 09:19:45 +10:00
}
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_objectsolver_clear_inverse(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Inverse";
ot->idname = "CONSTRAINT_OT_objectsolver_clear_inverse";
ot->description = "Clear inverse correction for ObjectSolver constraint";
/* callbacks */
ot->invoke = objectsolver_clear_inverse_invoke;
ot->exec = objectsolver_clear_inverse_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Constraint Management Utilities
* \{ */
void ED_object_constraint_active_set(Object *ob, bConstraint *con)
{
ListBase *lb = ED_object_constraint_list_from_constraint(ob, con, NULL);
/* lets be nice and escape if its active already */
2012-07-07 22:51:57 +00:00
/* NOTE: this assumes that the stack doesn't have other active ones set... */
2019-04-22 09:19:45 +10:00
if ((lb && con) && (con->flag & CONSTRAINT_ACTIVE)) {
return;
2019-04-22 09:19:45 +10:00
}
BKE_constraints_active_set(lb, con);
}
void ED_object_constraint_update(Main *bmain, Object *ob)
{
2019-04-22 09:19:45 +10:00
if (ob->pose) {
BKE_pose_update_constraint_flags(ob->pose);
2019-04-22 09:19:45 +10:00
}
object_test_constraints(bmain, ob);
2019-04-22 09:19:45 +10:00
if (ob->type == OB_ARMATURE) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM);
2019-04-22 09:19:45 +10:00
}
else {
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
2019-04-22 09:19:45 +10:00
}
}
static void object_pose_tag_update(Main *bmain, Object *ob)
{
BKE_pose_tag_recalc(bmain, ob->pose); /* Checks & sort pose channels. */
if (ob->proxy && ob->adt) {
/* We need to make use of ugly #POSE_ANIMATION_WORKAROUND here too,
* else anim data are not reloaded after calling `BKE_pose_rebuild()`,
* which causes T43872.
* Note that this is a bit wide here, since we cannot be sure whether there are some locked
* proxy bones or not.
* XXX Temp hack until new depsgraph hopefully solves this. */
DEG_id_tag_update(&ob->id, ID_RECALC_ANIMATION);
}
}
2013-02-22 00:51:58 +00:00
void ED_object_constraint_dependency_update(Main *bmain, Object *ob)
{
ED_object_constraint_update(bmain, ob);
if (ob->pose) {
object_pose_tag_update(bmain, ob);
}
DEG_relations_tag_update(bmain);
}
void ED_object_constraint_tag_update(Main *bmain, Object *ob, bConstraint *con)
{
if (ob->pose) {
BKE_pose_tag_update_constraint_flags(ob->pose);
}
if (con) {
object_test_constraint(bmain, ob, con);
}
2019-04-22 09:19:45 +10:00
if (ob->type == OB_ARMATURE) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM);
2019-04-22 09:19:45 +10:00
}
else {
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
2019-04-22 09:19:45 +10:00
}
/* Do Copy-on-Write tag here too, otherwise constraint
* influence/mute buttons in UI have no effect
*/
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
void ED_object_constraint_dependency_tag_update(Main *bmain, Object *ob, bConstraint *con)
{
ED_object_constraint_tag_update(bmain, ob, con);
if (ob->pose) {
object_pose_tag_update(bmain, ob);
}
DEG_relations_tag_update(bmain);
}
bool ED_object_constraint_move_to_index(Object *ob, bConstraint *con, const int index)
{
BLI_assert(con != NULL);
BLI_assert(index >= 0);
ListBase *conlist = ED_object_constraint_list_from_constraint(ob, con, NULL);
int current_index = BLI_findindex(conlist, con);
BLI_assert(current_index >= 0);
BLI_listbase_link_move(conlist, con, index - current_index);
WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT, ob);
return true;
}
void ED_object_constraint_link(Main *bmain, Object *ob_dst, ListBase *dst, ListBase *src)
{
BKE_constraints_free(dst);
BKE_constraints_copy(dst, src, true);
LISTBASE_FOREACH (bConstraint *, con, dst) {
ED_object_constraint_dependency_tag_update(bmain, ob_dst, con);
}
WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_ADDED, NULL);
}
void ED_object_constraint_copy_for_object(Main *bmain, Object *ob_dst, bConstraint *con)
{
BKE_constraint_copy_for_object(ob_dst, con);
ED_object_constraint_dependency_tag_update(bmain, ob_dst, con);
WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob_dst);
}
void ED_object_constraint_copy_for_pose(Main *bmain,
Object *ob_dst,
bPoseChannel *pchan,
bConstraint *con)
{
BKE_constraint_copy_for_pose(ob_dst, pchan, con);
ED_object_constraint_dependency_tag_update(bmain, ob_dst, con);
WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob_dst);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Delete Constraint Operator
* \{ */
static int constraint_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
ListBase *lb = ED_object_constraint_list_from_constraint(ob, con, NULL);
/* Store name temporarily for report. */
char name[MAX_NAME];
strcpy(name, con->name);
/* free the constraint */
if (BKE_constraint_remove_ex(lb, ob, con, true)) {
/* there's no active constraint now, so make sure this is the case */
BKE_constraints_active_set(&ob->constraints, NULL);
/* needed to set the flags on posebones correctly */
ED_object_constraint_update(bmain, ob);
2019-04-15 11:44:56 +02:00
/* relations */
DEG_relations_tag_update(CTX_data_main(C));
/* notifiers */
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
if (RNA_boolean_get(op->ptr, "report")) {
BKE_reportf(op->reports, RPT_INFO, "Removed constraint: %s", name);
}
return OPERATOR_FINISHED;
}
/* couldn't remove due to some invalid data */
return OPERATOR_CANCELLED;
}
static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
if (edit_constraint_invoke_properties(C, op, event, &retval)) {
return constraint_delete_exec(C, op);
}
return OPERATOR_CANCELLED;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_delete(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Constraint";
ot->idname = "CONSTRAINT_OT_delete";
ot->description = "Remove constraint from constraint stack";
/* callbacks */
ot->invoke = constraint_delete_invoke;
ot->exec = constraint_delete_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
edit_constraint_properties(ot);
edit_constraint_report_property(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Move Down Constraint Operator
* \{ */
2012-04-28 15:42:27 +00:00
static int constraint_move_down_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
if (con && con->next) {
ListBase *conlist = ED_object_constraint_list_from_constraint(ob, con, NULL);
2012-04-28 15:42:27 +00:00
bConstraint *nextCon = con->next;
/* insert the nominated constraint after the one that used to be after it */
BLI_remlink(conlist, con);
BLI_insertlinkafter(conlist, nextCon, con);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static int constraint_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
if (edit_constraint_invoke_properties(C, op, event, &retval)) {
return constraint_move_down_exec(C, op);
2019-04-22 09:19:45 +10:00
}
return retval;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_move_down(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Move Constraint Down";
ot->idname = "CONSTRAINT_OT_move_down";
ot->description = "Move constraint down in constraint stack";
/* callbacks */
ot->invoke = constraint_move_down_invoke;
ot->exec = constraint_move_down_exec;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
edit_constraint_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Move Up Constraint Operator
* \{ */
2012-04-28 15:42:27 +00:00
static int constraint_move_up_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
if (con && con->prev) {
ListBase *conlist = ED_object_constraint_list_from_constraint(ob, con, NULL);
2012-04-28 15:42:27 +00:00
bConstraint *prevCon = con->prev;
/* insert the nominated constraint before the one that used to be before it */
BLI_remlink(conlist, con);
BLI_insertlinkbefore(conlist, prevCon, con);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static int constraint_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
if (edit_constraint_invoke_properties(C, op, event, &retval)) {
return constraint_move_up_exec(C, op);
2019-04-22 09:19:45 +10:00
}
return retval;
}
2012-04-28 15:42:27 +00:00
void CONSTRAINT_OT_move_up(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Move Constraint Up";
ot->idname = "CONSTRAINT_OT_move_up";
ot->description = "Move constraint up in constraint stack";
/* callbacks */
ot->exec = constraint_move_up_exec;
ot->invoke = constraint_move_up_invoke;
ot->poll = edit_constraint_poll;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
edit_constraint_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Move Constraint To Index Operator
* \{ */
static int constraint_move_to_index_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
Fix T80464: Crash deleting bone constraints when the armature layer is not active Caused by {rB608d9b5aa1f1} Prior to rB608d9b5aa1f1, the constraint was gotten using **context** [CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint) -- which is valid for bones on hidden layers]. After rB608d9b5aa1f1, the constraint is found (or isnt) using `edit_constraint_property_get` [this is **not** valid for bones on hidden layers because internally `BKE_pose_channel_active` checks if the bone is on an active layer]. Some observations: - Every operator using `edit_constraint_property_get` doesnt work for bones on inactive layers [delete, moveup, movedown, move to index (drag n drop nowadays)] -- moveup, movedown, move to index check if they could find a constraint beforehand though (dont crash) -- delete crashes (doesnt check if a constraint could actually be found) - Every operator using `edit_constraint_property_get` for constraint data doesnt work for bones on inactive layers [stretchto_reset, limitdistance_reset, childof_set_inverse, ...] -- these all check if they could find a constraint beforehand though (dont crash) This is because the poll function is using **context** to get the constraint, the operators themselves use **edit_constraint_property_get** which leads to inconsistent/unexpected results. Possible solutions were: - [1] let the delete operator just work with the context constraint again (like prior to rB608d9b5aa1f1) -- allows for deleting constraints on bones in inactive layers - [2] check if we could get a constraint -- prevents the crash, but does **not** allow for deleting constraints on bones in inactive layers - [3] make the poll `edit_constraint_poll_generic` be as strict as the operators -- dont use **context** to get the constraint, but something like **edit_constraint_property_get** - [4] make the operators be more graceful and let them act on bones on hidden layers -- let **edit_constraint_property_get** actually use the same **context** This patch implements [4], so poll an doperators are now in sync. - prevents reported crash - also enables operators for bone constraints on hidden layers - also enables drag and drop reordering of constraints on hidden layers This might be a candidate for 2.90.1? (if it is, take care to include prior "Refactor getting constraints" refactoring commit) Note: Adding constraints also doesnt work for bones on inactive layers [that was the case in 2.79 as well -- it is also using `BKE_pose_channel_active`] Maniphest Tasks: T80464 Differential Revision: https://developer.blender.org/D8805
2020-09-09 14:10:47 +02:00
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
int new_index = RNA_int_get(op->ptr, "index");
if (new_index < 0) {
new_index = 0;
}
if (con) {
ED_object_constraint_move_to_index(ob, con, new_index);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static int constraint_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
if (edit_constraint_invoke_properties(C, op, event, &retval)) {
return constraint_move_to_index_exec(C, op);
}
return retval;
}
void CONSTRAINT_OT_move_to_index(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Move Constraint To Index";
ot->idname = "CONSTRAINT_OT_move_to_index";
ot->description =
"Change the constraint's position in the list so it evaluates after the set number of "
"others";
/* callbacks */
ot->exec = constraint_move_to_index_exec;
ot->invoke = constraint_move_to_index_invoke;
ot->poll = edit_constraint_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
edit_constraint_properties(ot);
RNA_def_int(ot->srna,
"index",
0,
0,
INT_MAX,
"Index",
"The index to move the constraint to",
0,
INT_MAX);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Clear Pose Constraints Operator
* \{ */
static int pose_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
Object *prev_ob = NULL;
/* free constraints for all selected bones */
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
BKE_constraints_free(&pchan->constraints);
2012-04-28 15:42:27 +00:00
pchan->constflag &= ~(PCHAN_HAS_IK | PCHAN_HAS_SPLINEIK | PCHAN_HAS_CONST);
if (prev_ob != ob) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
prev_ob = ob;
}
}
CTX_DATA_END;
/* force depsgraph to get recalculated since relationships removed */
DEG_relations_tag_update(bmain);
2012-03-18 07:38:51 +00:00
/* note, calling BIK_clear_data() isn't needed here */
return OPERATOR_FINISHED;
}
void POSE_OT_constraints_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Pose Constraints";
ot->idname = "POSE_OT_constraints_clear";
ot->description = "Clear all the constraints for the selected bones";
/* callbacks */
ot->exec = pose_constraints_clear_exec;
ot->poll = ED_operator_posemode_exclusive; /* XXX - do we want to ensure there are selected
* bones too? */
}
static int object_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
/* do freeing */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
BKE_constraints_free(&ob->constraints);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
}
CTX_DATA_END;
/* force depsgraph to get recalculated since relationships removed */
DEG_relations_tag_update(bmain);
/* do updates */
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, NULL);
return OPERATOR_FINISHED;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Clear Object Constraints Operator
* \{ */
void OBJECT_OT_constraints_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Object Constraints";
ot->idname = "OBJECT_OT_constraints_clear";
ot->description = "Clear all the constraints for the active Object only";
/* callbacks */
ot->exec = object_constraints_clear_exec;
ot->poll = ED_operator_object_active_local_editable;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Copy Pose Constraints Operator
* \{ */
static int pose_constraint_copy_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
bPoseChannel *pchan = CTX_data_active_pose_bone(C);
/* don't do anything if bone doesn't exist or doesn't have any constraints */
if (ELEM(NULL, pchan, pchan->constraints.first)) {
BKE_report(op->reports, RPT_ERROR, "No active bone with constraints for copying");
return OPERATOR_CANCELLED;
}
Object *prev_ob = NULL;
/* copy all constraints from active posebone to all selected posebones */
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) {
/* if we're not handling the object we're copying from, copy all constraints over */
if (pchan != chan) {
BKE_constraints_copy(&chan->constraints, &pchan->constraints, true);
/* update flags (need to add here, not just copy) */
chan->constflag |= pchan->constflag;
if (prev_ob != ob) {
BKE_pose_tag_recalc(bmain, ob->pose);
DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY);
prev_ob = ob;
}
}
}
CTX_DATA_END;
/* force depsgraph to get recalculated since new relationships added */
DEG_relations_tag_update(bmain);
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL);
return OPERATOR_FINISHED;
}
void POSE_OT_constraints_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Constraints to Selected Bones";
ot->idname = "POSE_OT_constraints_copy";
ot->description = "Copy constraints to other selected bones";
/* api callbacks */
ot->exec = pose_constraint_copy_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Copy Object Constraints Operator
* \{ */
static int object_constraint_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
Object *obact = ED_object_active_context(C);
/* copy all constraints from active object to all selected objects */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
/* if we're not handling the object we're copying from, copy all constraints over */
if (obact != ob) {
BKE_constraints_copy(&ob->constraints, &obact->constraints, true);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM);
}
}
CTX_DATA_END;
/* force depsgraph to get recalculated since new relationships added */
DEG_relations_tag_update(bmain);
/* notifiers for updates */
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, NULL);
return OPERATOR_FINISHED;
}
void OBJECT_OT_constraints_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Constraints to Selected Objects";
ot->idname = "OBJECT_OT_constraints_copy";
ot->description = "Copy constraints to other selected objects";
/* api callbacks */
ot->exec = object_constraint_copy_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Add Constraints Operator
* \{ */
/* get the Object and/or PoseChannel to use as target */
2016-06-27 15:47:44 +12:00
static bool get_new_constraint_target(
bContext *C, int con_type, Object **tar_ob, bPoseChannel **tar_pchan, bool add)
{
2012-04-28 15:42:27 +00:00
Object *obact = ED_object_active_context(C);
bPoseChannel *pchanact = BKE_pose_channel_active(obact);
2016-06-27 15:47:44 +12:00
bool only_curve = false, only_mesh = false, only_ob = false;
bool found = false;
/* clear tar_ob and tar_pchan fields before use
2018-11-14 12:53:15 +11:00
* - assume for now that both always exist...
*/
2012-04-28 15:42:27 +00:00
*tar_ob = NULL;
*tar_pchan = NULL;
/* check if constraint type doesn't requires a target
2018-11-14 12:53:15 +11:00
* - if so, no need to get any targets
*/
switch (con_type) {
/* no-target constraints --------------------------- */
2012-04-28 15:42:27 +00:00
/* null constraint - shouldn't even be added! */
case CONSTRAINT_TYPE_NULL:
2012-04-28 15:42:27 +00:00
/* limit constraints - no targets needed */
case CONSTRAINT_TYPE_LOCLIMIT:
case CONSTRAINT_TYPE_ROTLIMIT:
case CONSTRAINT_TYPE_SIZELIMIT:
case CONSTRAINT_TYPE_SAMEVOL:
2016-06-27 15:47:44 +12:00
return false;
/* restricted target-type constraints -------------- */
/* NOTE: for these, we cannot try to add a target object if no valid ones are found,
* since that doesn't work */
2012-04-28 15:42:27 +00:00
/* curve-based constraints - set the only_curve and only_ob flags */
case CONSTRAINT_TYPE_CLAMPTO:
case CONSTRAINT_TYPE_FOLLOWPATH:
case CONSTRAINT_TYPE_SPLINEIK:
2016-06-27 15:47:44 +12:00
only_curve = true;
only_ob = true;
add = false;
break;
2012-04-28 15:42:27 +00:00
/* mesh only? */
case CONSTRAINT_TYPE_SHRINKWRAP:
2016-06-27 15:47:44 +12:00
only_mesh = true;
only_ob = true;
add = false;
break;
}
/* if the active Object is Armature, and we can search for bones, do so... */
2016-06-27 15:47:44 +12:00
if ((obact->type == OB_ARMATURE) && (only_ob == false)) {
/* search in list of selected Pose-Channels for target */
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) {
/* just use the first one that we encounter, as long as it is not the active one */
if (pchan != pchanact) {
2012-04-28 15:42:27 +00:00
*tar_ob = obact;
*tar_pchan = pchan;
2016-06-27 15:47:44 +12:00
found = true;
break;
}
}
CTX_DATA_END;
}
/* if not yet found, try selected Objects... */
2016-06-27 15:47:44 +12:00
if (found == false) {
/* search in selected objects context */
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
/* just use the first object we encounter (that isn't the active object)
* and which fulfills the criteria for the object-target that we've got
*/
if (ob != obact) {
/* for armatures in pose mode, look inside the armature for the active bone
* so that we set up cross-armature constraints with less effort
*/
if ((ob->type == OB_ARMATURE) && (ob->mode & OB_MODE_POSE) &&
(!only_curve && !only_mesh)) {
/* Only use the object & bone if the bone is visible & selected
* since we may have multiple objects in pose mode at once. */
bPoseChannel *pchan = BKE_pose_channel_active_or_first_selected(ob);
if (pchan != NULL) {
*tar_pchan = pchan;
*tar_ob = ob;
found = true;
}
break;
}
if (((!only_curve) || (ob->type == OB_CURVE)) && ((!only_mesh) || (ob->type == OB_MESH))) {
/* set target */
*tar_ob = ob;
2016-06-27 15:47:44 +12:00
found = true;
/* perform some special operations on the target */
if (only_curve) {
/* Curve-Path option must be enabled for follow-path constraints to be able to work
*/
Curve *cu = (Curve *)ob->data;
cu->flag |= CU_PATH;
}
break;
}
}
}
CTX_DATA_END;
}
/* if still not found, add a new empty to act as a target (if allowed) */
2016-06-27 15:47:44 +12:00
if ((found == false) && (add)) {
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Base *base = BASACT(view_layer);
Object *obt;
/* add new target object */
obt = BKE_object_add(bmain, view_layer, OB_EMPTY, NULL);
/* transform cent to global coords for loc */
if (pchanact) {
/* Since by default, IK targets the tip of the last bone,
* use the tip of the active PoseChannel if adding a target for an IK Constraint. */
2019-04-22 09:19:45 +10:00
if (con_type == CONSTRAINT_TYPE_KINEMATIC) {
mul_v3_m4v3(obt->loc, obact->obmat, pchanact->pose_tail);
2019-04-22 09:19:45 +10:00
}
else {
mul_v3_m4v3(obt->loc, obact->obmat, pchanact->pose_head);
2019-04-22 09:19:45 +10:00
}
}
else {
copy_v3_v3(obt->loc, obact->obmat[3]);
}
/* restore, BKE_object_add sets active */
BASACT(view_layer) = base;
ED_object_base_select(base, BA_SELECT);
/* make our new target the new object */
2012-04-28 15:42:27 +00:00
*tar_ob = obt;
2016-06-27 15:47:44 +12:00
found = true;
}
/* return whether there's any target */
return found;
}
/* used by add constraint operators to add the constraint required */
2014-02-03 18:55:59 +11:00
static int constraint_add_exec(
bContext *C, wmOperator *op, Object *ob, ListBase *list, int type, const bool setTarget)
{
2012-04-28 15:42:27 +00:00
Main *bmain = CTX_data_main(C);
bPoseChannel *pchan;
bConstraint *con;
if (list == &ob->constraints) {
2012-04-28 15:42:27 +00:00
pchan = NULL;
}
else {
pchan = BKE_pose_channel_active(ob);
/* ensure not to confuse object/pose adding */
if (pchan == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active pose bone to add a constraint to");
return OPERATOR_CANCELLED;
}
}
/* check if constraint to be added is valid for the given constraints stack */
if (type == CONSTRAINT_TYPE_NULL) {
return OPERATOR_CANCELLED;
}
/* Create a new constraint of the type required,
* and add it to the active/given constraints list. */
2019-04-22 09:19:45 +10:00
if (pchan) {
con = BKE_constraint_add_for_pose(ob, pchan, NULL, type);
2019-04-22 09:19:45 +10:00
}
else {
con = BKE_constraint_add_for_object(ob, NULL, type);
2019-04-22 09:19:45 +10:00
}
/* get the first selected object/bone, and make that the target
2018-11-14 12:53:15 +11:00
* - apart from the buttons-window add buttons, we shouldn't add in this way
*/
if (setTarget) {
2012-04-28 15:42:27 +00:00
Object *tar_ob = NULL;
bPoseChannel *tar_pchan = NULL;
/* get the target objects, adding them as need be */
if (get_new_constraint_target(C, type, &tar_ob, &tar_pchan, 1)) {
/* Method of setting target depends on the type of target we've got - by default,
* just set the first target (distinction here is only for multiple-targeted constraints).
*/
2019-04-22 09:19:45 +10:00
if (tar_pchan) {
set_constraint_nth_target(con, tar_ob, tar_pchan->name, 0);
2019-04-22 09:19:45 +10:00
}
else {
set_constraint_nth_target(con, tar_ob, "", 0);
2019-04-22 09:19:45 +10:00
}
}
}
/* do type-specific tweaking to the constraint settings */
switch (type) {
case CONSTRAINT_TYPE_PYTHON: /* FIXME: this code is not really valid anymore */
{
#ifdef WITH_PYTHON
char *menustr;
2012-04-28 15:42:27 +00:00
int scriptint = 0;
/* popup a list of usable scripts */
2018-06-06 15:50:24 +02:00
menustr = buildmenu_pyconstraints(bmain, NULL, &scriptint);
/* XXX scriptint = pupmenu(menustr); */
MEM_freeN(menustr);
/* only add constraint if a script was chosen */
if (scriptint) {
/* add constraint */
2018-06-06 15:50:24 +02:00
validate_pyconstraint_cb(bmain, con->data, &scriptint);
/* make sure target allowance is set correctly */
BPY_pyconstraint_update(ob, con);
}
#endif
Camera tracking integration =========================== Commiting camera tracking integration gsoc project into trunk. This commit includes: - Bundled version of libmv library (with some changes against official repo, re-sync with libmv repo a bit later) - New datatype ID called MovieClip which is optimized to work with movie clips (both of movie files and image sequences) and doing camera/motion tracking operations. - New editor called Clip Editor which is currently used for motion/tracking stuff only, but which can be easily extended to work with masks too. This editor supports: * Loading movie files/image sequences * Build proxies with different size for loaded movie clip, also supports building undistorted proxies to increase speed of playback in undistorted mode. * Manual lens distortion mode calibration using grid and grease pencil * Supervised 2D tracking using two different algorithms KLT and SAD. * Basic algorithm for feature detection * Camera motion solving. scene orientation - New constraints to "link" scene objects with solved motions from clip: * Follow Track (make object follow 2D motion of track with given name or parent object to reconstructed 3D position of track) * Camera Solver to make camera moving in the same way as reconstructed camera This commit NOT includes changes from tomato branch: - New nodes (they'll be commited as separated patch) - Automatic image offset guessing for image input node and image editor (need to do more tests and gather more feedback) - Code cleanup in libmv-capi. It's not so critical cleanup, just increasing readability and understanadability of code. Better to make this chaneg when Keir will finish his current patch. More details about this project can be found on this page: http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011 Further development of small features would be done in trunk, bigger/experimental features would first be implemented in tomato branch.
2011-11-07 12:55:18 +00:00
break;
}
default:
break;
}
/* make sure all settings are valid - similar to above checks, but sometimes can be wrong */
object_test_constraints(bmain, ob);
2019-04-22 09:19:45 +10:00
if (pchan) {
BKE_pose_update_constraint_flags(ob->pose);
2019-04-22 09:19:45 +10:00
}
/* force depsgraph to get recalculated since new relationships added */
DEG_relations_tag_update(bmain);
2012-04-28 15:42:27 +00:00
if ((ob->type == OB_ARMATURE) && (pchan)) {
BKE_pose_tag_recalc(bmain, ob->pose); /* sort pose channels */
if (BKE_constraints_proxylocked_owner(ob, pchan) && ob->adt) {
/* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too,
* else anim data are not reloaded after calling `BKE_pose_rebuild()`, which causes T43872.
* XXX Temp hack until new depsgraph hopefully solves this. */
DEG_id_tag_update(&ob->id, ID_RECALC_ANIMATION);
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM);
}
2019-04-22 09:19:45 +10:00
else {
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
2019-04-22 09:19:45 +10:00
}
/* notifiers for updates */
2012-04-28 15:42:27 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob);
return OPERATOR_FINISHED;
}
/* ------------------ */
/* dummy operator callback */
static int object_constraint_add_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Object *ob = ED_object_active_context(C);
int type = RNA_enum_get(op->ptr, "type");
short with_targets = 0;
if (!ob) {
BKE_report(op->reports, RPT_ERROR, "No active object to add constraint to");
return OPERATOR_CANCELLED;
}
/* hack: set constraint targets from selected objects in context is allowed when
2018-11-14 12:53:15 +11:00
* operator name included 'with_targets', since the menu doesn't allow multiple properties
*/
2019-04-22 09:19:45 +10:00
if (strstr(op->idname, "with_targets")) {
2012-04-28 15:42:27 +00:00
with_targets = 1;
2019-04-22 09:19:45 +10:00
}
return constraint_add_exec(C, op, ob, &ob->constraints, type, with_targets);
}
/* dummy operator callback */
static int pose_constraint_add_exec(bContext *C, wmOperator *op)
{
Object *ob = BKE_object_pose_armature_get(ED_object_active_context(C));
2012-04-28 15:42:27 +00:00
int type = RNA_enum_get(op->ptr, "type");
short with_targets = 0;
if (!ob) {
BKE_report(op->reports, RPT_ERROR, "No active object to add constraint to");
return OPERATOR_CANCELLED;
}
/* hack: set constraint targets from selected objects in context is allowed when
2018-11-14 12:53:15 +11:00
* operator name included 'with_targets', since the menu doesn't allow multiple properties
*/
2019-04-22 09:19:45 +10:00
if (strstr(op->idname, "with_targets")) {
2012-04-28 15:42:27 +00:00
with_targets = 1;
2019-04-22 09:19:45 +10:00
}
return constraint_add_exec(C, op, ob, ED_object_constraint_active_list(ob), type, with_targets);
}
/* ------------------ */
/* Filters constraints that are only compatible with bones */
static const EnumPropertyItem *object_constraint_add_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
const EnumPropertyItem *item = rna_enum_constraint_type_items;
EnumPropertyItem *object_constraint_items = NULL;
int totitem = 0;
2019-05-06 11:59:03 +10:00
while (item->identifier) {
2020-11-06 12:30:59 +11:00
if (!ELEM(item->value, CONSTRAINT_TYPE_KINEMATIC, CONSTRAINT_TYPE_SPLINEIK)) {
RNA_enum_item_add(&object_constraint_items, &totitem, item);
}
item++;
}
RNA_enum_item_end(&object_constraint_items, &totitem);
*r_free = true;
return object_constraint_items;
}
void OBJECT_OT_constraint_add(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Add Constraint";
ot->description = "Add a constraint to the active object";
ot->idname = "OBJECT_OT_constraint_add";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = object_constraint_add_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna, "type", DummyRNA_NULL_items, 0, "Type", "");
RNA_def_enum_funcs(prop, object_constraint_add_itemf);
ot->prop = prop;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Add Constraints With Targets Operator
* \{ */
void OBJECT_OT_constraint_add_with_targets(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Add Constraint (with Targets)";
ot->description =
"Add a constraint to the active object, with target (where applicable) set to the "
"selected Objects/Bones";
ot->idname = "OBJECT_OT_constraint_add_with_targets";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = object_constraint_add_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna, "type", DummyRNA_NULL_items, 0, "Type", "");
RNA_def_enum_funcs(prop, object_constraint_add_itemf);
ot->prop = prop;
}
void POSE_OT_constraint_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Constraint";
ot->description = "Add a constraint to the active bone";
ot->idname = "POSE_OT_constraint_add";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = pose_constraint_add_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_constraint_type_items, 0, "Type", "");
}
void POSE_OT_constraint_add_with_targets(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Constraint (with Targets)";
ot->description =
"Add a constraint to the active bone, with target (where applicable) set to the selected "
"Objects/Bones";
ot->idname = "POSE_OT_constraint_add_with_targets";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = pose_constraint_add_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_constraint_type_items, 0, "Type", "");
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Add IK to Bone Operator
*
* \note Only for pose-channels.
* \{ */
/* TODO: should these be here, or back in editors/armature/poseobject.c again? */
/* present menu with options + validation for targets to use */
static int pose_ik_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
bPoseChannel *pchan = BKE_pose_channel_active(ob);
2012-04-28 15:42:27 +00:00
bConstraint *con = NULL;
uiPopupMenu *pup;
uiLayout *layout;
2012-04-28 15:42:27 +00:00
Object *tar_ob = NULL;
bPoseChannel *tar_pchan = NULL;
/* must have active bone */
if (ELEM(NULL, ob, pchan)) {
BKE_report(op->reports, RPT_ERROR, "Must have an active bone to add IK constraint to");
return OPERATOR_CANCELLED;
}
/* bone must not have any constraints already */
2012-04-28 15:42:27 +00:00
for (con = pchan->constraints.first; con; con = con->next) {
2019-04-22 09:19:45 +10:00
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
2012-04-28 15:42:27 +00:00
break;
2019-04-22 09:19:45 +10:00
}
}
if (con) {
BKE_report(op->reports, RPT_ERROR, "Bone already has an IK constraint");
return OPERATOR_CANCELLED;
}
/* prepare popup menu to choose targeting options */
pup = UI_popup_menu_begin(C, IFACE_("Add IK"), ICON_NONE);
layout = UI_popup_menu_layout(pup);
/* the type of targets we'll set determines the menu entries to show... */
if (get_new_constraint_target(C, CONSTRAINT_TYPE_KINEMATIC, &tar_ob, &tar_pchan, 0)) {
/* bone target, or object target?
2018-11-14 12:53:15 +11:00
* - the only thing that matters is that we want a target...
*/
2019-04-22 09:19:45 +10:00
if (tar_pchan) {
uiItemBooleanO(
layout, IFACE_("To Active Bone"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
2019-04-22 09:19:45 +10:00
}
else {
uiItemBooleanO(
layout, IFACE_("To Active Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
2019-04-22 09:19:45 +10:00
}
}
else {
/* we have a choice of adding to a new empty, or not setting any target (targetless IK) */
uiItemBooleanO(
layout, IFACE_("To New Empty Object"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 1);
uiItemBooleanO(
layout, IFACE_("Without Targets"), ICON_NONE, "POSE_OT_ik_add", "with_targets", 0);
}
/* finish building the menu, and process it (should result in calling self again) */
UI_popup_menu_end(C, pup);
return OPERATOR_INTERFACE;
}
/* call constraint_add_exec() to add the IK constraint */
static int pose_ik_add_exec(bContext *C, wmOperator *op)
{
2012-04-28 15:42:27 +00:00
Object *ob = CTX_data_active_object(C);
2014-02-03 18:55:59 +11:00
const bool with_targets = RNA_boolean_get(op->ptr, "with_targets");
/* add the constraint - all necessary checks should have
* been done by the invoke() callback already... */
return constraint_add_exec(
C, op, ob, ED_object_constraint_active_list(ob), CONSTRAINT_TYPE_KINEMATIC, with_targets);
}
void POSE_OT_ik_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add IK to Bone";
ot->description = "Add IK Constraint to the active Bone";
ot->idname = "POSE_OT_ik_add";
/* api callbacks */
ot->invoke = pose_ik_add_invoke;
ot->exec = pose_ik_add_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_boolean(ot->srna,
"with_targets",
1,
"With Targets",
"Assign IK Constraint with targets derived from the select bones/objects");
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Clear IK from Bone Operator
*
* Remove IK constraints from selected bones.
* \{ */
static int pose_ik_clear_exec(bContext *C, wmOperator *UNUSED(op))
{
2018-10-16 18:30:27 -03:00
Object *prev_ob = NULL;
/* only remove IK Constraints */
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
bConstraint *con, *next;
/* TODO: should we be checking if these constraints were local
* before we try and remove them? */
2012-04-28 15:42:27 +00:00
for (con = pchan->constraints.first; con; con = next) {
next = con->next;
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
BKE_constraint_remove(&pchan->constraints, con);
}
}
2012-04-28 15:42:27 +00:00
pchan->constflag &= ~(PCHAN_HAS_IK | PCHAN_HAS_TARGET);
2018-10-16 18:30:27 -03:00
if (prev_ob != ob) {
prev_ob = ob;
2018-10-16 18:30:27 -03:00
/* Refresh depsgraph. */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
2018-10-16 18:30:27 -03:00
/* Note, notifier might evolve. */
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
}
}
CTX_DATA_END;
return OPERATOR_FINISHED;
}
void POSE_OT_ik_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove IK";
ot->description = "Remove all IK Constraints from selected bones";
ot->idname = "POSE_OT_ik_clear";
/* api callbacks */
ot->exec = pose_ik_clear_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
2012-04-28 15:42:27 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */