Files
test2/source/blender/src/editconstraint.c
Martin Poirier ea815d51ce All constraints (except FollowPath) now check if subtarget (bone) is valid and disable constraint if not. Previously, this only worked with IK Solver.
The problem was that it defaulted to the armature object as target but didn't clear the subtarget field. This makes it clear that there's a problem.
2004-09-05 21:20:03 +00:00

909 lines
21 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 "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 <config.h>
#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;
}