/** * $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 #include #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "BKE_utildefines.h" #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_object.h" #include "BKE_global.h" #include "BKE_constraint.h" #include "BKE_ipo.h" #include "BIF_editarmature.h" #include "BIF_editconstraint.h" #include "BIF_interface.h" #include "BIF_screen.h" #include "BIF_toolbox.h" #include "BSE_editaction.h" #include "blendef.h" #include "nla.h" #ifdef HAVE_CONFIG_H #include #endif static short add_constraint_element (Object *owner, const char *substring, Object *parent, const char *parentstring); static short detect_constraint_loop (Object *owner, const char* substring, int disable, char type); static void test_bonelist_constraints (Object *owner, ListBase *list); static void clear_object_constraint_loop_flags(Object *ob); //static int is_child_of(struct Object *owner, struct Object *parent); //static int is_bonechild_of(struct Bone *bone, struct Bone *parent); static int is_child_of_ex(Object *owner, const char *ownersubstr, Object *parent, const char *parsubstr); ListBase g_conBase; const char *g_conString; Object *g_conObj; static int is_child_of_ex(Object *owner, const char *ownersubstr, Object *parent, const char *parsubstr) { Object *curob; Bone *bone = NULL; Bone *parbone= NULL; curob=owner; /* If this is a bone */ if (strlen(ownersubstr)) bone = get_named_bone(get_armature(owner->parent), ownersubstr); if (strlen(parsubstr)) parbone = get_named_bone(get_armature(parent), parsubstr); /* Traverse the scene graph */ while (curob && !bone){ switch (curob->partype){ case PARBONE: if (strlen(parsubstr)){ bone = get_named_bone(get_armature(curob->parent), curob->parsubstr); break; } /* The break is supposed to be missing */ default: if (curob==parent){ if (parbone) return 0; else return 1; } } curob=curob->parent; } /* Descend into the armature scene graph */ while (bone){ if (bone==parbone) return 1; bone=bone->parent; } return 0; } /* static int is_child_of(Object *owner, Object *parent) { Object *curpar; for (curpar = owner->parent; curpar; curpar=curpar->parent){ if (curpar==parent) return 1; } return 0; } static int is_bonechild_of(Bone *bone, Bone *parent) { Bone *curpar; if (!bone) return 0; for (curpar = bone->parent; curpar; curpar=curpar->parent){ if (curpar==parent) return 1; } return 0; } */ static short add_constraint_element (Object *owner, const char *substring, Object *parent, const char *parentstring) { if (!owner) return 0; /* See if this is the original object */ if (parent == owner){ if (!strcmp (parentstring, substring)) return 1; } if (owner == g_conObj){ if (!strcmp (g_conString, substring)) return 1; } /* See if this is a child of the adding object */ if (parent){ // if (is_child_of (owner, parent)) if (is_child_of_ex (owner, substring, parent, parentstring)) return 1; /* Parent is a bone */ /* if ((owner==parent) && (owner->type == OB_ARMATURE)){ if (strlen (substring) && strlen(parentstring)){ if (is_bonechild_of(get_named_bone(owner->data, substring), get_named_bone(parent->data, parentstring))) return 1; } } */ } return 0; } static void test_bonelist_constraints (Object *owner, ListBase *list) { Bone *bone; Base *base1; for (bone = list->first; bone; bone=bone->next){ for (base1 = G.scene->base.first; base1; base1=base1->next){ clear_object_constraint_loop_flags(base1->object); } test_constraints(owner, bone->name, 1); test_bonelist_constraints (owner, &bone->childbase); } } static void clear_object_constraint_loop_flags(Object *ob) { bConstraint *con; if (!ob) return; /* Test object constraints */ for (con = ob->constraints.first; con; con=con->next){ con->flag &= ~CONSTRAINT_LOOPTESTED; } switch (ob->type){ case OB_ARMATURE: if (ob->pose){ bPoseChannel *pchan; for (pchan = ob->pose->chanbase.first; pchan; pchan=pchan->next){ for (con = pchan->constraints.first; con; con=con->next){ con->flag &= ~CONSTRAINT_LOOPTESTED; } } } break; default: break; } } void test_scene_constraints (void) { Base *base, *base1; /* Clear the "done" flags of all constraints */ for (base = G.scene->base.first; base; base=base->next){ clear_object_constraint_loop_flags(base->object); } /* Test all constraints */ for (base = G.scene->base.first; base; base=base->next){ /* Test the object */ for (base1 = G.scene->base.first; base1; base1=base1->next) clear_object_constraint_loop_flags(base1->object); test_constraints (base->object, "", 1); /* Test the subobject constraints */ switch (base->object->type){ case OB_ARMATURE: { bArmature *arm; arm = get_armature(base->object); if (arm) test_bonelist_constraints (base->object, &arm->bonebase); } break; default: break; } } } int test_constraints (Object *owner, const char *substring, int disable) { /* init_constraint_elements();*/ g_conObj = owner; g_conString = substring; if (detect_constraint_loop (owner, substring, disable, 0)) return 1; else return 0; /* free_constraint_elements(); */ } static short detect_constraint_loop (Object *owner, const char* substring, int disable, char typefrom) { bConstraint *curcon; ListBase *conlist; int type; int result = 0; if (!owner) return result; /* 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; /* Check parents */ #if 0 if (owner->parent && (ELEM (owner->partype, PAROBJECT, PARBONE))){ if (add_constraint_element (owner->parent, "", NULL, NULL)){ return 1; } /* if (detect_constraint_loop (owner->parent, "", disable)){ return 1; } */ } /* Check tracking */ if (owner->track && (ELEM (owner->partype, PAROBJECT, PARBONE))){ if (add_constraint_element (owner->track, "", NULL, NULL)){ return 1; } /* if (detect_constraint_loop (owner->track, "", disable)){ return 1; } */ } #else if (owner->parent && (owner->partype==PAROBJECT)) if (add_constraint_element (owner->parent, "", NULL, NULL)) return 1; if (owner->parent && (owner->partype==PARBONE)) if (add_constraint_element (owner->parent, owner->parsubstr, NULL, NULL)) return 1; /* Check tracking */ if (owner->track) if (add_constraint_element (owner->track, "", NULL, NULL)) return 1; #endif 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){ conlist = &chan->constraints; if (bone->parent){ if (add_constraint_element (owner, bone->parent->name, NULL, NULL)) return 1; if (detect_constraint_loop (owner, bone->parent->name, disable, 0)) return 1; } else{ if (add_constraint_element (owner, "", NULL, NULL)) return 1; if (detect_constraint_loop (owner, "", disable, 0)) return 1; } } else conlist = NULL; } break; default: conlist = NULL; break; } /* Cycle constraints */ if (conlist){ for (curcon = conlist->first; curcon; curcon=curcon->next){ /* Clear the disable flag */ if (curcon->flag & CONSTRAINT_LOOPTESTED){ return 0; } else { curcon->flag &= ~CONSTRAINT_DISABLE; curcon->flag |= CONSTRAINT_LOOPTESTED; switch (curcon->type){ case CONSTRAINT_TYPE_ACTION: { bActionConstraint *data = curcon->data; if (!exist_object(data->tar)){ data->tar = NULL; break; } if ( (data->tar == owner) && (!get_named_bone(get_armature(owner), data->subtarget))) { curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; } if (add_constraint_element (data->tar, data->subtarget, owner, substring)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (detect_constraint_loop (data->tar, data->subtarget, disable, CONSTRAINT_TYPE_ACTION)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } break; case CONSTRAINT_TYPE_LOCLIKE: { bLocateLikeConstraint *data = curcon->data; if (!exist_object(data->tar)){ data->tar = NULL; break; } if ( (data->tar == owner) && (!get_named_bone(get_armature(owner), data->subtarget))) { curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; } if (add_constraint_element (data->tar, data->subtarget, owner, substring)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (detect_constraint_loop (data->tar, data->subtarget, disable, CONSTRAINT_TYPE_LOCLIKE)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } break; case CONSTRAINT_TYPE_ROTLIKE: { bRotateLikeConstraint *data = curcon->data; if (!exist_object(data->tar)){ data->tar = NULL; break; } if ( (data->tar == owner) && (!get_named_bone(get_armature(owner), data->subtarget))) { curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; } if (add_constraint_element (data->tar, data->subtarget, owner, substring)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (detect_constraint_loop (data->tar, data->subtarget, disable, CONSTRAINT_TYPE_ROTLIKE)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } break; case CONSTRAINT_TYPE_KINEMATIC: { bKinematicConstraint *data = curcon->data; if (!exist_object(data->tar)){ data->tar = NULL; break; } if ( (data->tar == owner) && (!get_named_bone(get_armature(owner), data->subtarget))) { curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; } if (add_constraint_element (data->tar, data->subtarget, owner, substring)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (detect_constraint_loop (data->tar, data->subtarget, disable, CONSTRAINT_TYPE_KINEMATIC)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } break; case CONSTRAINT_TYPE_TRACKTO: { bTrackToConstraint *data = curcon->data; if (!exist_object(data->tar)) { data->tar = NULL; break; } if ( (data->tar == owner) && (!get_named_bone(get_armature(owner), data->subtarget))) { curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; } if (typefrom != CONSTRAINT_TYPE_TRACKTO && typefrom != CONSTRAINT_TYPE_LOCKTRACK){ if (add_constraint_element (data->tar, data->subtarget, owner, substring)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } else { curcon->flag |= CONSTRAINT_NOREFRESH; } if (detect_constraint_loop (data->tar, data->subtarget, disable, CONSTRAINT_TYPE_TRACKTO)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (data->reserved2==data->reserved1){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (data->reserved2+3==data->reserved1){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } break; case CONSTRAINT_TYPE_LOCKTRACK: { bLockTrackConstraint *data = curcon->data; if (!exist_object(data->tar)){ data->tar = NULL; break; } if ( (data->tar == owner) && (!get_named_bone(get_armature(owner), data->subtarget))) { curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; } if (typefrom != CONSTRAINT_TYPE_TRACKTO && typefrom != CONSTRAINT_TYPE_LOCKTRACK){ if (add_constraint_element (data->tar, data->subtarget, owner, substring)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } else { curcon->flag |= CONSTRAINT_NOREFRESH; } if (detect_constraint_loop (data->tar, data->subtarget, disable, CONSTRAINT_TYPE_LOCKTRACK)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (data->lockflag==data->trackflag){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (data->lockflag+3==data->trackflag){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } break; case CONSTRAINT_TYPE_STRETCHTO: { bStretchToConstraint *data = curcon->data; if (!exist_object(data->tar)){ data->tar = NULL; break; } if ( (data->tar == owner) && (!get_named_bone(get_armature(owner), data->subtarget))) { curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; } if (detect_constraint_loop (data->tar, data->subtarget, disable, CONSTRAINT_TYPE_LOCKTRACK)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } break; case CONSTRAINT_TYPE_FOLLOWPATH: { bFollowPathConstraint *data = curcon->data; if (!exist_object(data->tar)){ data->tar = NULL; break; } if (data->tar->type != OB_CURVE){ data->tar = NULL; break; } if (add_constraint_element (data->tar, "", owner, substring)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (detect_constraint_loop (data->tar, "", disable, CONSTRAINT_TYPE_FOLLOWPATH)){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (data->upflag==data->trackflag){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } if (data->upflag+3==data->trackflag){ curcon->flag |= CONSTRAINT_DISABLE; result = 1; break; // return 1; } } break; } } } } return result; } ListBase *get_constraint_client_channels (int forcevalid) { Object *ob; char ipstr[64]; ob=OBACT; if (!ob) return NULL; /* See if we are a bone constraint */ if (G.obpose){ switch (G.obpose->type){ case OB_ARMATURE: { bActionChannel *achan; Bone *bone; bone = get_first_selected_bone(); if (!bone) break; /* Make sure we have an action */ if (!G.obpose->action){ if (!forcevalid) return NULL; G.obpose->action=add_empty_action(); } /* Make sure we have an actionchannel */ achan = get_named_actionchannel(G.obpose->action, bone->name); if (!achan){ if (!forcevalid) return NULL; achan = MEM_callocN (sizeof(bActionChannel), "actionChannel"); strcpy (achan->name, bone->name); sprintf (ipstr, "%s.%s", G.obpose->action->id.name+2, achan->name); ipstr[23]=0; achan->ipo= add_ipo(ipstr, ID_AC); BLI_addtail (&G.obpose->action->chanbase, achan); } return &achan->constraintChannels; } } } return &ob->constraintChannels; } ListBase *get_constraint_client(char *name, short *clientType, void **clientdata) { Object *ob; ListBase *list; ob=OBACT; if (clientType) *clientType = -1; if (!ob) return NULL; list = &ob->constraints; /* Prep the object's constraint channels */ if (clientType) *clientType = TARGET_OBJECT; if (name) strcpy (name, ob->id.name+2); if (G.obpose){ switch (G.obpose->type){ case OB_ARMATURE: { Bone *bone; bone = get_first_selected_bone(); if (!bone) break; { bPoseChannel *chan; /* Is the bone the client? */ if (clientType) *clientType = TARGET_BONE; if (clientdata) *clientdata = bone; if (name) sprintf (name, "%s>>%s", name, bone->name); chan = verify_pose_channel(G.obpose->pose, bone->name); list = &chan->constraints; } } break; } } return list; } bConstraint * add_new_constraint(char 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); } } void add_constraint_to_client(bConstraint *con) { ListBase *list; short type; list = get_constraint_client(NULL, &type, NULL); if (list) { unique_constraint_name(con, list); BLI_addtail(list, con); } } bConstraintChannel *add_new_constraint_channel(const char* name) { bConstraintChannel *chan = NULL; chan = MEM_callocN(sizeof(bConstraintChannel), "constraintChannel"); strcpy(chan->name, name); return chan; } void add_influence_key_to_constraint (bConstraint *con){ printf("doesn't do anything yet\n"); } char *get_con_subtarget_name(bConstraint *constraint, Object *target) { /* * If the target for this constraint is target, return a pointer * to the name for this constraints subtarget ... NULL otherwise */ switch (constraint->type) { case CONSTRAINT_TYPE_ACTION: { bActionConstraint *data = constraint->data; if (data->tar==target) return data->subtarget; } break; case CONSTRAINT_TYPE_LOCLIKE: { bLocateLikeConstraint *data = constraint->data; if (data->tar==target) return data->subtarget; } break; case CONSTRAINT_TYPE_ROTLIKE: { bRotateLikeConstraint *data = constraint->data; if (data->tar==target) return data->subtarget; } break; case CONSTRAINT_TYPE_KINEMATIC: { bKinematicConstraint *data = constraint->data; if (data->tar==target) return data->subtarget; } break; case CONSTRAINT_TYPE_TRACKTO: { bTrackToConstraint *data = constraint->data; if (data->tar==target) return data->subtarget; } break; case CONSTRAINT_TYPE_LOCKTRACK: { bLockTrackConstraint *data = constraint->data; if (data->tar==target) return data->subtarget; } break; case CONSTRAINT_TYPE_STRETCHTO: { bStretchToConstraint *data = constraint->data; if (data->tar==target) return data->subtarget; } break; case CONSTRAINT_TYPE_FOLLOWPATH: /* wonder if this is relevent, since this constraint * cannot have a subtarget - theeth */ { /* * bFollowPathConstraint *data = constraint->data; */ return NULL; } break; } return NULL; }