This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/src/editconstraint.c
Joshua Leung 0b562ae340 == Child-Of Constraint ==
Now 'Set Inverse' works correctly for PoseChannels in all cases. Previously, it was still using a temporary hack that failed when 'parent' bone had any transforms.
2007-08-15 04:59:46 +00:00

1091 lines
28 KiB
C

/**
* $Id$
*
* ***** BEGIN GPL/BL DUAL 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. The Blender
* Foundation also sells licenses for use in proprietary software under
* the Blender License. See http://www.blender.org/BL/ for information
* about this.
*
* 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): none yet.
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
#include <stdio.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_arithb.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"
#include "BKE_depsgraph.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_ipo.h"
#include "BKE_object.h"
#include "BKE_utildefines.h"
#include "BIF_editaction.h"
#include "BIF_editarmature.h"
#include "BIF_editconstraint.h"
#include "BIF_poseobject.h"
#include "BIF_interface.h"
#include "BIF_screen.h"
#include "BIF_space.h"
#include "BIF_toolbox.h"
#include "BPY_extern.h"
#include "blendef.h"
#include "nla.h"
#include "mydevice.h"
ListBase *get_active_constraint_channels (Object *ob, int forcevalid)
{
char ipstr[64];
if (!ob)
return NULL;
/* See if we are a bone constraint */
if (ob->flag & OB_POSEMODE) {
bActionChannel *achan;
bPoseChannel *pchan;
pchan = get_active_posechannel(ob);
if (pchan) {
/* Make sure we have an action */
if (!ob->action){
if (!forcevalid)
return NULL;
ob->action=add_empty_action("Action");
}
/* Make sure we have an actionchannel */
achan = get_action_channel(ob->action, pchan->name);
if (!achan){
if (!forcevalid)
return NULL;
achan = MEM_callocN (sizeof(bActionChannel), "actionChannel");
strcpy (achan->name, pchan->name);
sprintf (ipstr, "%s.%s", ob->action->id.name+2, achan->name);
ipstr[23]=0;
achan->ipo= add_ipo(ipstr, ID_AC);
BLI_addtail (&ob->action->chanbase, achan);
}
return &achan->constraintChannels;
}
else return NULL;
}
/* else we return object constraints */
else {
if(ob->ipoflag & OB_ACTION_OB) {
bActionChannel *achan = get_action_channel(ob->action, "Object");
if(achan)
return &achan->constraintChannels;
else
return NULL;
}
return &ob->constraintChannels;
}
}
/* if object in posemode, active bone constraints, else object constraints */
ListBase *get_active_constraints(Object *ob)
{
if (!ob)
return NULL;
if (ob->flag & OB_POSEMODE) {
bPoseChannel *pchan;
pchan = get_active_posechannel(ob);
if (pchan)
return &pchan->constraints;
}
else
return &ob->constraints;
return NULL;
}
/* single constraint */
bConstraint *get_active_constraint(Object *ob)
{
ListBase *lb= get_active_constraints(ob);
if(lb) {
bConstraint *con;
for(con= lb->first; con; con=con->next)
if(con->flag & CONSTRAINT_ACTIVE)
return con;
}
return NULL;
}
/* single channel, for ipo */
bConstraintChannel *get_active_constraint_channel(Object *ob)
{
bConstraint *con;
bConstraintChannel *chan;
if (ob->flag & OB_POSEMODE) {
if(ob->action) {
bPoseChannel *pchan;
pchan = get_active_posechannel(ob);
if(pchan) {
for(con= pchan->constraints.first; con; con= con->next)
if(con->flag & CONSTRAINT_ACTIVE)
break;
if(con) {
bActionChannel *achan = get_action_channel(ob->action, pchan->name);
if(achan) {
for(chan= achan->constraintChannels.first; chan; chan= chan->next)
if(!strcmp(chan->name, con->name))
break;
return chan;
}
}
}
}
}
else {
for(con= ob->constraints.first; con; con= con->next)
if(con->flag & CONSTRAINT_ACTIVE)
break;
if(con) {
ListBase *lb= get_active_constraint_channels(ob, 0);
if(lb) {
for(chan= lb->first; chan; chan= chan->next)
if(!strcmp(chan->name, con->name))
break;
return chan;
}
}
}
return NULL;
}
bConstraint *add_new_constraint(short type)
{
bConstraint *con;
con = MEM_callocN(sizeof(bConstraint), "constraint");
/* Set up a generic constraint datablock */
con->type = type;
con->flag |= CONSTRAINT_EXPAND;
con->enforce=1.0F;
/* Load the data for it */
con->data = new_constraint_data(con->type);
strcpy (con->name, "Const");
return con;
}
void add_constraint_to_object(bConstraint *con, Object *ob)
{
ListBase *list;
list = &ob->constraints;
if (list) {
unique_constraint_name(con, list);
BLI_addtail(list, con);
con->flag |= CONSTRAINT_ACTIVE;
for(con= con->prev; con; con= con->prev)
con->flag &= ~CONSTRAINT_ACTIVE;
}
}
char *get_con_subtarget_name(bConstraint *con, Object *target)
{
/*
* If the target for this constraint is target, return a pointer
* to the name for this constraints subtarget ... NULL otherwise
*/
if (constraint_has_target(con)) {
Object *tar;
char *subtarget;
tar = get_constraint_target(con, &subtarget);
if (tar==target) return subtarget;
}
return NULL;
}
/* 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 */
/* Get the constraint list for this object */
if (strlen (substring)) {
switch (owner->type) {
case OB_ARMATURE:
type = TARGET_BONE;
break;
default:
type = TARGET_OBJECT;
break;
}
}
else
type = TARGET_OBJECT;
switch (type) {
case TARGET_OBJECT:
conlist = &owner->constraints;
break;
case TARGET_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) {
curcon->flag &= ~CONSTRAINT_DISABLE;
switch (curcon->type) {
case CONSTRAINT_TYPE_PYTHON:
{
bPythonConstraint *data = curcon->data;
float dummy_matrix[4][4];
/* is there are valid script? */
if (!data->text) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
else if (!BPY_is_pyconstraint(data->text)) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
data->flag &= ~PYCON_SCRIPTERROR;
/* does the constraint require target input? */
if (BPY_pyconstraint_targets(data, dummy_matrix))
data->flag |= PYCON_USETARGETS;
else
data->flag &= ~PYCON_USETARGETS;
/* check whether we have a valid target */
if (data->flag & PYCON_USETARGETS) {
/* validate target */
if (!exist_object(data->tar)) {
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
else {
/* don't hold onto target */
data->tar = NULL;
BLI_strncpy(data->subtarget, "", 32);
}
}
break;
case CONSTRAINT_TYPE_ACTION:
{
bActionConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_LOCLIKE:
{
bLocateLikeConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_MINMAX:
{
bMinMaxConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_ROTLIKE:
{
bRotateLikeConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_SIZELIKE:
{
bSizeLikeConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_KINEMATIC:
{
bKinematicConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_TRACKTO:
{
bTrackToConstraint *data = curcon->data;
if (!exist_object(data->tar)) {
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if (data->reserved2==data->reserved1){
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if (data->reserved2+3==data->reserved1){
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_LOCKTRACK:
{
bLockTrackConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if (data->lockflag==data->trackflag){
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if (data->lockflag+3==data->trackflag){
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_STRETCHTO:
{
bStretchToConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_FOLLOWPATH:
{
bFollowPathConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if (data->tar->type != OB_CURVE){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if (data->upflag==data->trackflag){
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if (data->upflag+3==data->trackflag){
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
case CONSTRAINT_TYPE_CLAMPTO:
{
bClampToConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if (data->tar->type != OB_CURVE){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
else {
Curve *cu= data->tar->data;
/* auto-set 'Path' setting on curve so this works */
cu->flag |= CU_PATH;
}
}
break;
case CONSTRAINT_TYPE_TRANSFORM:
{
bTransformConstraint *data = curcon->data;
if (!exist_object(data->tar)){
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
if ( (data->tar == owner) &&
(!get_named_bone(get_armature(owner),
data->subtarget))) {
curcon->flag |= CONSTRAINT_DISABLE;
break;
}
}
break;
}
}
}
}
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;
arm = get_armature(owner);
if (arm)
test_bonelist_constraints (owner, &arm->bonebase);
}
}
/* context: active object in posemode, active channel, optional selected channel */
void add_constraint(int only_IK)
{
Object *ob= OBACT, *obsel=NULL;
bPoseChannel *pchanact=NULL, *pchansel=NULL;
bConstraint *con=NULL;
Base *base;
short nr;
/* paranoia checks */
if(ob==NULL || ob==G.obedit) return;
if(ob->pose && (ob->flag & OB_POSEMODE)) {
bArmature *arm= ob->data;
/* find active channel */
pchanact= get_active_posechannel(ob);
if(pchanact==NULL) return;
/* check protection */
if(ob->proxy && (pchanact->bone->layer & arm->layer_protected)) {
error("Bone is Proxy protected");
return;
}
/* find selected bone */
for(pchansel= ob->pose->chanbase.first; pchansel; pchansel= pchansel->next) {
if(pchansel!=pchanact)
if(pchansel->bone->flag & BONE_SELECTED)
if(pchansel->bone->layer & arm->layer)
break;
}
}
/* find selected object */
for(base= FIRSTBASE; base; base= base->next)
if( TESTBASE(base) && base->object!=ob )
obsel= base->object;
/* the only_IK caller has checked for posemode! */
if(only_IK) {
for(con= pchanact->constraints.first; con; con= con->next) {
if(con->type==CONSTRAINT_TYPE_KINEMATIC) break;
}
if(con) {
error("Pose Channel already has IK");
return;
}
if(pchansel)
nr= pupmenu("Add IK Constraint%t|To Active Bone%x10");
else if(obsel)
nr= pupmenu("Add IK Constraint%t|To Active Object%x10");
else
nr= pupmenu("Add IK Constraint%t|To New Empty Object%x10|Without Target%x11");
}
else {
if(pchanact) {
if(pchansel)
nr= pupmenu("Add Constraint to Active Bone%t|Child Of%x19|Transformation%x20|%l|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7|%l|Action%x16|Script%x18");
else if(obsel && obsel->type==OB_CURVE)
nr= pupmenu("Add Constraint to Active Object%t|Child Of%x19|Transformation%x20|%l|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Follow Path%x6|Clamp To%x17|Stretch To%x7|%l|Action%x16|Script%x18");
else if(obsel)
nr= pupmenu("Add Constraint to Active Object%t|Child Of%x19|Transformation%x20|%l|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7|%l|Action%x16|Script%x18");
else
nr= pupmenu("Add Constraint to New Empty Object%t|Child Of%x19|Transformation%x20|%l|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Stretch To%x7|%l|Script%x18");
}
else {
if(obsel && obsel->type==OB_CURVE)
nr= pupmenu("Add Constraint to Active Object%t|Child Of%x19|Transformation%x20|%l|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|Follow Path%x6|Clamp To%x17|%l|Script%x18");
else if(obsel)
nr= pupmenu("Add Constraint to Active Object%t|Child Of%x19|Transformation%x20|%l|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|%l|Script%x18");
else
nr= pupmenu("Add Constraint to New Empty Object%t|Child Of%x19|Transformation%x20|%l|Copy Location%x1|Copy Rotation%x2|Copy Scale%x8|%l|Limit Location%x13|Limit Rotation%x14|Limit Scale%x15|%l|Track To%x3|Floor%x4|Locked Track%x5|%l|Script%x18");
}
}
if(nr<1) return;
/* handle IK separate */
if(nr==10 || nr==11) {
/* prevent weird chains... */
if(pchansel) {
bPoseChannel *pchan= pchanact;
while(pchan) {
if(pchan==pchansel) break;
pchan= pchan->parent;
}
if(pchan) {
error("IK root cannot be linked to IK tip");
return;
}
pchan= pchansel;
while(pchan) {
if(pchan==pchanact) break;
pchan= pchan->parent;
}
if(pchan) {
error("IK tip cannot be linked to IK root");
return;
}
}
con = add_new_constraint(CONSTRAINT_TYPE_KINEMATIC);
BLI_addtail(&pchanact->constraints, con);
unique_constraint_name(con, &pchanact->constraints);
pchanact->constflag |= PCHAN_HAS_IK; // for draw, but also for detecting while pose solving
if(nr==11) pchanact->constflag |= PCHAN_HAS_TARGET;
}
else {
if (nr==1) con = add_new_constraint(CONSTRAINT_TYPE_LOCLIKE);
else if (nr==2) con = add_new_constraint(CONSTRAINT_TYPE_ROTLIKE);
else if (nr==3) con = add_new_constraint(CONSTRAINT_TYPE_TRACKTO);
else if (nr==4) con = add_new_constraint(CONSTRAINT_TYPE_MINMAX);
else if (nr==5) con = add_new_constraint(CONSTRAINT_TYPE_LOCKTRACK);
else if (nr==6) {
Curve *cu= obsel->data;
cu->flag |= CU_PATH;
con = add_new_constraint(CONSTRAINT_TYPE_FOLLOWPATH);
}
else if (nr==7) con = add_new_constraint(CONSTRAINT_TYPE_STRETCHTO);
else if (nr==8) con = add_new_constraint(CONSTRAINT_TYPE_SIZELIKE);
else if (nr==13) con = add_new_constraint(CONSTRAINT_TYPE_LOCLIMIT);
else if (nr==14) con = add_new_constraint(CONSTRAINT_TYPE_ROTLIMIT);
else if (nr==15) con = add_new_constraint(CONSTRAINT_TYPE_SIZELIMIT);
else if (nr==16) {
/* TODO: add a popup-menu to display list of available actions to use (like for pyconstraints) */
con = add_new_constraint(CONSTRAINT_TYPE_ACTION);
}
else if (nr==17) {
Curve *cu= obsel->data;
cu->flag |= CU_PATH;
con = add_new_constraint(CONSTRAINT_TYPE_CLAMPTO);
}
else if (nr==18) {
char *menustr;
int scriptint= 0, dummy_int=0;
float dummy_matrix[4][4];
/* popup a list of usable scripts */
menustr = buildmenu_pyconstraints(NULL, &dummy_int);
scriptint = pupmenu(menustr);
MEM_freeN(menustr);
/* only add constraint if a script was chosen */
if (scriptint) {
/* add constraint */
con = add_new_constraint(CONSTRAINT_TYPE_PYTHON);
validate_pyconstraint_cb(con->data, &scriptint);
/* make sure target allowance is set correctly */
dummy_int = BPY_pyconstraint_targets(con->data, dummy_matrix);
if (dummy_int) {
bPythonConstraint *pycon= (bPythonConstraint *)con->data;
pycon->flag |= PYCON_USETARGETS;
}
}
}
else if (nr==19) {
con = add_new_constraint(CONSTRAINT_TYPE_CHILDOF);
/* if this constraint is being added to a posechannel, make sure
* the constraint gets evaluated in pose-space
*/
if (pchanact) {
con->ownspace = CONSTRAINT_SPACE_POSE;
con->flag |= CONSTRAINT_SPACEONCE;
}
}
else if (nr==20) con = add_new_constraint(CONSTRAINT_TYPE_TRANSFORM);
if (con==NULL) return; /* paranoia */
if (pchanact) {
BLI_addtail(&pchanact->constraints, con);
unique_constraint_name(con, &pchanact->constraints);
pchanact->constflag |= PCHAN_HAS_CONST; /* for draw */
}
else {
BLI_addtail(&ob->constraints, con);
unique_constraint_name(con, &ob->constraints);
}
}
/* set the target */
if (pchansel) {
set_constraint_target(con, ob, pchansel->name);
}
else if(obsel) {
set_constraint_target(con, obsel, NULL);
}
else if (ELEM4(nr, 11, 13, 14, 15)==0) { /* add new empty as target */
Base *base= BASACT, *newbase;
Object *obt;
obt= add_object(OB_EMPTY);
/* set layers OK */
newbase= BASACT;
newbase->lay= base->lay;
obt->lay= newbase->lay;
/* transform cent to global coords for loc */
if (pchanact) {
if(only_IK)
VecMat4MulVecfl(obt->loc, ob->obmat, pchanact->pose_tail);
else
VecMat4MulVecfl(obt->loc, ob->obmat, pchanact->pose_head);
}
else
VECCOPY(obt->loc, ob->obmat[3]);
set_constraint_target(con, obt, NULL);
/* restore, add_object sets active */
BASACT= base;
base->flag |= SELECT;
}
/* active flag */
con->flag |= CONSTRAINT_ACTIVE;
for(con= con->prev; con; con= con->prev)
con->flag &= ~CONSTRAINT_ACTIVE;
DAG_scene_sort(G.scene); // sort order of objects
if (pchanact) {
ob->pose->flag |= POSE_RECALC; // sort pose channels
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); // and all its relations
}
else
DAG_object_flush_update(G.scene, ob, OB_RECALC_OB); // and all its relations
allqueue (REDRAWVIEW3D, 0);
allqueue (REDRAWBUTSOBJECT, 0);
allqueue (REDRAWOOPS, 0);
if (only_IK)
BIF_undo_push("Add IK Constraint");
else
BIF_undo_push("Add Constraint");
}
void ob_clear_constraints(void)
{
Object *ob= OBACT;
/* paranoia checks */
if(!ob) return;
if(ob==G.obedit || (ob->flag & OB_POSEMODE)) return;
if(okee("Clear Constraints")==0) return;
free_constraints(&ob->constraints);
DAG_object_flush_update(G.scene, ob, OB_RECALC_OB);
allqueue (REDRAWVIEW3D, 0);
allqueue (REDRAWBUTSOBJECT, 0);
allqueue (REDRAWOOPS, 0);
BIF_undo_push("Clear Constraint(s)");
}
/* con already has the new name */
void rename_constraint(Object *ob, bConstraint *con, char *oldname)
{
bConstraint *tcon;
bConstraintChannel *conchan;
ListBase *conlist= NULL;
int from_object= 0;
char *channame="";
/* get context by searching for con (primitive...) */
for(tcon= ob->constraints.first; tcon; tcon= tcon->next)
if(tcon==con)
break;
if(tcon) {
conlist= &ob->constraints;
channame= "Object";
from_object= 1;
}
else if(ob->pose) {
bPoseChannel *pchan;
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
for(tcon= pchan->constraints.first; tcon; tcon= tcon->next) {
if(tcon==con)
break;
}
if(tcon)
break;
}
if(tcon) {
conlist= &pchan->constraints;
channame= pchan->name;
}
}
if(conlist==NULL) {
printf("rename constraint failed\n"); /* should not happen in UI */
return;
}
/* first make sure it's a unique name within context */
unique_constraint_name (con, conlist);
/* own channels */
if(from_object) {
for(conchan= ob->constraintChannels.first; conchan; conchan= conchan->next) {
if( strcmp(oldname, conchan->name)==0 )
BLI_strncpy(conchan->name, con->name, sizeof(conchan->name));
}
}
/* own action */
if(ob->action) {
bActionChannel *achan= get_action_channel(ob->action, channame);
if(achan) {
conchan= get_constraint_channel(&achan->constraintChannels, oldname);
if(conchan)
BLI_strncpy(conchan->name, con->name, sizeof(conchan->name));
}
}
}
/* ********************** CONSTRAINT-SPECIFIC STUFF ********************* */
/* ------------- 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;
int index = *((int *)arg2);
int i;
/* 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;
}
/* this returns a string for the list of usable pyconstraint script names */
char *buildmenu_pyconstraints(Text *con_text, int *pyconindex)
{
Text *text;
char *menustr = MEM_callocN(128, "menustr pyconstraints");
char *name, stmp[128];
int buf = 128;
int used = strlen("Scripts: %t") + 1;
int i;
sprintf(menustr, "%s", "Scripts: %t");
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;
/* menu entry is length of name + 3(len(|%X)) + 6 characters for the int.*/
if (BPY_is_pyconstraint(text)) {
name= text->id.name;
if (strlen(name)+used+10 >= buf) {
char *newbuf = MEM_callocN(buf+128, "menustr pyconstraints 2");
memcpy(newbuf, menustr, used);
MEM_freeN(menustr);
menustr = newbuf;
buf += 128;
}
sprintf(stmp, "|%s%%x%d", name, i);
strcat(menustr, stmp);
used += strlen(name)+10;
}
}
return menustr;
}
/* ------------- Child-Of Constraint ------------------ */
/* ChildOf Constraint - set inverse callback */
void childof_const_setinv (void *conv, void *unused)
{
bChildOfConstraint *data= (bChildOfConstraint *)conv;
Object *ob= OBACT;
bPoseChannel *pchan= NULL;
/* try to find a pose channel */
if (ob && ob->pose)
pchan= get_active_posechannel(ob);
/* calculate/set inverse matrix */
if (pchan) {
bConstraintOb *cob;
float ctime= bsystem_time(ob, (float)G.scene->r.cfra, 0.0); /* not accurate... */
float pmat[4][4], chmat[4][4], cimat[4][4];
float vec0[3]={0,0,0}, vec1[3]={1,1,1};
/* make copies of pchan's original matrices (to be restored later) */
Mat4CpyMat4(pmat, pchan->pose_mat);
Mat4CpyMat4(chmat, pchan->chan_mat);
Mat4CpyMat4(cimat, pchan->constinv);
/* clear pchan's transform (for constraint solving) */
LocEulSizeToMat4(pchan->chan_mat, vec0, vec0, vec1);
Mat4MulMat4(pchan->pose_mat, pmat, cimat);
Mat4One(pchan->constinv);
Mat4One(data->invmat);
/* do constraint solving on pose-matrix containing no transforms
* N.B. code is copied from armature.c (where_is_pose_bone)
*/
cob= constraints_make_evalob(ob, pchan, TARGET_BONE);
solve_constraints(&pchan->constraints, cob, ctime);
constraints_clear_evalob(cob);
/* parent-inverse matrix for this constraint is given by taking the
* local-space (i.e. without any standard parents + restpose) pose_matrix
* (that was calulated with no transforms applied), and inverting it.
*/
Mat4CpyMat4(pchan->constinv, pchan->pose_mat);
constraint_mat_convertspace(ob, pchan, pchan->constinv,
CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL);
Mat4Invert(data->invmat, pchan->constinv);
/* restore original matrices of pchan */
Mat4CpyMat4(pchan->pose_mat, pmat);
Mat4CpyMat4(pchan->chan_mat, chmat);
Mat4CpyMat4(pchan->constinv, cimat);
}
else if (ob) {
/* 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(ob);
Mat4Invert(data->invmat, workob.obmat);
}
else
Mat4One(data->invmat);
}
/* ChildOf Constraint - clear inverse callback */
void childof_const_clearinv (void *conv, void *unused)
{
bChildOfConstraint *data= (bChildOfConstraint *)conv;
/* simply clear the matrix */
Mat4One(data->invmat);
}