2009-01-10 19:34:23 +00:00
|
|
|
/**
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
* 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,
|
|
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* The Original Code is: all of this file.
|
|
|
|
*
|
|
|
|
* Contributor(s): Joshua Leung, Blender Foundation
|
|
|
|
*
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "BLI_blenlib.h"
|
2009-11-10 20:43:45 +00:00
|
|
|
#include "BLI_math.h"
|
2009-01-10 19:34:23 +00:00
|
|
|
#include "BLI_dynstr.h"
|
|
|
|
|
|
|
|
#include "DNA_action_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_screen_types.h"
|
|
|
|
#include "DNA_text_types.h"
|
|
|
|
#include "DNA_view3d_types.h"
|
|
|
|
|
|
|
|
#include "BKE_action.h"
|
|
|
|
#include "BKE_armature.h"
|
|
|
|
#include "BKE_constraint.h"
|
2009-05-27 00:03:49 +00:00
|
|
|
#include "BKE_context.h"
|
2009-01-10 19:34:23 +00:00
|
|
|
#include "BKE_depsgraph.h"
|
|
|
|
#include "BKE_global.h"
|
|
|
|
#include "BKE_main.h"
|
|
|
|
#include "BKE_object.h"
|
2009-05-27 00:03:49 +00:00
|
|
|
#include "BKE_report.h"
|
2009-01-10 19:34:23 +00:00
|
|
|
#include "BKE_utildefines.h"
|
2009-09-24 21:22:24 +00:00
|
|
|
#include "BIK_api.h"
|
2009-01-10 19:34:23 +00:00
|
|
|
|
|
|
|
#ifndef DISABLE_PYTHON
|
|
|
|
#include "BPY_extern.h"
|
|
|
|
#endif
|
|
|
|
|
2009-05-27 00:03:49 +00:00
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
#include "RNA_define.h"
|
|
|
|
#include "RNA_enum_types.h"
|
2009-07-19 13:06:18 +00:00
|
|
|
#include "RNA_types.h"
|
2009-05-27 00:03:49 +00:00
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
#include "ED_object.h"
|
2009-05-27 00:03:49 +00:00
|
|
|
#include "ED_screen.h"
|
2009-01-10 19:34:23 +00:00
|
|
|
|
2009-07-19 13:06:18 +00:00
|
|
|
#include "UI_interface.h"
|
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
#include "object_intern.h"
|
|
|
|
|
|
|
|
/* -------------- Get Active Constraint Data ---------------------- */
|
|
|
|
|
|
|
|
/* if object in posemode, active bone constraints, else object constraints */
|
|
|
|
ListBase *get_active_constraints (Object *ob)
|
|
|
|
{
|
|
|
|
if (ob == NULL)
|
|
|
|
return NULL;
|
2010-02-03 11:36:09 +00:00
|
|
|
|
2009-08-16 03:24:23 +00:00
|
|
|
if (ob->mode & OB_MODE_POSE) {
|
2009-01-10 19:34:23 +00:00
|
|
|
bPoseChannel *pchan;
|
|
|
|
|
|
|
|
pchan = get_active_posechannel(ob);
|
|
|
|
if (pchan)
|
|
|
|
return &pchan->constraints;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return &ob->constraints;
|
2010-02-03 11:36:09 +00:00
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-02-03 11:36:09 +00:00
|
|
|
/* Find the list that a given constraint belongs to, and/or also get the posechannel this is from (if applicable) */
|
2009-11-26 09:48:53 +00:00
|
|
|
ListBase *get_constraint_lb (Object *ob, bConstraint *con, bPoseChannel **pchan_r)
|
|
|
|
{
|
2010-02-03 11:36:09 +00:00
|
|
|
if (pchan_r)
|
2009-11-26 09:48:53 +00:00
|
|
|
*pchan_r= NULL;
|
2010-02-03 11:36:09 +00:00
|
|
|
|
2009-11-26 09:48:53 +00:00
|
|
|
if (ELEM(NULL, ob, con))
|
|
|
|
return NULL;
|
2010-02-03 11:36:09 +00:00
|
|
|
|
|
|
|
/* try object constraints first */
|
|
|
|
if ((BLI_findindex(&ob->constraints, con) != -1)) {
|
2009-11-26 09:48:53 +00:00
|
|
|
return &ob->constraints;
|
|
|
|
}
|
2010-02-03 11:36:09 +00:00
|
|
|
|
|
|
|
/* if armature, try pose bones too */
|
|
|
|
if (ob->pose) {
|
2009-11-26 09:48:53 +00:00
|
|
|
bPoseChannel *pchan;
|
2010-02-03 11:36:09 +00:00
|
|
|
|
|
|
|
/* try each bone in order
|
|
|
|
* NOTE: it's not possible to directly look up the active bone yet, so this will have to do
|
|
|
|
*/
|
|
|
|
for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
|
|
|
if ((BLI_findindex(&pchan->constraints, con) != -1)) {
|
|
|
|
|
|
|
|
if (pchan_r)
|
2009-11-26 09:48:53 +00:00
|
|
|
*pchan_r= pchan;
|
2010-02-03 11:36:09 +00:00
|
|
|
|
2009-11-26 09:48:53 +00:00
|
|
|
return &pchan->constraints;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-02-03 11:36:09 +00:00
|
|
|
|
|
|
|
/* done */
|
2009-11-26 09:48:53 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
/* single constraint */
|
|
|
|
bConstraint *get_active_constraint (Object *ob)
|
|
|
|
{
|
2009-11-16 12:33:42 +00:00
|
|
|
return constraints_get_active(get_active_constraints(ob));
|
2009-01-10 19:34:23 +00:00
|
|
|
}
|
|
|
|
/* -------------- Constraint Management (Add New, Remove, Rename) -------------------- */
|
|
|
|
/* ------------- PyConstraints ------------------ */
|
|
|
|
|
|
|
|
/* this callback sets the text-file to be used for selected menu item */
|
|
|
|
void validate_pyconstraint_cb (void *arg1, void *arg2)
|
|
|
|
{
|
|
|
|
bPythonConstraint *data = arg1;
|
|
|
|
Text *text= NULL;
|
|
|
|
int index = *((int *)arg2);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* exception for no script */
|
|
|
|
if (index) {
|
|
|
|
/* innovative use of a for...loop to search */
|
|
|
|
for (text=G.main->text.first, i=1; text && index!=i; i++, text=text->id.next);
|
|
|
|
}
|
|
|
|
data->text = text;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef DISABLE_PYTHON
|
|
|
|
/* this returns a string for the list of usable pyconstraint script names */
|
|
|
|
char *buildmenu_pyconstraints (Text *con_text, int *pyconindex)
|
|
|
|
{
|
|
|
|
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 */
|
|
|
|
if (con_text == NULL)
|
|
|
|
*pyconindex= 0;
|
|
|
|
|
|
|
|
/* loop through markers, adding them */
|
|
|
|
for (text=G.main->text.first, i=1; text; i++, text=text->id.next) {
|
|
|
|
/* this is important to ensure that right script is shown as active */
|
|
|
|
if (text == con_text) *pyconindex = i;
|
|
|
|
|
|
|
|
/* only include valid pyconstraint scripts */
|
|
|
|
if (BPY_is_pyconstraint(text)) {
|
|
|
|
BLI_dynstr_append(pupds, text->id.name+2);
|
|
|
|
|
|
|
|
sprintf(buf, "%%x%d", i);
|
|
|
|
BLI_dynstr_append(pupds, buf);
|
|
|
|
|
|
|
|
if (text->id.next)
|
|
|
|
BLI_dynstr_append(pupds, "|");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* convert to normal MEM_malloc'd string */
|
|
|
|
str= BLI_dynstr_get_cstring(pupds);
|
|
|
|
BLI_dynstr_free(pupds);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
#endif /* DISABLE_PYTHON */
|
|
|
|
|
|
|
|
/* this callback gets called when the 'refresh' button of a pyconstraint gets pressed */
|
|
|
|
void update_pyconstraint_cb (void *arg1, void *arg2)
|
|
|
|
{
|
|
|
|
Object *owner= (Object *)arg1;
|
|
|
|
bConstraint *con= (bConstraint *)arg2;
|
|
|
|
#ifndef DISABLE_PYTHON
|
|
|
|
if (owner && con)
|
|
|
|
BPY_pyconstraint_update(owner, con);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* helper function for add_constriant - sets the last target for the active constraint */
|
|
|
|
static void set_constraint_nth_target (bConstraint *con, Object *target, char subtarget[], int index)
|
|
|
|
{
|
|
|
|
bConstraintTypeInfo *cti= constraint_get_typeinfo(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_countlist(&targets);
|
|
|
|
|
|
|
|
if (index < 0) {
|
|
|
|
if (abs(index) < num_targets)
|
|
|
|
index= num_targets - abs(index);
|
|
|
|
else
|
|
|
|
index= num_targets - 1;
|
|
|
|
}
|
|
|
|
else if (index >= num_targets) {
|
|
|
|
index= num_targets - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ct=targets.first, i=0; ct; ct= ct->next, i++) {
|
|
|
|
if (i == index) {
|
|
|
|
ct->tar= target;
|
|
|
|
strcpy(ct->subtarget, subtarget);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cti->flush_constraint_targets)
|
|
|
|
cti->flush_constraint_targets(con, &targets, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------- Constraint Sanity Testing ------------------- */
|
|
|
|
|
|
|
|
/* checks validity of object pointers, and NULLs,
|
|
|
|
* if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag
|
|
|
|
*/
|
|
|
|
static void test_constraints (Object *owner, const char substring[])
|
|
|
|
{
|
|
|
|
bConstraint *curcon;
|
|
|
|
ListBase *conlist= NULL;
|
|
|
|
int type;
|
|
|
|
|
|
|
|
if (owner==NULL) return;
|
|
|
|
|
|
|
|
/* Check parents */
|
|
|
|
if (strlen(substring)) {
|
|
|
|
switch (owner->type) {
|
|
|
|
case OB_ARMATURE:
|
|
|
|
type = CONSTRAINT_OBTYPE_BONE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
type = CONSTRAINT_OBTYPE_OBJECT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
type = CONSTRAINT_OBTYPE_OBJECT;
|
|
|
|
|
|
|
|
/* Get the constraint list for this object */
|
|
|
|
switch (type) {
|
|
|
|
case CONSTRAINT_OBTYPE_OBJECT:
|
|
|
|
conlist = &owner->constraints;
|
|
|
|
break;
|
|
|
|
case CONSTRAINT_OBTYPE_BONE:
|
|
|
|
{
|
|
|
|
Bone *bone;
|
|
|
|
bPoseChannel *chan;
|
|
|
|
|
|
|
|
bone = get_named_bone( ((bArmature *)owner->data), substring );
|
|
|
|
chan = get_pose_channel(owner->pose, substring);
|
|
|
|
if (bone && chan) {
|
|
|
|
conlist = &chan->constraints;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check all constraints - is constraint valid? */
|
|
|
|
if (conlist) {
|
|
|
|
for (curcon = conlist->first; curcon; curcon=curcon->next) {
|
|
|
|
bConstraintTypeInfo *cti= constraint_get_typeinfo(curcon);
|
|
|
|
ListBase targets = {NULL, NULL};
|
|
|
|
bConstraintTarget *ct;
|
|
|
|
|
|
|
|
/* clear disabled-flag first */
|
|
|
|
curcon->flag &= ~CONSTRAINT_DISABLE;
|
2009-11-01 11:29:40 +00:00
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
if (curcon->type == CONSTRAINT_TYPE_KINEMATIC) {
|
|
|
|
bKinematicConstraint *data = curcon->data;
|
|
|
|
|
|
|
|
/* bad: we need a separate set of checks here as poletarget is
|
|
|
|
* optional... otherwise poletarget must exist too or else
|
|
|
|
* the constraint is deemed invalid
|
|
|
|
*/
|
2009-09-24 21:22:24 +00:00
|
|
|
/* default IK check ... */
|
2009-01-10 19:34:23 +00:00
|
|
|
if (exist_object(data->tar) == 0) {
|
|
|
|
data->tar = NULL;
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
else if (data->tar == owner) {
|
|
|
|
if (!get_named_bone(get_armature(owner), data->subtarget)) {
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->poletar) {
|
|
|
|
if (exist_object(data->poletar) == 0) {
|
|
|
|
data->poletar = NULL;
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
else if (data->poletar == owner) {
|
|
|
|
if (!get_named_bone(get_armature(owner), data->polesubtarget)) {
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-09-24 21:22:24 +00:00
|
|
|
/* ... can be overwritten here */
|
|
|
|
BIK_test_constraint(owner, curcon);
|
2009-01-10 19:34:23 +00:00
|
|
|
/* targets have already been checked for this */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (curcon->type == CONSTRAINT_TYPE_ACTION) {
|
|
|
|
bActionConstraint *data = curcon->data;
|
|
|
|
|
|
|
|
/* validate action */
|
|
|
|
if (data->act == NULL)
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
else if (curcon->type == CONSTRAINT_TYPE_FOLLOWPATH) {
|
|
|
|
bFollowPathConstraint *data = curcon->data;
|
|
|
|
|
|
|
|
/* don't allow track/up axes to be the same */
|
|
|
|
if (data->upflag==data->trackflag)
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
if (data->upflag+3==data->trackflag)
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
else if (curcon->type == CONSTRAINT_TYPE_TRACKTO) {
|
|
|
|
bTrackToConstraint *data = curcon->data;
|
|
|
|
|
|
|
|
/* don't allow track/up axes to be the same */
|
|
|
|
if (data->reserved2==data->reserved1)
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
if (data->reserved2+3==data->reserved1)
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
else if (curcon->type == CONSTRAINT_TYPE_LOCKTRACK) {
|
|
|
|
bLockTrackConstraint *data = curcon->data;
|
|
|
|
|
|
|
|
if (data->lockflag==data->trackflag)
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
if (data->lockflag+3==data->trackflag)
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
2009-11-01 11:29:40 +00:00
|
|
|
else if (curcon->type == CONSTRAINT_TYPE_SPLINEIK) {
|
|
|
|
bSplineIKConstraint *data = curcon->data;
|
|
|
|
|
|
|
|
/* if the number of points does not match the amount required by the chain length,
|
|
|
|
* free the points array and request a rebind...
|
|
|
|
*/
|
2009-11-12 12:20:57 +00:00
|
|
|
if ((data->points == NULL) || (data->numpoints != data->chainlen+1))
|
2009-11-01 11:29:40 +00:00
|
|
|
{
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
2009-01-10 19:34:23 +00:00
|
|
|
|
|
|
|
/* Check targets for constraints */
|
|
|
|
if (cti && cti->get_constraint_targets) {
|
|
|
|
cti->get_constraint_targets(curcon, &targets);
|
|
|
|
|
|
|
|
/* disable and clear constraints targets that are incorrect */
|
|
|
|
for (ct= targets.first; ct; ct= ct->next) {
|
|
|
|
/* general validity checks (for those constraints that need this) */
|
|
|
|
if (exist_object(ct->tar) == 0) {
|
|
|
|
ct->tar = NULL;
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
else if (ct->tar == owner) {
|
|
|
|
if (!get_named_bone(get_armature(owner), ct->subtarget)) {
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* target checks for specific constraints */
|
2009-11-01 11:29:40 +00:00
|
|
|
if (ELEM3(curcon->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) {
|
2009-01-10 19:34:23 +00:00
|
|
|
if (ct->tar) {
|
|
|
|
if (ct->tar->type != OB_CURVE) {
|
|
|
|
ct->tar= NULL;
|
|
|
|
curcon->flag |= CONSTRAINT_DISABLE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Curve *cu= ct->tar->data;
|
|
|
|
|
|
|
|
/* auto-set 'Path' setting on curve so this works */
|
|
|
|
cu->flag |= CU_PATH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free any temporary targets */
|
|
|
|
if (cti->flush_constraint_targets)
|
|
|
|
cti->flush_constraint_targets(curcon, &targets, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_bonelist_constraints (Object *owner, ListBase *list)
|
|
|
|
{
|
|
|
|
Bone *bone;
|
|
|
|
|
|
|
|
for (bone = list->first; bone; bone = bone->next) {
|
|
|
|
test_constraints(owner, bone->name);
|
|
|
|
test_bonelist_constraints(owner, &bone->childbase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_test_constraints (Object *owner)
|
|
|
|
{
|
|
|
|
test_constraints(owner, "");
|
|
|
|
|
|
|
|
if (owner->type==OB_ARMATURE) {
|
|
|
|
bArmature *arm= get_armature(owner);
|
|
|
|
|
|
|
|
if (arm)
|
|
|
|
test_bonelist_constraints(owner, &arm->bonebase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ********************** CONSTRAINT-SPECIFIC STUFF ********************* */
|
|
|
|
|
2009-07-26 11:57:27 +00:00
|
|
|
/* ---------- Distance-Dependent Constraints ---------- */
|
|
|
|
/* StretchTo, Limit Distance */
|
|
|
|
|
2009-09-04 08:49:11 +00:00
|
|
|
static int stretchto_poll(bContext *C)
|
|
|
|
{
|
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_StretchToConstraint);
|
|
|
|
return (ptr.id.data && ptr.data);
|
|
|
|
}
|
|
|
|
|
2009-07-26 11:57:27 +00:00
|
|
|
static int stretchto_reset_exec (bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_StretchToConstraint);
|
|
|
|
|
|
|
|
/* just set original length to 0.0, which will cause a reset on next recalc */
|
|
|
|
RNA_float_set(&ptr, "original_length", 0.0f);
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
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.";
|
|
|
|
|
|
|
|
ot->exec= stretchto_reset_exec;
|
2009-09-04 08:49:11 +00:00
|
|
|
ot->poll= stretchto_poll;
|
2009-07-26 11:57:27 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int limitdistance_reset_exec (bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_LimitDistanceConstraint);
|
|
|
|
|
|
|
|
/* just set distance to 0.0, which will cause a reset on next recalc */
|
|
|
|
RNA_float_set(&ptr, "distance", 0.0f);
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
2009-09-04 08:49:11 +00:00
|
|
|
static int limitdistance_poll(bContext *C)
|
|
|
|
{
|
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_LimitDistanceConstraint);
|
|
|
|
return (ptr.id.data && ptr.data);
|
|
|
|
}
|
|
|
|
|
2009-07-26 11:57: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.";
|
|
|
|
|
|
|
|
ot->exec= limitdistance_reset_exec;
|
2009-09-04 08:49:11 +00:00
|
|
|
ot->poll= limitdistance_poll;
|
2009-07-26 11:57:27 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
}
|
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
/* ------------- Child-Of Constraint ------------------ */
|
|
|
|
|
2009-09-04 08:49:11 +00:00
|
|
|
static int childof_poll(bContext *C)
|
|
|
|
{
|
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_ChildOfConstraint);
|
|
|
|
return (ptr.id.data && ptr.data);
|
|
|
|
}
|
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
/* ChildOf Constraint - set inverse callback */
|
2009-07-11 11:52:20 +00:00
|
|
|
static int childof_set_inverse_exec (bContext *C, wmOperator *op)
|
2009-01-10 19:34:23 +00:00
|
|
|
{
|
2009-07-11 11:52:20 +00:00
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_ChildOfConstraint);
|
|
|
|
Scene *scene= CTX_data_scene(C);
|
|
|
|
Object *ob= ptr.id.data;
|
|
|
|
bConstraint *con= ptr.data;
|
2009-01-10 19:34:23 +00:00
|
|
|
bChildOfConstraint *data= (bChildOfConstraint *)con->data;
|
|
|
|
bPoseChannel *pchan= NULL;
|
|
|
|
|
|
|
|
/* try to find a pose channel */
|
2009-07-11 11:52:20 +00:00
|
|
|
// TODO: get from context instead?
|
2009-01-10 19:34:23 +00:00
|
|
|
if (ob && ob->pose)
|
|
|
|
pchan= get_active_posechannel(ob);
|
|
|
|
|
|
|
|
/* calculate/set inverse matrix */
|
|
|
|
if (pchan) {
|
|
|
|
float pmat[4][4], cinf;
|
|
|
|
float imat[4][4], tmat[4][4];
|
|
|
|
|
|
|
|
/* make copy of pchan's original pose-mat (for use later) */
|
2009-11-10 20:43:45 +00:00
|
|
|
copy_m4_m4(pmat, pchan->pose_mat);
|
2009-01-10 19:34:23 +00:00
|
|
|
|
|
|
|
/* disable constraint for pose to be solved without it */
|
|
|
|
cinf= con->enforce;
|
|
|
|
con->enforce= 0.0f;
|
|
|
|
|
|
|
|
/* solve pose without constraint */
|
|
|
|
where_is_pose(scene, ob);
|
|
|
|
|
|
|
|
/* determine effect of constraint by removing the newly calculated
|
|
|
|
* pchan->pose_mat from the original pchan->pose_mat, thus determining
|
|
|
|
* the effect of the constraint
|
|
|
|
*/
|
2009-11-10 20:43:45 +00:00
|
|
|
invert_m4_m4(imat, pchan->pose_mat);
|
|
|
|
mul_m4_m4m4(tmat, imat, pmat);
|
|
|
|
invert_m4_m4(data->invmat, tmat);
|
2009-01-10 19:34:23 +00:00
|
|
|
|
|
|
|
/* recalculate pose with new inv-mat */
|
|
|
|
con->enforce= cinf;
|
|
|
|
where_is_pose(scene, ob);
|
|
|
|
}
|
|
|
|
else if (ob) {
|
|
|
|
Object workob;
|
|
|
|
/* use what_does_parent to find inverse - just like for normal parenting.
|
|
|
|
* NOTE: what_does_parent uses a static workob defined in object.c
|
|
|
|
*/
|
|
|
|
what_does_parent(scene, ob, &workob);
|
2009-11-10 20:43:45 +00:00
|
|
|
invert_m4_m4(data->invmat, workob.obmat);
|
2009-01-10 19:34:23 +00:00
|
|
|
}
|
|
|
|
else
|
2009-11-10 20:43:45 +00:00
|
|
|
unit_m4(data->invmat);
|
2009-07-11 11:52:20 +00:00
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, ob);
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
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.";
|
|
|
|
|
|
|
|
ot->exec= childof_set_inverse_exec;
|
2009-09-04 08:49:11 +00:00
|
|
|
ot->poll= childof_poll;
|
2009-07-11 11:52:20 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-01-10 19:34:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ChildOf Constraint - clear inverse callback */
|
2009-07-11 11:52:20 +00:00
|
|
|
static int childof_clear_inverse_exec (bContext *C, wmOperator *op)
|
2009-01-10 19:34:23 +00:00
|
|
|
{
|
2009-07-11 11:52:20 +00:00
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_ChildOfConstraint);
|
|
|
|
Object *ob= ptr.id.data;
|
|
|
|
bConstraint *con= ptr.data;
|
2009-01-10 19:34:23 +00:00
|
|
|
bChildOfConstraint *data= (bChildOfConstraint *)con->data;
|
|
|
|
|
|
|
|
/* simply clear the matrix */
|
2009-11-10 20:43:45 +00:00
|
|
|
unit_m4(data->invmat);
|
2009-07-11 11:52:20 +00:00
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, ob);
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
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.";
|
|
|
|
|
|
|
|
ot->exec= childof_clear_inverse_exec;
|
2009-09-04 08:49:11 +00:00
|
|
|
ot->poll= childof_poll;
|
2009-07-11 11:52:20 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-01-10 19:34:23 +00:00
|
|
|
}
|
2009-05-27 00:03:49 +00:00
|
|
|
|
|
|
|
/***************************** BUTTONS ****************************/
|
|
|
|
|
|
|
|
void ED_object_constraint_set_active(Object *ob, bConstraint *con)
|
2009-11-16 11:11:16 +00:00
|
|
|
{
|
2010-02-03 11:43:26 +00:00
|
|
|
ListBase *lb = get_constraint_lb(ob, con, NULL);
|
|
|
|
|
2009-05-27 00:03:49 +00:00
|
|
|
/* lets be nice and escape if its active already */
|
2009-11-16 12:33:42 +00:00
|
|
|
// NOTE: this assumes that the stack doesn't have other active ones set...
|
2010-02-03 11:43:26 +00:00
|
|
|
if ((lb && con) && (con->flag & CONSTRAINT_ACTIVE))
|
|
|
|
return;
|
2009-05-27 00:03:49 +00:00
|
|
|
|
2010-02-03 11:43:26 +00:00
|
|
|
constraints_set_active(lb, con);
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
|
|
|
|
2009-09-24 21:22:24 +00:00
|
|
|
void ED_object_constraint_update(Object *ob)
|
|
|
|
{
|
|
|
|
|
|
|
|
if(ob->pose) update_pose_constraint_flags(ob->pose);
|
|
|
|
|
|
|
|
object_test_constraints(ob);
|
|
|
|
|
|
|
|
if(ob->type==OB_ARMATURE) DAG_id_flush_update(&ob->id, OB_RECALC_DATA|OB_RECALC_OB);
|
|
|
|
else DAG_id_flush_update(&ob->id, OB_RECALC_OB);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ED_object_constraint_dependency_update(Scene *scene, Object *ob)
|
|
|
|
{
|
|
|
|
ED_object_constraint_update(ob);
|
|
|
|
|
|
|
|
if(ob->pose) ob->pose->flag |= POSE_RECALC; // checks & sorts pose channels
|
|
|
|
DAG_scene_sort(scene);
|
|
|
|
}
|
|
|
|
|
2009-09-04 08:49:11 +00:00
|
|
|
static int constraint_poll(bContext *C)
|
|
|
|
{
|
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
|
|
|
|
return (ptr.id.data && ptr.data);
|
|
|
|
}
|
|
|
|
|
2009-07-11 12:54:17 +00:00
|
|
|
static int constraint_delete_exec (bContext *C, wmOperator *op)
|
2009-05-27 00:03:49 +00:00
|
|
|
{
|
2009-07-11 12:54:17 +00:00
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
|
|
|
|
Object *ob= ptr.id.data;
|
|
|
|
bConstraint *con= ptr.data;
|
2010-02-03 11:36:09 +00:00
|
|
|
ListBase *lb = get_constraint_lb(ob, con, NULL);
|
2009-05-27 00:03:49 +00:00
|
|
|
|
2010-02-03 11:36:09 +00:00
|
|
|
/* free the constraint */
|
|
|
|
if (remove_constraint(lb, con)) {
|
2010-02-03 11:43:26 +00:00
|
|
|
/* there's no active constraint now, so make sure this is the case */
|
|
|
|
constraints_set_active(lb, NULL);
|
2010-02-03 11:36:09 +00:00
|
|
|
|
|
|
|
/* notifiers */
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, ob);
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* couldn't remove due to some invalid data */
|
2009-09-24 21:22:24 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2010-02-03 11:36:09 +00:00
|
|
|
}
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-11 12:54:17 +00:00
|
|
|
void CONSTRAINT_OT_delete (wmOperatorType *ot)
|
2009-05-27 00:03:49 +00:00
|
|
|
{
|
2009-07-11 12:54:17 +00:00
|
|
|
/* identifiers */
|
|
|
|
ot->name= "Delete Constraint";
|
|
|
|
ot->idname= "CONSTRAINT_OT_delete";
|
|
|
|
ot->description= "Remove constraitn from constraint stack.";
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
ot->exec= constraint_delete_exec;
|
2009-09-04 08:49:11 +00:00
|
|
|
ot->poll= constraint_poll;
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int constraint_move_down_exec (bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
|
|
|
|
Object *ob= ptr.id.data;
|
|
|
|
bConstraint *con= ptr.data;
|
|
|
|
|
|
|
|
if (con->next) {
|
2010-02-03 11:36:09 +00:00
|
|
|
ListBase *conlist= get_constraint_lb(ob, con, NULL);
|
2009-07-11 12:54:17 +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);
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, ob);
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2009-05-27 00:03:49 +00:00
|
|
|
|
2009-07-11 12:54:17 +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 constraint stack.";
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
ot->exec= constraint_move_down_exec;
|
2009-09-04 08:49:11 +00:00
|
|
|
ot->poll= constraint_poll;
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
static int constraint_move_up_exec (bContext *C, wmOperator *op)
|
2009-05-27 00:03:49 +00:00
|
|
|
{
|
2009-07-11 12:54:17 +00:00
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
|
|
|
|
Object *ob= ptr.id.data;
|
|
|
|
bConstraint *con= ptr.data;
|
|
|
|
|
|
|
|
if (con->prev) {
|
2010-02-03 11:36:09 +00:00
|
|
|
ListBase *conlist= get_constraint_lb(ob, con, NULL);
|
2009-07-11 12:54:17 +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);
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, ob);
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2009-05-27 00:03:49 +00:00
|
|
|
|
2009-07-11 12:54:17 +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 constraint stack.";
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
ot->exec= constraint_move_up_exec;
|
2009-09-04 08:49:11 +00:00
|
|
|
ot->poll= constraint_poll;
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***************************** OPERATORS ****************************/
|
|
|
|
|
2009-07-19 07:20:21 +00:00
|
|
|
/************************ remove constraint operators *********************/
|
|
|
|
|
|
|
|
static int pose_constraints_clear_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
2009-09-20 11:21:44 +00:00
|
|
|
Scene *scene= CTX_data_scene(C);
|
2009-07-19 07:20:21 +00:00
|
|
|
|
|
|
|
/* free constraints for all selected bones */
|
2009-11-25 15:00:29 +00:00
|
|
|
CTX_DATA_BEGIN(C, bPoseChannel*, pchan, selected_pose_bones)
|
2009-07-19 07:20:21 +00:00
|
|
|
{
|
|
|
|
free_constraints(&pchan->constraints);
|
2009-11-01 11:29:40 +00:00
|
|
|
pchan->constflag &= ~(PCHAN_HAS_IK|PCHAN_HAS_SPLINEIK|PCHAN_HAS_CONST);
|
2009-07-19 07:20:21 +00:00
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
2009-09-20 11:21:44 +00:00
|
|
|
/* force depsgraph to get recalculated since relationships removed */
|
|
|
|
DAG_scene_sort(scene); /* sort order of objects */
|
|
|
|
|
2009-07-19 07:20:21 +00:00
|
|
|
/* do updates */
|
2009-09-20 11:21:44 +00:00
|
|
|
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, ob);
|
2009-07-19 07:20:21 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void POSE_OT_constraints_clear(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
2009-07-19 14:57:20 +00:00
|
|
|
ot->name = "Clear Constraints";
|
2009-07-19 07:20:21 +00:00
|
|
|
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; // XXX - do we want to ensure there are selected bones too?
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int object_constraints_clear_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
2009-09-20 11:21:44 +00:00
|
|
|
Scene *scene= CTX_data_scene(C);
|
2009-07-19 07:20:21 +00:00
|
|
|
|
|
|
|
/* do freeing */
|
|
|
|
// TODO: we should free constraints for all selected objects instead (to be more consistent with bones)
|
|
|
|
free_constraints(&ob->constraints);
|
|
|
|
|
2009-09-20 11:21:44 +00:00
|
|
|
/* force depsgraph to get recalculated since relationships removed */
|
|
|
|
DAG_scene_sort(scene); /* sort order of objects */
|
|
|
|
|
2009-07-19 07:20:21 +00:00
|
|
|
/* do updates */
|
2.5
Notifiers
---------
Various fixes for wrong use of notifiers, and some new notifiers
to make things a bit more clear and consistent, with two notable
changes:
* Geometry changes are now done with NC_GEOM, rather than
NC_OBJECT|ND_GEOM_, so an object does need to be available.
* Space data now use NC_SPACE|ND_SPACE_*, instead of data
notifiers or even NC_WINDOW in some cases. Note that NC_SPACE
should only be used for notifying about changes in space data,
we don't want to go back to allqueue(REDRAW..).
Depsgraph
---------
The dependency graph now has a different flush call:
DAG_object_flush_update(scene, ob, flag)
is replaced by:
DAG_id_flush_update(id, flag)
It still works basically the same, one difference is that it now
also accepts object data (e.g. Mesh), again to avoid requiring an
Object to be available. Other ID types will simply do nothing at
the moment.
Docs
----
I made some guidelines for how/when to do which kinds of updates
and notifiers. I can't specify totally exact how to make these
decisions, but these are basically the guidelines I use. So, new
and updated docs are here:
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/NotifiersUpdates
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/DataNotifiers
2009-09-04 20:51:09 +00:00
|
|
|
DAG_id_flush_update(&ob->id, OB_RECALC_OB);
|
2009-09-20 11:21:44 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, ob);
|
2009-07-19 07:20:21 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OBJECT_OT_constraints_clear(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
|
|
|
ot->name = "Clear 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;
|
2009-12-04 06:33:01 +00:00
|
|
|
ot->poll= ED_operator_object_active_editable;
|
2009-07-19 07:20:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************ add constraint operators *********************/
|
2009-05-27 00:03:49 +00:00
|
|
|
|
2009-07-19 13:06:18 +00:00
|
|
|
/* get the Object and/or PoseChannel to use as target */
|
|
|
|
static short get_new_constraint_target(bContext *C, int con_type, Object **tar_ob, bPoseChannel **tar_pchan, short add)
|
|
|
|
{
|
|
|
|
Object *obact= CTX_data_active_object(C);
|
2009-07-21 04:21:07 +00:00
|
|
|
bPoseChannel *pchanact= get_active_posechannel(obact);
|
2009-07-19 13:06:18 +00:00
|
|
|
short only_curve= 0, only_mesh= 0, only_ob= 0;
|
|
|
|
short found= 0;
|
|
|
|
|
|
|
|
/* clear tar_ob and tar_pchan fields before use
|
|
|
|
* - assume for now that both always exist...
|
|
|
|
*/
|
|
|
|
*tar_ob= NULL;
|
|
|
|
*tar_pchan= NULL;
|
|
|
|
|
|
|
|
/* check if constraint type doesn't requires a target
|
|
|
|
* - if so, no need to get any targets
|
|
|
|
*/
|
|
|
|
switch (con_type) {
|
|
|
|
/* no-target constraints --------------------------- */
|
|
|
|
/* null constraint - shouldn't even be added! */
|
|
|
|
case CONSTRAINT_TYPE_NULL:
|
|
|
|
/* limit constraints - no targets needed */
|
|
|
|
case CONSTRAINT_TYPE_LOCLIMIT:
|
|
|
|
case CONSTRAINT_TYPE_ROTLIMIT:
|
|
|
|
case CONSTRAINT_TYPE_SIZELIMIT:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* restricted target-type constraints -------------- */
|
2009-07-21 04:21:07 +00:00
|
|
|
/* NOTE: for these, we cannot try to add a target object if no valid ones are found, since that doesn't work */
|
2009-07-19 13:06:18 +00:00
|
|
|
/* curve-based constraints - set the only_curve and only_ob flags */
|
|
|
|
case CONSTRAINT_TYPE_CLAMPTO:
|
|
|
|
case CONSTRAINT_TYPE_FOLLOWPATH:
|
2009-11-01 11:29:40 +00:00
|
|
|
case CONSTRAINT_TYPE_SPLINEIK:
|
2009-07-19 13:06:18 +00:00
|
|
|
only_curve= 1;
|
|
|
|
only_ob= 1;
|
2009-07-21 04:21:07 +00:00
|
|
|
add= 0;
|
2009-07-19 13:06:18 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* mesh only? */
|
|
|
|
case CONSTRAINT_TYPE_SHRINKWRAP:
|
|
|
|
only_mesh= 1;
|
|
|
|
only_ob= 1;
|
2009-07-21 04:21:07 +00:00
|
|
|
add= 0;
|
2009-07-19 13:06:18 +00:00
|
|
|
break;
|
|
|
|
|
2009-07-21 04:21:07 +00:00
|
|
|
/* object only - add here is ok? */
|
2009-07-19 13:06:18 +00:00
|
|
|
case CONSTRAINT_TYPE_RIGIDBODYJOINT:
|
|
|
|
only_ob= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if the active Object is Armature, and we can search for bones, do so... */
|
|
|
|
if ((obact->type == OB_ARMATURE) && (only_ob == 0)) {
|
|
|
|
/* search in list of selected Pose-Channels for target */
|
2009-11-25 15:00:29 +00:00
|
|
|
CTX_DATA_BEGIN(C, bPoseChannel*, pchan, selected_pose_bones)
|
2009-07-19 13:06:18 +00:00
|
|
|
{
|
2009-07-21 04:21:07 +00:00
|
|
|
/* just use the first one that we encounter, as long as it is not the active one */
|
|
|
|
if (pchan != pchanact) {
|
|
|
|
*tar_ob= obact;
|
|
|
|
*tar_pchan= pchan;
|
|
|
|
found= 1;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2009-07-19 13:06:18 +00:00
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if not yet found, try selected Objects... */
|
|
|
|
if (found == 0) {
|
|
|
|
/* 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) &&
|
|
|
|
((!only_curve) || (ob->type == OB_CURVE)) &&
|
|
|
|
((!only_mesh) || (ob->type == OB_MESH)) )
|
|
|
|
{
|
|
|
|
/* set target */
|
|
|
|
*tar_ob= ob;
|
|
|
|
found= 1;
|
|
|
|
|
|
|
|
/* 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) */
|
|
|
|
if ((found == 0) && (add)) {
|
2009-07-21 04:21:07 +00:00
|
|
|
Scene *scene= CTX_data_scene(C);
|
|
|
|
Base *base= BASACT, *newbase=NULL;
|
2009-07-19 13:06:18 +00:00
|
|
|
Object *obt;
|
|
|
|
|
2009-07-21 04:21:07 +00:00
|
|
|
/* add new target object */
|
2009-07-19 13:06:18 +00:00
|
|
|
obt= add_object(scene, OB_EMPTY);
|
2009-07-21 04:21:07 +00:00
|
|
|
|
2009-07-19 13:06:18 +00:00
|
|
|
/* set layers OK */
|
|
|
|
newbase= BASACT;
|
|
|
|
newbase->lay= base->lay;
|
|
|
|
obt->lay= newbase->lay;
|
|
|
|
|
|
|
|
/* transform cent to global coords for loc */
|
|
|
|
if (pchanact) {
|
2009-07-21 04:21:07 +00:00
|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
if (con_type == CONSTRAINT_TYPE_KINEMATIC)
|
2009-11-10 20:43:45 +00:00
|
|
|
mul_v3_m4v3(obt->loc, obact->obmat, pchanact->pose_tail);
|
2009-07-19 13:06:18 +00:00
|
|
|
else
|
2009-11-10 20:43:45 +00:00
|
|
|
mul_v3_m4v3(obt->loc, obact->obmat, pchanact->pose_head);
|
2009-07-19 13:06:18 +00:00
|
|
|
}
|
|
|
|
else
|
2009-07-21 04:21:07 +00:00
|
|
|
VECCOPY(obt->loc, obact->obmat[3]);
|
2009-07-19 13:06:18 +00:00
|
|
|
|
|
|
|
/* restore, add_object sets active */
|
|
|
|
BASACT= base;
|
|
|
|
base->flag |= SELECT;
|
2009-07-21 04:21:07 +00:00
|
|
|
|
|
|
|
/* make our new target the new object */
|
|
|
|
*tar_ob= obt;
|
|
|
|
found= 1;
|
2009-07-19 13:06:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* return whether there's any target */
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* used by add constraint operators to add the constraint required */
|
2009-07-20 12:42:31 +00:00
|
|
|
static int constraint_add_exec(bContext *C, wmOperator *op, Object *ob, ListBase *list, int type, short setTarget)
|
2009-05-27 00:03:49 +00:00
|
|
|
{
|
|
|
|
Scene *scene= CTX_data_scene(C);
|
2009-11-11 19:58:30 +00:00
|
|
|
bPoseChannel *pchan;
|
2009-07-19 13:06:18 +00:00
|
|
|
bConstraint *con;
|
2009-07-20 12:42:31 +00:00
|
|
|
|
2009-11-11 19:58:30 +00:00
|
|
|
if(list == &ob->constraints)
|
|
|
|
pchan= NULL;
|
|
|
|
else
|
|
|
|
pchan= get_active_posechannel(ob);
|
|
|
|
|
2009-07-20 12:42:31 +00:00
|
|
|
/* check if constraint to be added is valid for the given constraints stack */
|
|
|
|
if (type == CONSTRAINT_TYPE_NULL) {
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
if ( (type == CONSTRAINT_TYPE_RIGIDBODYJOINT) && (list != &ob->constraints) ) {
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "Rigid Body Joint Constraint can only be added to Objects.");
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
if ( (type == CONSTRAINT_TYPE_KINEMATIC) && ((!pchan) || (list != &pchan->constraints)) ) {
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "IK Constraint can only be added to Bones.");
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2009-11-01 11:29:40 +00:00
|
|
|
if ( (type == CONSTRAINT_TYPE_SPLINEIK) && ((!pchan) || (list != &pchan->constraints)) ) {
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "Spline IK Constraint can only be added to Bones.");
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2009-07-19 13:06:18 +00:00
|
|
|
|
|
|
|
/* create a new constraint of the type requried, and add it to the active/given constraints list */
|
2009-11-11 19:58:30 +00:00
|
|
|
if(pchan)
|
|
|
|
con = add_pose_constraint(ob, pchan, NULL, type);
|
|
|
|
else
|
|
|
|
con = add_ob_constraint(ob, NULL, type);
|
2009-05-27 00:03:49 +00:00
|
|
|
|
2009-07-19 13:06:18 +00:00
|
|
|
/* get the first selected object/bone, and make that the target
|
|
|
|
* - apart from the buttons-window add buttons, we shouldn't add in this way
|
|
|
|
*/
|
|
|
|
if (setTarget) {
|
|
|
|
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-targetted constraints)
|
|
|
|
*/
|
|
|
|
if (tar_pchan)
|
|
|
|
set_constraint_nth_target(con, tar_ob, tar_pchan->name, 0);
|
|
|
|
else
|
|
|
|
set_constraint_nth_target(con, tar_ob, "", 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do type-specific tweaking to the constraint settings */
|
2009-11-01 11:29:40 +00:00
|
|
|
// TODO: does action constraint need anything here - i.e. spaceonce?
|
2009-07-19 13:06:18 +00:00
|
|
|
switch (type) {
|
2009-05-27 00:03:49 +00:00
|
|
|
case CONSTRAINT_TYPE_CHILDOF:
|
2009-07-19 13:06:18 +00:00
|
|
|
{
|
|
|
|
/* if this constraint is being added to a posechannel, make sure
|
|
|
|
* the constraint gets evaluated in pose-space */
|
2009-08-16 03:24:23 +00:00
|
|
|
if (ob->mode & OB_MODE_POSE) {
|
2009-07-19 13:06:18 +00:00
|
|
|
con->ownspace = CONSTRAINT_SPACE_POSE;
|
|
|
|
con->flag |= CONSTRAINT_SPACEONCE;
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
2009-07-19 13:06:18 +00:00
|
|
|
}
|
2009-05-27 00:03:49 +00:00
|
|
|
break;
|
2009-07-19 13:06:18 +00:00
|
|
|
|
|
|
|
case CONSTRAINT_TYPE_PYTHON: // FIXME: this code is not really valid anymore
|
|
|
|
{
|
|
|
|
char *menustr;
|
|
|
|
int scriptint= 0;
|
|
|
|
#ifndef DISABLE_PYTHON
|
|
|
|
/* popup a list of usable scripts */
|
|
|
|
menustr = buildmenu_pyconstraints(NULL, &scriptint);
|
2.5: Object module
* Split object_edit.c into multiple files:
object_add.c, object_edit.c, object_hook.c, object_relations.c,
object_select.c, object_transform.c.
* Rename files to have consistent object_ and mball_ prefix:
object_shapekey.c, object_lattice.c, object_vgroup.c, mball_edit.c.
* Added operators:
* vertex group menu and set active
* apply location, rotation, scale, visual transform (location is new)
* make local
* make vertex parent
* move to layer
* convert to curve/mesh (not finished yet)
* Many small fixes for marked issues, but still much code to be cleaned
up here...
2009-09-09 11:52:56 +00:00
|
|
|
// XXX scriptint = pupmenu(menustr);
|
2009-07-19 13:06:18 +00:00
|
|
|
MEM_freeN(menustr);
|
|
|
|
|
|
|
|
/* only add constraint if a script was chosen */
|
|
|
|
if (scriptint) {
|
|
|
|
/* add constraint */
|
|
|
|
validate_pyconstraint_cb(con->data, &scriptint);
|
2009-07-11 12:54:17 +00:00
|
|
|
|
2009-07-19 13:06:18 +00:00
|
|
|
/* make sure target allowance is set correctly */
|
|
|
|
BPY_pyconstraint_update(ob, con);
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
2009-07-19 13:06:18 +00:00
|
|
|
#endif
|
|
|
|
}
|
2009-05-27 00:03:49 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-07-19 13:06:18 +00:00
|
|
|
|
|
|
|
/* make sure all settings are valid - similar to above checks, but sometimes can be wrong */
|
2009-05-27 00:03:49 +00:00
|
|
|
object_test_constraints(ob);
|
|
|
|
|
2009-07-19 13:06:18 +00:00
|
|
|
if (ob->pose)
|
2009-05-27 00:03:49 +00:00
|
|
|
update_pose_constraint_flags(ob->pose);
|
|
|
|
|
2009-07-19 13:06:18 +00:00
|
|
|
|
|
|
|
/* force depsgraph to get recalculated since new relationships added */
|
|
|
|
DAG_scene_sort(scene); /* sort order of objects */
|
|
|
|
|
|
|
|
if ((ob->type==OB_ARMATURE) && (pchan)) {
|
|
|
|
ob->pose->flag |= POSE_RECALC; /* sort pose channels */
|
2.5
Notifiers
---------
Various fixes for wrong use of notifiers, and some new notifiers
to make things a bit more clear and consistent, with two notable
changes:
* Geometry changes are now done with NC_GEOM, rather than
NC_OBJECT|ND_GEOM_, so an object does need to be available.
* Space data now use NC_SPACE|ND_SPACE_*, instead of data
notifiers or even NC_WINDOW in some cases. Note that NC_SPACE
should only be used for notifying about changes in space data,
we don't want to go back to allqueue(REDRAW..).
Depsgraph
---------
The dependency graph now has a different flush call:
DAG_object_flush_update(scene, ob, flag)
is replaced by:
DAG_id_flush_update(id, flag)
It still works basically the same, one difference is that it now
also accepts object data (e.g. Mesh), again to avoid requiring an
Object to be available. Other ID types will simply do nothing at
the moment.
Docs
----
I made some guidelines for how/when to do which kinds of updates
and notifiers. I can't specify totally exact how to make these
decisions, but these are basically the guidelines I use. So, new
and updated docs are here:
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/NotifiersUpdates
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/DataNotifiers
2009-09-04 20:51:09 +00:00
|
|
|
DAG_id_flush_update(&ob->id, OB_RECALC_DATA|OB_RECALC_OB);
|
2009-07-19 13:06:18 +00:00
|
|
|
}
|
2009-05-27 00:03:49 +00:00
|
|
|
else
|
2.5
Notifiers
---------
Various fixes for wrong use of notifiers, and some new notifiers
to make things a bit more clear and consistent, with two notable
changes:
* Geometry changes are now done with NC_GEOM, rather than
NC_OBJECT|ND_GEOM_, so an object does need to be available.
* Space data now use NC_SPACE|ND_SPACE_*, instead of data
notifiers or even NC_WINDOW in some cases. Note that NC_SPACE
should only be used for notifying about changes in space data,
we don't want to go back to allqueue(REDRAW..).
Depsgraph
---------
The dependency graph now has a different flush call:
DAG_object_flush_update(scene, ob, flag)
is replaced by:
DAG_id_flush_update(id, flag)
It still works basically the same, one difference is that it now
also accepts object data (e.g. Mesh), again to avoid requiring an
Object to be available. Other ID types will simply do nothing at
the moment.
Docs
----
I made some guidelines for how/when to do which kinds of updates
and notifiers. I can't specify totally exact how to make these
decisions, but these are basically the guidelines I use. So, new
and updated docs are here:
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/NotifiersUpdates
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/DataNotifiers
2009-09-04 20:51:09 +00:00
|
|
|
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
|
2009-07-19 13:06:18 +00:00
|
|
|
|
|
|
|
/* notifiers for updates */
|
2009-07-19 07:20:21 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT|NA_ADDED, ob);
|
2009-05-27 00:03:49 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
2009-07-19 13:06:18 +00:00
|
|
|
/* ------------------ */
|
|
|
|
|
|
|
|
/* dummy operator callback */
|
2009-07-14 20:27:28 +00:00
|
|
|
static int object_constraint_add_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
2009-07-20 00:02:03 +00:00
|
|
|
ScrArea *sa= CTX_wm_area(C);
|
|
|
|
Object *ob;
|
2009-07-20 12:42:31 +00:00
|
|
|
int type= RNA_enum_get(op->ptr, "type");
|
|
|
|
short with_targets= 0;
|
2009-07-20 00:02:03 +00:00
|
|
|
|
2009-07-20 12:42:31 +00:00
|
|
|
/* get active object from context */
|
2009-07-20 00:02:03 +00:00
|
|
|
if (sa->spacetype == SPACE_BUTS)
|
|
|
|
ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
|
|
|
|
else
|
|
|
|
ob= CTX_data_active_object(C);
|
|
|
|
|
2009-07-20 12:42:31 +00:00
|
|
|
if (!ob) {
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "No active object to add constraint to.");
|
2009-07-14 20:27:28 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2009-07-19 13:06:18 +00:00
|
|
|
}
|
2009-07-20 12:42:31 +00:00
|
|
|
|
|
|
|
/* hack: set constraint targets from selected objects in context is allowed when
|
|
|
|
* operator name included 'with_targets', since the menu doesn't allow multiple properties
|
|
|
|
*/
|
|
|
|
if (strstr(op->idname, "with_targets"))
|
|
|
|
with_targets= 1;
|
|
|
|
|
|
|
|
return constraint_add_exec(C, op, ob, &ob->constraints, type, with_targets);
|
2009-07-19 13:06:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* dummy operator callback */
|
2009-07-14 20:27:28 +00:00
|
|
|
static int pose_constraint_add_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
2009-07-20 00:02:03 +00:00
|
|
|
ScrArea *sa= CTX_wm_area(C);
|
|
|
|
Object *ob;
|
2009-07-20 12:42:31 +00:00
|
|
|
int type= RNA_enum_get(op->ptr, "type");
|
|
|
|
short with_targets= 0;
|
2009-07-20 00:02:03 +00:00
|
|
|
|
2009-07-20 12:42:31 +00:00
|
|
|
/* get active object from context */
|
2009-07-20 00:02:03 +00:00
|
|
|
if (sa->spacetype == SPACE_BUTS)
|
|
|
|
ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
|
|
|
|
else
|
|
|
|
ob= CTX_data_active_object(C);
|
|
|
|
|
2009-07-20 12:42:31 +00:00
|
|
|
if (!ob) {
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "No active object to add constraint to.");
|
2009-07-19 05:20:30 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2009-07-20 12:42:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* hack: set constraint targets from selected objects in context is allowed when
|
|
|
|
* operator name included 'with_targets', since the menu doesn't allow multiple properties
|
|
|
|
*/
|
|
|
|
if (strstr(op->idname, "with_targets"))
|
|
|
|
with_targets= 1;
|
2009-07-14 20:27:28 +00:00
|
|
|
|
2009-07-20 12:42:31 +00:00
|
|
|
return constraint_add_exec(C, op, ob, get_active_constraints(ob), type, with_targets);
|
2009-07-14 20:27:28 +00:00
|
|
|
}
|
|
|
|
|
2009-07-20 12:42:31 +00:00
|
|
|
/* ------------------ */
|
|
|
|
|
2009-05-27 00:03:49 +00:00
|
|
|
void OBJECT_OT_constraint_add(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
|
|
|
ot->name= "Add Constraint";
|
|
|
|
ot->description = "Add a constraint to the active object.";
|
|
|
|
ot->idname= "OBJECT_OT_constraint_add";
|
|
|
|
|
|
|
|
/* api callbacks */
|
2009-07-20 12:42:31 +00:00
|
|
|
ot->invoke= WM_menu_invoke;
|
|
|
|
ot->exec= object_constraint_add_exec;
|
2009-12-04 06:33:01 +00:00
|
|
|
ot->poll= ED_operator_object_active_editable;
|
2009-07-20 12:42:31 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
|
|
|
|
/* properties */
|
2010-01-15 22:40:33 +00:00
|
|
|
ot->prop= RNA_def_enum(ot->srna, "type", constraint_type_items, 0, "Type", "");
|
2009-07-20 12:42:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void OBJECT_OT_constraint_add_with_targets(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* 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;
|
2009-07-14 20:27:28 +00:00
|
|
|
ot->exec= object_constraint_add_exec;
|
2009-12-04 06:33:01 +00:00
|
|
|
ot->poll= ED_operator_object_active_editable;
|
2009-05-27 00:03:49 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
|
2009-07-14 20:27:28 +00:00
|
|
|
/* properties */
|
2010-01-15 22:40:33 +00:00
|
|
|
ot->prop= RNA_def_enum(ot->srna, "type", constraint_type_items, 0, "Type", "");
|
2009-07-14 20:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
2009-07-20 12:42:31 +00:00
|
|
|
ot->invoke= WM_menu_invoke;
|
|
|
|
ot->exec= pose_constraint_add_exec;
|
|
|
|
ot->poll= ED_operator_posemode;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
|
|
|
|
/* properties */
|
2010-01-15 22:40:33 +00:00
|
|
|
ot->prop= RNA_def_enum(ot->srna, "type", constraint_type_items, 0, "Type", "");
|
2009-07-20 12:42:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2009-07-14 20:27:28 +00:00
|
|
|
ot->exec= pose_constraint_add_exec;
|
|
|
|
ot->poll= ED_operator_posemode;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
|
|
|
|
/* properties */
|
2010-01-15 22:40:33 +00:00
|
|
|
ot->prop= RNA_def_enum(ot->srna, "type", constraint_type_items, 0, "Type", "");
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
|
|
|
|
2009-07-21 04:21:07 +00:00
|
|
|
/************************ IK Constraint operators *********************/
|
|
|
|
/* 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, wmEvent *evt)
|
|
|
|
{
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
|
|
|
bPoseChannel *pchan= get_active_posechannel(ob);
|
|
|
|
bConstraint *con= NULL;
|
|
|
|
|
|
|
|
uiPopupMenu *pup;
|
|
|
|
uiLayout *layout;
|
|
|
|
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 active bone to add IK Constraint to.");
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bone must not have any constraints already */
|
|
|
|
for (con= pchan->constraints.first; con; con= con->next) {
|
|
|
|
if (con->type==CONSTRAINT_TYPE_KINEMATIC) break;
|
|
|
|
}
|
|
|
|
if (con) {
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "Bone already has IK Constraint.");
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* prepare popup menu to choose targetting options */
|
|
|
|
pup= uiPupMenuBegin(C, "Add IK", 0);
|
|
|
|
layout= uiPupMenuLayout(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?
|
|
|
|
* - the only thing that matters is that we want a target...
|
|
|
|
*/
|
|
|
|
if (tar_pchan)
|
|
|
|
uiItemBooleanO(layout, "To Active Bone", 0, "POSE_OT_ik_add", "with_targets", 1);
|
|
|
|
else
|
|
|
|
uiItemBooleanO(layout, "To Active Object", 0, "POSE_OT_ik_add", "with_targets", 1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* we have a choice of adding to a new empty, or not setting any target (targetless IK) */
|
|
|
|
uiItemBooleanO(layout, "To New Empty Object", 0, "POSE_OT_ik_add", "with_targets", 1);
|
|
|
|
uiItemBooleanO(layout, "Without Targets", 0, "POSE_OT_ik_add", "with_targets", 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finish building the menu, and process it (should result in calling self again) */
|
|
|
|
uiPupMenuEnd(C, pup);
|
|
|
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* call constraint_add_exec() to add the IK constraint */
|
|
|
|
static int pose_ik_add_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
|
|
|
int 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, get_active_constraints(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;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------ */
|
|
|
|
|
|
|
|
/* remove IK constraints from selected bones */
|
|
|
|
static int pose_ik_clear_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
|
|
|
|
|
|
|
/* only remove IK Constraints */
|
2009-11-25 15:00:29 +00:00
|
|
|
CTX_DATA_BEGIN(C, bPoseChannel*, pchan, selected_pose_bones)
|
2009-07-21 04:21:07 +00:00
|
|
|
{
|
|
|
|
bConstraint *con, *next;
|
|
|
|
|
|
|
|
// TODO: should we be checking if these contraints were local before we try and remove them?
|
|
|
|
for (con= pchan->constraints.first; con; con= next) {
|
|
|
|
next= con->next;
|
|
|
|
if (con->type==CONSTRAINT_TYPE_KINEMATIC) {
|
2009-11-16 11:11:16 +00:00
|
|
|
remove_constraint(&pchan->constraints, con);
|
2009-07-21 04:21:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pchan->constflag &= ~(PCHAN_HAS_IK|PCHAN_HAS_TARGET);
|
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
2009-11-01 22:30:47 +00:00
|
|
|
/* refresh depsgraph */
|
2.5
Notifiers
---------
Various fixes for wrong use of notifiers, and some new notifiers
to make things a bit more clear and consistent, with two notable
changes:
* Geometry changes are now done with NC_GEOM, rather than
NC_OBJECT|ND_GEOM_, so an object does need to be available.
* Space data now use NC_SPACE|ND_SPACE_*, instead of data
notifiers or even NC_WINDOW in some cases. Note that NC_SPACE
should only be used for notifying about changes in space data,
we don't want to go back to allqueue(REDRAW..).
Depsgraph
---------
The dependency graph now has a different flush call:
DAG_object_flush_update(scene, ob, flag)
is replaced by:
DAG_id_flush_update(id, flag)
It still works basically the same, one difference is that it now
also accepts object data (e.g. Mesh), again to avoid requiring an
Object to be available. Other ID types will simply do nothing at
the moment.
Docs
----
I made some guidelines for how/when to do which kinds of updates
and notifiers. I can't specify totally exact how to make these
decisions, but these are basically the guidelines I use. So, new
and updated docs are here:
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/NotifiersUpdates
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/DataNotifiers
2009-09-04 20:51:09 +00:00
|
|
|
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
|
2009-07-21 04:21:07 +00:00
|
|
|
|
|
|
|
/* note, notifier might evolve */
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT|NA_REMOVED, ob);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
}
|
2.5: Object module
* Split object_edit.c into multiple files:
object_add.c, object_edit.c, object_hook.c, object_relations.c,
object_select.c, object_transform.c.
* Rename files to have consistent object_ and mball_ prefix:
object_shapekey.c, object_lattice.c, object_vgroup.c, mball_edit.c.
* Added operators:
* vertex group menu and set active
* apply location, rotation, scale, visual transform (location is new)
* make local
* make vertex parent
* move to layer
* convert to curve/mesh (not finished yet)
* Many small fixes for marked issues, but still much code to be cleaned
up here...
2009-09-09 11:52:56 +00:00
|
|
|
|