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,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2009-01-10 19:34:23 +00:00
|
|
|
*
|
|
|
|
* 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_constraint_types.h"
|
|
|
|
#include "DNA_curve_types.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "DNA_text_types.h"
|
2010-08-04 04:01:27 +00:00
|
|
|
#include "DNA_object_types.h"
|
2009-01-10 19:34:23 +00:00
|
|
|
|
|
|
|
#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-09-24 21:22:24 +00:00
|
|
|
#include "BIK_api.h"
|
2009-01-10 19:34:23 +00:00
|
|
|
|
2010-10-31 04:11:39 +00:00
|
|
|
#ifdef WITH_PYTHON
|
2009-01-10 19:34:23 +00:00
|
|
|
#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"
|
|
|
|
|
2010-03-31 00:55:01 +00:00
|
|
|
#include "ED_object.h"
|
2010-10-07 23:17:14 +00:00
|
|
|
#include "ED_armature.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
|
|
|
}
|
2010-05-27 11:56:31 +00:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-10-31 04:11:39 +00:00
|
|
|
#ifdef WITH_PYTHON
|
2009-01-10 19:34:23 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2010-10-31 04:11:39 +00:00
|
|
|
#endif /* WITH_PYTHON */
|
2009-01-10 19:34:23 +00:00
|
|
|
|
|
|
|
/* this callback gets called when the 'refresh' button of a pyconstraint gets pressed */
|
|
|
|
void update_pyconstraint_cb (void *arg1, void *arg2)
|
|
|
|
{
|
2010-10-31 04:11:39 +00:00
|
|
|
#ifndef WITH_PYTHON
|
2010-10-23 16:03:31 +00:00
|
|
|
(void)arg1; /* unused */
|
|
|
|
(void)arg2; /* unused */
|
|
|
|
#else
|
2009-01-10 19:34:23 +00:00
|
|
|
Object *owner= (Object *)arg1;
|
|
|
|
bConstraint *con= (bConstraint *)arg2;
|
|
|
|
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,
|
2010-02-18 16:05:01 +00:00
|
|
|
* if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag.
|
2009-01-10 19:34:23 +00:00
|
|
|
*/
|
2010-02-18 16:41:47 +00:00
|
|
|
static void test_constraints (Object *owner, bPoseChannel *pchan)
|
2009-01-10 19:34:23 +00:00
|
|
|
{
|
|
|
|
bConstraint *curcon;
|
|
|
|
ListBase *conlist= NULL;
|
|
|
|
int type;
|
|
|
|
|
|
|
|
if (owner==NULL) return;
|
|
|
|
|
|
|
|
/* Check parents */
|
2010-02-18 16:41:47 +00:00
|
|
|
if (pchan) {
|
2009-01-10 19:34:23 +00:00
|
|
|
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:
|
2010-02-18 16:41:47 +00:00
|
|
|
conlist = &pchan->constraints;
|
2009-01-10 19:34:23 +00:00
|
|
|
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;
|
|
|
|
}
|
== Pivot Constraint ==
This constraint allows an object or bone to have their rotations applied as if their origin/pivot-point was located elsewhere. The most obvious uses include foot-roll, see-saws, but could also include more complicated rolling-box examples.
== Usage Examples ==
=== Foot Roll ===
1. Add 'Pivot' Constraint to the bone without any target.
2. Set the 'Y' value of the offset to the length of the bone. Usually this should be negative (if you rig with feet facing 'forwards' along -Y axis). This gives you a pivot point relative to the bone's (preconstraint) location, which should be at the tip of the bone here. Disabling the 'Use Relative Offset' would make this offset be relative to 0,0,0 instead of to the owner/bone-head.
3. Ensure that the 'Pivot When' setting is set to '-X Rot', (default) which means that the pivot will only used when the rotation on the X-Axis is negative to get tip-toe 'roll'.
=== See Saw ===
1. Add a 'Pivot' constraint too see-saw plank object, this time with a target that you wish to have as the pivot-point. It's possible to do this without too (as before), but is less intuitive.
2. Optionally, if you want the plank slightly raised, set the z-offset value, which should make the pivot-point used to be relative to the target with the z-offset applied.
3. Ensure that 'Pivot When' is set to 'Always', which means that the pivot will always be used, irrespective of the rotation.
== Notes ==
* The 'Pivot When' setting has been integrated in the constraint, since this is something that will often be required for these setups. Having to set up additional drivers to drive the constraint to do this kindof beats the purpose of providing this.
* The 'Offset' functionality is probably not presented as clearly as it could be. We may need to go over this again.
* For foot-roll - if any scaling of the foot is required, simply set up a driver on the y-offset to make this dynamically respond to the "scale" RNA property of the bones (don't use the "Transform Channel" vartype since that won't work correct here). However, this shouldn't be common enough to warrant special treatment.
2010-05-27 10:50:06 +00:00
|
|
|
else if (curcon->type == CONSTRAINT_TYPE_PIVOT) {
|
|
|
|
bPivotConstraint *data = curcon->data;
|
|
|
|
|
|
|
|
/* target doesn't have to exist, but if it is non-null, it must exist! */
|
|
|
|
if (data->tar && 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* targets have already been checked for this */
|
|
|
|
continue;
|
|
|
|
}
|
2009-01-10 19:34:23 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void object_test_constraints (Object *owner)
|
|
|
|
{
|
2010-05-27 11:56:31 +00:00
|
|
|
if (owner->constraints.first)
|
2010-02-18 16:41:47 +00:00
|
|
|
test_constraints(owner, NULL);
|
2010-05-27 11:56:31 +00:00
|
|
|
|
2010-02-18 16:41:47 +00:00
|
|
|
if (owner->type==OB_ARMATURE && owner->pose) {
|
|
|
|
bPoseChannel *pchan;
|
2010-05-27 11:56:31 +00:00
|
|
|
|
|
|
|
for (pchan= owner->pose->chanbase.first; pchan; pchan= pchan->next) {
|
|
|
|
if (pchan->constraints.first)
|
2010-02-18 16:41:47 +00:00
|
|
|
test_constraints(owner, pchan);
|
2010-05-27 11:56:31 +00:00
|
|
|
}
|
2009-01-10 19:34:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-26 03:42:38 +00:00
|
|
|
/************************ generic functions for operators using constraint names and data context *********************/
|
|
|
|
|
|
|
|
#define EDIT_CONSTRAINT_OWNER_OBJECT 0
|
|
|
|
#define EDIT_CONSTRAINT_OWNER_BONE 1
|
|
|
|
|
|
|
|
static EnumPropertyItem constraint_owner_items[] = {
|
2010-05-27 11:56:31 +00:00
|
|
|
{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}};
|
2010-04-26 03:42:38 +00:00
|
|
|
|
2009-07-26 11:57:27 +00:00
|
|
|
|
2010-04-26 03:42:38 +00:00
|
|
|
static int edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
|
2009-09-04 08:49:11 +00:00
|
|
|
{
|
2010-04-26 03:42:38 +00:00
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", rna_type);
|
|
|
|
Object *ob= (ptr.id.data)?ptr.id.data:ED_object_active_context(C);
|
|
|
|
|
|
|
|
if (!ob || ob->id.lib) return 0;
|
|
|
|
if (ptr.data && ((ID*)ptr.id.data)->lib) return 0;
|
|
|
|
|
|
|
|
return 1;
|
2009-09-04 08:49:11 +00:00
|
|
|
}
|
|
|
|
|
2010-04-26 03:42:38 +00:00
|
|
|
static int edit_constraint_poll(bContext *C)
|
2009-07-26 11:57:27 +00:00
|
|
|
{
|
2010-04-26 03:42:38 +00:00
|
|
|
return edit_constraint_poll_generic(C, &RNA_Constraint);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void edit_constraint_properties(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
RNA_def_string(ot->srna, "constraint", "", 32, "Constraint", "Name of the constraint to edit");
|
|
|
|
RNA_def_enum(ot->srna, "owner", constraint_owner_items, 0, "Owner", "The owner of this constraint");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
|
|
|
|
Object *ob= (ptr.id.data)?ptr.id.data:ED_object_active_context(C);
|
|
|
|
bConstraint *con;
|
|
|
|
ListBase *list;
|
|
|
|
|
|
|
|
if (RNA_property_is_set(op->ptr, "constraint") && RNA_property_is_set(op->ptr, "owner"))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (ptr.data) {
|
|
|
|
con = ptr.data;
|
|
|
|
RNA_string_set(op->ptr, "constraint", con->name);
|
|
|
|
|
|
|
|
list = get_constraint_lb(ob, con, NULL);
|
|
|
|
|
|
|
|
if (&ob->constraints == list)
|
|
|
|
RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_OBJECT);
|
|
|
|
else
|
|
|
|
RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_BONE);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2009-07-26 11:57:27 +00:00
|
|
|
|
2010-04-26 03:42:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-10-15 05:18:45 +00:00
|
|
|
static bConstraint *edit_constraint_property_get(wmOperator *op, Object *ob, int type)
|
2010-04-26 03:42:38 +00:00
|
|
|
{
|
|
|
|
char constraint_name[32];
|
|
|
|
int owner = RNA_enum_get(op->ptr, "owner");
|
|
|
|
bConstraint *con;
|
2010-05-07 03:44:34 +00:00
|
|
|
ListBase *list=NULL;
|
2010-04-26 03:42:38 +00:00
|
|
|
|
|
|
|
RNA_string_get(op->ptr, "constraint", constraint_name);
|
|
|
|
|
|
|
|
if (owner == EDIT_CONSTRAINT_OWNER_OBJECT) {
|
|
|
|
list = &ob->constraints;
|
2010-05-27 11:56:31 +00:00
|
|
|
}
|
|
|
|
else if (owner == EDIT_CONSTRAINT_OWNER_BONE) {
|
2010-04-26 03:42:38 +00:00
|
|
|
bPoseChannel *pchan= get_active_posechannel(ob);
|
|
|
|
if (pchan)
|
|
|
|
list = &pchan->constraints;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
con = constraints_findByName(list, constraint_name);
|
|
|
|
|
2010-05-27 11:56:31 +00:00
|
|
|
if (con && (type != 0) && (con->type != type))
|
2010-04-26 03:42:38 +00:00
|
|
|
con = NULL;
|
|
|
|
|
|
|
|
return con;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ********************** CONSTRAINT-SPECIFIC STUFF ********************* */
|
|
|
|
|
|
|
|
/* ---------- Distance-Dependent Constraints ---------- */
|
|
|
|
/* StretchTo, Limit Distance */
|
|
|
|
|
|
|
|
static int stretchto_reset_exec (bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Object *ob = ED_object_active_context(C);
|
2010-10-15 05:18:45 +00:00
|
|
|
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_STRETCHTO);
|
2010-05-05 00:44:42 +00:00
|
|
|
bStretchToConstraint *data= (con) ? (bStretchToConstraint *)con->data : NULL;
|
|
|
|
|
|
|
|
/* despite 3 layers of checks, we may still not be able to find a constraint */
|
|
|
|
if (data == NULL)
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
2009-07-26 11:57:27 +00:00
|
|
|
/* just set original length to 0.0, which will cause a reset on next recalc */
|
2010-04-26 03:42:38 +00:00
|
|
|
data->orglength = 0.0f;
|
|
|
|
ED_object_constraint_update(ob);
|
2009-07-26 11:57:27 +00:00
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int stretchto_reset_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
|
2010-04-26 03:42:38 +00:00
|
|
|
{
|
|
|
|
if (edit_constraint_invoke_properties(C, op))
|
|
|
|
return stretchto_reset_exec(C, op);
|
|
|
|
else
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
2009-07-26 11:57:27 +00:00
|
|
|
void CONSTRAINT_OT_stretchto_reset (wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
|
|
|
ot->name= "Reset Original Length";
|
|
|
|
ot->idname= "CONSTRAINT_OT_stretchto_reset";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description= "Reset original length of bone for Stretch To Constraint";
|
2009-07-26 11:57:27 +00:00
|
|
|
|
|
|
|
ot->exec= stretchto_reset_exec;
|
2010-04-26 03:42:38 +00:00
|
|
|
ot->invoke= stretchto_reset_invoke;
|
|
|
|
ot->poll= edit_constraint_poll;
|
2009-07-26 11:57:27 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2010-04-26 03:42:38 +00:00
|
|
|
edit_constraint_properties(ot);
|
2009-07-26 11:57:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int limitdistance_reset_exec (bContext *C, wmOperator *op)
|
|
|
|
{
|
2010-04-26 03:42:38 +00:00
|
|
|
Object *ob = ED_object_active_context(C);
|
2010-10-15 05:18:45 +00:00
|
|
|
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_DISTLIMIT);
|
2010-05-05 00:44:42 +00:00
|
|
|
bDistLimitConstraint *data= (con) ? (bDistLimitConstraint *)con->data : NULL;
|
|
|
|
|
|
|
|
/* despite 3 layers of checks, we may still not be able to find a constraint */
|
|
|
|
if (data == NULL)
|
|
|
|
return OPERATOR_CANCELLED;
|
2009-07-26 11:57:27 +00:00
|
|
|
|
2010-04-26 03:42:38 +00:00
|
|
|
/* just set original length to 0.0, which will cause a reset on next recalc */
|
|
|
|
data->dist = 0.0f;
|
|
|
|
ED_object_constraint_update(ob);
|
2009-07-26 11:57:27 +00:00
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int limitdistance_reset_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
|
2009-09-04 08:49:11 +00:00
|
|
|
{
|
2010-04-26 03:42:38 +00:00
|
|
|
if (edit_constraint_invoke_properties(C, op))
|
|
|
|
return limitdistance_reset_exec(C, op);
|
|
|
|
else
|
|
|
|
return OPERATOR_CANCELLED;
|
2009-09-04 08:49:11 +00:00
|
|
|
}
|
|
|
|
|
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";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description= "Reset limiting distance for Limit Distance Constraint";
|
2009-07-26 11:57:27 +00:00
|
|
|
|
|
|
|
ot->exec= limitdistance_reset_exec;
|
2010-04-26 03:42:38 +00:00
|
|
|
ot->invoke= limitdistance_reset_invoke;
|
|
|
|
ot->poll= edit_constraint_poll;
|
2009-07-26 11:57:27 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2010-04-26 03:42:38 +00:00
|
|
|
edit_constraint_properties(ot);
|
2009-07-26 11:57:27 +00:00
|
|
|
}
|
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
/* ------------- Child-Of Constraint ------------------ */
|
2010-05-07 10:59:48 +00:00
|
|
|
|
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
|
|
|
Scene *scene= CTX_data_scene(C);
|
2010-04-26 03:42:38 +00:00
|
|
|
Object *ob = ED_object_active_context(C);
|
2010-10-15 05:18:45 +00:00
|
|
|
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_CHILDOF);
|
2010-05-05 00:44:42 +00:00
|
|
|
bChildOfConstraint *data= (con) ? (bChildOfConstraint *)con->data : NULL;
|
2009-01-10 19:34:23 +00:00
|
|
|
bPoseChannel *pchan= NULL;
|
2010-05-05 00:44:42 +00:00
|
|
|
|
|
|
|
/* despite 3 layers of checks, we may still not be able to find a constraint */
|
|
|
|
if (data == NULL)
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
2009-01-10 19:34:23 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int childof_set_inverse_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
|
2010-04-26 03:42:38 +00:00
|
|
|
{
|
|
|
|
if (edit_constraint_invoke_properties(C, op))
|
|
|
|
return childof_set_inverse_exec(C, op);
|
|
|
|
else
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
2009-07-11 11:52:20 +00:00
|
|
|
void CONSTRAINT_OT_childof_set_inverse (wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
|
|
|
ot->name= "Set Inverse";
|
|
|
|
ot->idname= "CONSTRAINT_OT_childof_set_inverse";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description= "Set inverse correction for ChildOf constraint";
|
2009-07-11 11:52:20 +00:00
|
|
|
|
|
|
|
ot->exec= childof_set_inverse_exec;
|
2010-04-26 03:42:38 +00:00
|
|
|
ot->invoke= childof_set_inverse_invoke;
|
|
|
|
ot->poll= edit_constraint_poll;
|
2009-07-11 11:52:20 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2010-04-26 03:42:38 +00:00
|
|
|
edit_constraint_properties(ot);
|
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
|
|
|
{
|
2010-04-26 03:42:38 +00:00
|
|
|
Object *ob = ED_object_active_context(C);
|
2010-10-15 05:18:45 +00:00
|
|
|
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_CHILDOF);
|
2010-05-05 00:44:42 +00:00
|
|
|
bChildOfConstraint *data= (con) ? (bChildOfConstraint *)con->data : NULL;
|
2009-01-10 19:34:23 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int childof_clear_inverse_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
|
2010-04-26 03:42:38 +00:00
|
|
|
{
|
|
|
|
if (edit_constraint_invoke_properties(C, op))
|
|
|
|
return childof_clear_inverse_exec(C, op);
|
|
|
|
else
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
2009-07-11 11:52:20 +00:00
|
|
|
void CONSTRAINT_OT_childof_clear_inverse (wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
|
|
|
ot->name= "Clear Inverse";
|
|
|
|
ot->idname= "CONSTRAINT_OT_childof_clear_inverse";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description= "Clear inverse correction for ChildOf constraint";
|
2009-07-11 11:52:20 +00:00
|
|
|
|
|
|
|
ot->exec= childof_clear_inverse_exec;
|
2010-04-26 03:42:38 +00:00
|
|
|
ot->invoke= childof_clear_inverse_invoke;
|
|
|
|
ot->poll= edit_constraint_poll;
|
2009-07-11 11:52:20 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2010-04-26 03:42:38 +00:00
|
|
|
edit_constraint_properties(ot);
|
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);
|
|
|
|
}
|
|
|
|
|
2010-08-01 12:47:49 +00:00
|
|
|
void ED_object_constraint_dependency_update(Main *bmain, Scene *scene, Object *ob)
|
2009-09-24 21:22:24 +00:00
|
|
|
{
|
|
|
|
ED_object_constraint_update(ob);
|
|
|
|
|
|
|
|
if(ob->pose) ob->pose->flag |= POSE_RECALC; // checks & sorts pose channels
|
2010-08-01 12:47:49 +00:00
|
|
|
DAG_scene_sort(bmain, scene);
|
2009-09-24 21:22:24 +00:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int constraint_delete_exec (bContext *C, wmOperator *UNUSED(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
|
|
|
|
2010-09-16 07:14:48 +00:00
|
|
|
ED_object_constraint_update(ob); /* needed to set the flags on posebones correctly */
|
|
|
|
|
2010-02-03 11:36:09 +00:00
|
|
|
/* notifiers */
|
2010-09-18 08:37:47 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT|NA_REMOVED, ob);
|
2010-02-03 11:36:09 +00:00
|
|
|
|
|
|
|
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";
|
2010-04-26 03:42:38 +00:00
|
|
|
ot->description= "Remove constraint from constraint stack";
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
2010-04-26 03:42:38 +00:00
|
|
|
Object *ob = ED_object_active_context(C);
|
2010-10-15 05:18:45 +00:00
|
|
|
bConstraint *con = edit_constraint_property_get(op, ob, 0);
|
2009-07-11 12:54:17 +00:00
|
|
|
|
2010-04-26 03:42:38 +00:00
|
|
|
if (con && 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
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int constraint_move_down_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
|
2010-04-26 03:42:38 +00:00
|
|
|
{
|
|
|
|
if (edit_constraint_invoke_properties(C, op))
|
|
|
|
return constraint_move_down_exec(C, op);
|
|
|
|
else
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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";
|
2010-07-04 09:42:00 +00:00
|
|
|
ot->description= "Move constraint down in constraint stack";
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
ot->exec= constraint_move_down_exec;
|
2010-04-26 03:42:38 +00:00
|
|
|
ot->invoke= constraint_move_down_invoke;
|
|
|
|
ot->poll= edit_constraint_poll;
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2010-04-26 03:42:38 +00:00
|
|
|
edit_constraint_properties(ot);
|
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
|
|
|
{
|
2010-04-26 03:42:38 +00:00
|
|
|
Object *ob = ED_object_active_context(C);
|
2010-10-15 05:18:45 +00:00
|
|
|
bConstraint *con = edit_constraint_property_get(op, ob, 0);
|
2009-07-11 12:54:17 +00:00
|
|
|
|
2010-04-26 03:42:38 +00:00
|
|
|
if (con && 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
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int constraint_move_up_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
|
2010-04-26 03:42:38 +00:00
|
|
|
{
|
|
|
|
if (edit_constraint_invoke_properties(C, op))
|
|
|
|
return constraint_move_up_exec(C, op);
|
|
|
|
else
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
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";
|
2010-07-04 09:42:00 +00:00
|
|
|
ot->description= "Move constraint up in constraint stack";
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
ot->exec= constraint_move_up_exec;
|
2010-04-26 03:42:38 +00:00
|
|
|
ot->invoke= constraint_move_up_invoke;
|
|
|
|
ot->poll= edit_constraint_poll;
|
2009-07-11 12:54:17 +00:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2010-04-26 03:42:38 +00:00
|
|
|
edit_constraint_properties(ot);
|
2009-05-27 00:03:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***************************** OPERATORS ****************************/
|
|
|
|
|
2009-07-19 07:20:21 +00:00
|
|
|
/************************ remove constraint operators *********************/
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int pose_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-07-19 07:20:21 +00:00
|
|
|
{
|
2010-08-01 12:47:49 +00:00
|
|
|
Main *bmain= CTX_data_main(C);
|
2009-09-20 11:21:44 +00:00
|
|
|
Scene *scene= CTX_data_scene(C);
|
2010-10-07 23:17:14 +00:00
|
|
|
Object *ob= ED_object_pose_armature(CTX_data_active_object(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 */
|
2010-08-01 12:47:49 +00:00
|
|
|
DAG_scene_sort(bmain, scene); /* sort order of objects */
|
2009-09-20 11:21:44 +00:00
|
|
|
|
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";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description= "Clear all the constraints for the selected bones";
|
2009-07-19 07:20:21 +00:00
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
ot->exec= pose_constraints_clear_exec;
|
|
|
|
ot->poll= ED_operator_posemode; // XXX - do we want to ensure there are selected bones too?
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int object_constraints_clear_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-07-19 07:20:21 +00:00
|
|
|
{
|
2010-08-01 12:47:49 +00:00
|
|
|
Main *bmain= CTX_data_main(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 */
|
2010-05-27 11:56:31 +00:00
|
|
|
CTX_DATA_BEGIN(C, Object*, ob, selected_editable_objects)
|
|
|
|
{
|
|
|
|
free_constraints(&ob->constraints);
|
|
|
|
DAG_id_flush_update(&ob->id, OB_RECALC_OB);
|
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
2009-07-19 07:20:21 +00:00
|
|
|
|
2009-09-20 11:21:44 +00:00
|
|
|
/* force depsgraph to get recalculated since relationships removed */
|
2010-08-01 12:47:49 +00:00
|
|
|
DAG_scene_sort(bmain, scene); /* sort order of objects */
|
2009-09-20 11:21:44 +00:00
|
|
|
|
2009-07-19 07:20:21 +00:00
|
|
|
/* do updates */
|
2010-05-27 11:56:31 +00:00
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, NULL);
|
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";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description= "Clear all the constraints for the active Object only";
|
2009-07-19 07:20:21 +00:00
|
|
|
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
2010-05-27 11:56:31 +00:00
|
|
|
/************************ copy all constraints operators *********************/
|
|
|
|
|
|
|
|
static int pose_constraint_copy_exec(bContext *C, wmOperator *op)
|
|
|
|
{
|
2010-08-01 12:47:49 +00:00
|
|
|
Main *bmain= CTX_data_main(C);
|
2010-05-27 11:56:31 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2010-08-01 12:47:49 +00:00
|
|
|
bPoseChannel *pchan = CTX_data_active_pose_bone(C);
|
2010-05-27 11:56:31 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy all constraints from active posebone to all selected posebones */
|
|
|
|
CTX_DATA_BEGIN(C, bPoseChannel*, chan, selected_pose_bones)
|
|
|
|
{
|
|
|
|
/* if we're not handling the object we're copying from, copy all constraints over */
|
|
|
|
if (pchan != chan)
|
|
|
|
copy_constraints(&chan->constraints, &pchan->constraints, TRUE);
|
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
/* force depsgraph to get recalculated since new relationships added */
|
2010-08-01 12:47:49 +00:00
|
|
|
DAG_scene_sort(bmain, scene); /* sort order of objects/bones */
|
2010-05-27 11:56:31 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void POSE_OT_constraints_copy(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
|
|
|
ot->name= "Copy Constraints to Selected";
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
}
|
|
|
|
|
2010-10-15 01:36:14 +00:00
|
|
|
static int object_constraint_copy_exec(bContext *C, wmOperator *UNUSED(op))
|
2010-05-27 11:56:31 +00:00
|
|
|
{
|
2010-08-01 12:47:49 +00:00
|
|
|
Main *bmain= CTX_data_main(C);
|
2010-05-27 11:56:31 +00:00
|
|
|
Scene *scene = CTX_data_scene(C);
|
2010-08-01 12:47:49 +00:00
|
|
|
Object *obact = ED_object_active_context(C);
|
2010-05-27 11:56:31 +00:00
|
|
|
|
|
|
|
/* 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)
|
|
|
|
copy_constraints(&ob->constraints, &obact->constraints, TRUE);
|
|
|
|
}
|
|
|
|
CTX_DATA_END;
|
|
|
|
|
|
|
|
/* force depsgraph to get recalculated since new relationships added */
|
2010-08-01 12:47:49 +00:00
|
|
|
DAG_scene_sort(bmain, scene); /* sort order of objects */
|
2010-05-27 11:56:31 +00:00
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OBJECT_OT_constraints_copy(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
|
|
|
ot->name= "Copy Constraints to Selected";
|
|
|
|
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 */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
2010-03-31 00:55:01 +00:00
|
|
|
Object *obact= ED_object_active_context(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:
|
2010-03-16 18:34:30 +00:00
|
|
|
case CONSTRAINT_TYPE_SAMEVOL:
|
2009-07-19 13:06:18 +00:00
|
|
|
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
|
|
|
{
|
2010-08-01 12:47:49 +00:00
|
|
|
Main *bmain= CTX_data_main(C);
|
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 */
|
2010-05-27 11:56:31 +00:00
|
|
|
if (pchan)
|
2009-11-11 19:58:30 +00:00
|
|
|
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
|
|
|
|
{
|
2010-10-31 04:11:39 +00:00
|
|
|
#ifdef WITH_PYTHON
|
2009-07-19 13:06:18 +00:00
|
|
|
char *menustr;
|
|
|
|
int scriptint= 0;
|
|
|
|
/* 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 */
|
2010-08-01 12:47:49 +00:00
|
|
|
DAG_scene_sort(bmain, scene); /* sort order of objects */
|
2009-07-19 13:06:18 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2010-03-31 00:55:01 +00:00
|
|
|
Object *ob=ED_object_active_context(C);
|
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
|
|
|
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)
|
|
|
|
{
|
2010-10-07 23:17:14 +00:00
|
|
|
Object *ob= ED_object_pose_armature(ED_object_active_context(C));
|
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
|
|
|
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";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description = "Add a constraint to the active object";
|
2009-05-27 00:03:49 +00:00
|
|
|
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)";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description = "Add a constraint to the active object, with target (where applicable) set to the selected Objects/Bones";
|
2009-07-20 12:42:31 +00:00
|
|
|
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";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description = "Add a constraint to the active bone";
|
2009-07-14 20:27:28 +00:00
|
|
|
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)";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description = "Add a constraint to the active bone, with target (where applicable) set to the selected Objects/Bones";
|
2009-07-20 12:42:31 +00:00
|
|
|
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 */
|
2010-10-15 01:36:14 +00:00
|
|
|
static int pose_ik_add_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
|
2009-07-21 04:21:07 +00:00
|
|
|
{
|
2010-10-07 23:17:14 +00:00
|
|
|
Object *ob= ED_object_pose_armature(CTX_data_active_object(C));
|
2009-07-21 04:21:07 +00:00
|
|
|
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";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description= "Add IK Constraint to the active Bone";
|
2009-07-21 04:21:07 +00:00
|
|
|
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 */
|
2010-10-15 01:36:14 +00:00
|
|
|
static int pose_ik_clear_exec(bContext *C, wmOperator *UNUSED(op))
|
2009-07-21 04:21:07 +00:00
|
|
|
{
|
2010-10-07 23:17:14 +00:00
|
|
|
Object *ob= ED_object_pose_armature(CTX_data_active_object(C));
|
2009-07-21 04:21:07 +00:00
|
|
|
|
|
|
|
/* 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";
|
2010-02-10 21:15:44 +00:00
|
|
|
ot->description= "Remove all IK Constraints from selected bones";
|
2009-07-21 04:21:07 +00:00
|
|
|
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
|
|
|
|