From 9f009558bf12aef4fe6fca3a32493f12150575e6 Mon Sep 17 00:00:00 2001 From: Chris Want Date: Sun, 4 Jan 2004 03:39:06 +0000 Subject: [PATCH] A few related bug-fixes/refinements * A patch to make sure that constraints on bones that point to external objects are evaluated correctly (and that the bones that depend on these bones are evaluated correctly, etc, ad nauseum). This addresses some of intrr's issues (the blender-related ones, that is). * Make sure that deformed displists are updated when the user manipulates any of the constraint buttons. * Added a nice little function, ik_chain_looper(), that executes a callback for every bone in an IK chain. --- source/blender/include/BIF_editarmature.h | 3 +- source/blender/makesdna/DNA_object_types.h | 5 + source/blender/src/buttons_object.c | 5 +- source/blender/src/editarmature.c | 37 ++++ source/blender/src/editobject.c | 234 ++++++++++++++------- 5 files changed, 210 insertions(+), 74 deletions(-) diff --git a/source/blender/include/BIF_editarmature.h b/source/blender/include/BIF_editarmature.h index 5d40f71b851..df6d74cd036 100644 --- a/source/blender/include/BIF_editarmature.h +++ b/source/blender/include/BIF_editarmature.h @@ -119,7 +119,8 @@ void show_all_pose_bones(void); int bone_looper(Object *ob, struct Bone *bone, void *data, int (*bone_func)(Object *, struct Bone *, void *)); - +int ik_chain_looper(Object *ob, struct Bone *bone, void *data, + int (*bone_func)(Object *, struct Bone *, void *)); int is_delay_deform(void); #define BONESEL_TIP 0x08000000 diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 32be260b450..c5fa7aeafd5 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -176,6 +176,11 @@ extern Object workob; /* used many places... should be specialized */ #define SELECT 1 #define ACTIVE 2 +#define GONNA_MOVE 32 /* temporary, for figuring what stuff needs + * updating during transform due to parenting + * or constraints... do not rely on this value + * unless you are me (CW). + */ /* type */ #define OB_EMPTY 0 diff --git a/source/blender/src/buttons_object.c b/source/blender/src/buttons_object.c index 54afa9caf66..393131b4b6b 100644 --- a/source/blender/src/buttons_object.c +++ b/source/blender/src/buttons_object.c @@ -731,8 +731,6 @@ static uiBlock *add_constraintmenu(void *arg_unused) void do_constraintbuts(unsigned short event) { - clear_object_constraint_status(OBACT); - switch(event) { case B_CONSTRAINT_CHANGENAME: break; @@ -860,6 +858,9 @@ void do_constraintbuts(unsigned short event) default: break; } + + clear_object_constraint_status(OBACT); + make_displists_by_armature (OBACT); } static void object_panel_constraint(void) diff --git a/source/blender/src/editarmature.c b/source/blender/src/editarmature.c index 0b8e96bd858..dcfd2f6838a 100644 --- a/source/blender/src/editarmature.c +++ b/source/blender/src/editarmature.c @@ -2508,6 +2508,43 @@ int bone_looper(Object *ob, Bone *bone, void *data, return count; } +int ik_chain_looper(Object *ob, Bone *bone, void *data, + int (*bone_func)(Object *, Bone *, void *)) { + + /* We want to apply the function bone_func to every bone + * in an ik chain -- feed ikchain_looper a bone in the chain and + * a pointer to the bone_func and watch it go!. The int count + * can be useful for counting bones with a certain property + * (e.g. skinnable) + */ + Bone *curBone; + int count = 0; + + if (bone) { + + /* This bone */ + count += bone_func(ob, bone, data); + + /* The parents */ + for (curBone = bone; curBone; curBone=curBone->parent) { + if (!curBone->parent) + break; + else if (!(curBone->flag & BONE_IK_TOPARENT)) + break; + count += bone_func(ob, curBone->parent, data); + } + + /* The children */ + for (curBone = bone->childbase.first; curBone; curBone=curBone->next){ + if (curBone->flag & BONE_IK_TOPARENT) { + count += bone_func(ob, curBone, data); + } + } + } + + return count; +} + int bone_skinnable(Object *ob, Bone *bone, void *data) { /* Bones that are not of boneclass BONE_UNSKINNABLE diff --git a/source/blender/src/editobject.c b/source/blender/src/editobject.c index 9ebd3898e20..398ae297a4a 100644 --- a/source/blender/src/editobject.c +++ b/source/blender/src/editobject.c @@ -2638,37 +2638,6 @@ static int is_ob_constraint_target(Object *ob, ListBase *conlist) { } -static int pose_do_update_flag(Object *ob) { - /* Figure out which pose channels need constant updating. - */ - Base *base; - bPoseChannel *chan; - int do_update = 0; - - if (ob->pose) { - for (chan = ob->pose->chanbase.first; chan; chan=chan->next){ - if (chan->constraints.first) { - for (base= FIRSTBASE; base; base= base->next) { - if (is_ob_constraint_target(base->object, - &chan->constraints)) { - if( (base->flag & SELECT) || (ob->flag & SELECT)) { - /* If this armature is selected, or if the - * object that is the target of a constraint - * is selected, then lets constantly update - * this pose channel. - */ - chan->flag |= PCHAN_TRANS_UPDATE; - do_update = 1; - } - } - } - } - } - } - - return do_update; -} - int clear_bone_nocalc(Object *ob, Bone *bone, void *ptr) { /* When we aren't transform()-ing, we'll want to turn off * the no calc flag for bone bone in case the frame changes, @@ -2737,31 +2706,6 @@ static int is_ik_root_docalc(Bone *bone) { return 0; } -static void ik_chain_docalc(Bone *bone) { - /* Let's clear the no calc flag for an entire IK chain - */ - Bone *curBone; - - /* This bone */ - bone->flag &= ~BONE_NOCALC; - - /* The parents */ - for (curBone = bone; curBone; curBone=curBone->parent) { - if (!curBone->parent) - break; - else if (!(curBone->flag & BONE_IK_TOPARENT)) - break; - curBone->parent->flag &= ~BONE_NOCALC; - } - - /* The children */ - for (curBone = bone->childbase.first; curBone; curBone=curBone->next){ - if (curBone->flag & BONE_IK_TOPARENT) { - curBone->flag &= ~BONE_NOCALC; - } - } -} - static void figure_bone_nocalc_constraint(Bone *conbone, bConstraint *con, Object *ob, bArmature *arm) { /* If this bone has a constraint with a subtarget that has @@ -2782,14 +2726,16 @@ static void figure_bone_nocalc_constraint(Bone *conbone, bConstraint *con, if ( (subtarbone = get_named_bone(arm, subtar)) ) { if (~subtarbone->flag & BONE_NOCALC) { if (con->type == CONSTRAINT_TYPE_KINEMATIC) - ik_chain_docalc(conbone); + ik_chain_looper(ob, conbone, NULL, + clear_bone_nocalc); else conbone->flag &= ~BONE_NOCALC; } else { if (is_ik_root_docalc(conbone)) { if (con->type == CONSTRAINT_TYPE_KINEMATIC) - ik_chain_docalc(conbone); + ik_chain_looper(ob, conbone, NULL, + clear_bone_nocalc); else conbone->flag &= ~BONE_NOCALC; } @@ -2800,7 +2746,8 @@ static void figure_bone_nocalc_constraint(Bone *conbone, bConstraint *con, /* no subtarget ... target is regular object */ if (is_ik_root_docalc(conbone)) { if (con->type == CONSTRAINT_TYPE_KINEMATIC) - ik_chain_docalc(conbone); + ik_chain_looper(ob, conbone, NULL, + clear_bone_nocalc); else conbone->flag &= ~BONE_NOCALC; } @@ -2808,28 +2755,17 @@ static void figure_bone_nocalc_constraint(Bone *conbone, bConstraint *con, } -static void figure_bone_nocalc(Object *ob) { +static void figure_bone_nocalc_core(Object *ob, bArmature *arm) { /* Let's figure out which bones need to be recalculated, * and which don't. Calculations are based on which bones * are selected, and the constraints that love them. */ - bArmature *arm; bPoseChannel *chan; bConstraint *con; Bone *conbone; int numbones, oldnumbones, iterations; - arm = get_armature(ob); - if (!arm) return; - - if (arm->flag & ARM_RESTPOS) return; - - /* Set no calc for all bones - */ - bone_looper(ob, arm->bonebase.first, NULL, - set_bone_nocalc); - oldnumbones = -1; numbones = 0; iterations = 0; @@ -2862,6 +2798,160 @@ static void figure_bone_nocalc(Object *ob) { } } +static void figure_bone_nocalc(Object *ob) { + /* Let's figure out which bones need to be recalculated, + * and which don't. Calculations are based on which bones + * are selected, and the constraints that love them. + */ + bArmature *arm; + + arm = get_armature(ob); + if (!arm) return; + + if (arm->flag & ARM_RESTPOS) return; + + /* Set no calc for all bones + */ + bone_looper(ob, arm->bonebase.first, NULL, + set_bone_nocalc); + + figure_bone_nocalc_core(ob, arm); +} + +int bone_nocalc2chan_trans_update(Object *ob, Bone *bone, void *ptr) { + /* Set PCHAN_TRANS_UPDATE for channels with bones that don't have + * the no calc flag set ... I hate this. + */ + bPoseChannel *chan; + + if (~bone->flag & BONE_NOCALC) { + chan = get_pose_channel(ob->pose, bone->name); + if (chan) chan->flag |= PCHAN_TRANS_UPDATE; + } + else { + /* reset this thing too */ + bone->flag &= ~BONE_NOCALC; + } + + return 0; +} + +void clear_gonna_move(void) { + Base *base; + + /* clear the gonna move flag */ + for (base= FIRSTBASE; base; base= base->next) { + base->object->flag &= ~GONNA_MOVE; + } +} + +int is_parent_gonna_move(Object *ob) { + if ( (ob->parent) && + (ob->parent->flag & GONNA_MOVE) ) { + return 1; + } + return 0; +} + +int is_constraint_target_gonna_move(Object *ob) { + Object *tarOb; + bConstraint *con; + + for (con = ob->constraints.first; con; con=con->next) { + if ( (tarOb = get_con_target(con)) ) { + if (tarOb->flag & GONNA_MOVE ) + return 1; + } + } + return 0; +} + +void flag_moving_objects(void) { + Base *base; + int numgonnamove = 0, oldnumgonnamove = -1; + + clear_gonna_move(); + + /* the 'well ordering principle' guarantees convergence (honest) + */ + while (numgonnamove != oldnumgonnamove) { + oldnumgonnamove = numgonnamove; + numgonnamove = 0; + for (base= FIRSTBASE; base; base= base->next) { + if (base->object->flag & GONNA_MOVE) { + ++numgonnamove; + } + else if (base->flag & SELECT) { + base->object->flag |= GONNA_MOVE; + ++numgonnamove; + } + else if (is_parent_gonna_move(base->object)) { + base->object->flag |= GONNA_MOVE; + ++numgonnamove; + } + else if (is_constraint_target_gonna_move(base->object)) { + base->object->flag |= GONNA_MOVE; + ++numgonnamove; + } + } + } + +} + +static int pose_do_update_flag(Object *ob) { + /* Figure out which pose channels need constant updating. + * Well use the bone BONE_NOCALC bit to do some temporary + * flagging (so we can reuse code), which will later be + * converted to a value for a channel... I hate this. + */ + Base *base; + bPoseChannel *chan; + int do_update = 0; + bArmature *arm; + + arm = get_armature(ob); + if (!arm) return 0; + + /* initialize */ + bone_looper(ob, arm->bonebase.first, NULL, + set_bone_nocalc); + + if (ob->pose) { + for (chan = ob->pose->chanbase.first; chan; chan=chan->next){ + if (chan->constraints.first) { + for (base= FIRSTBASE; base; base= base->next) { + if (is_ob_constraint_target(base->object, + &chan->constraints)) { + if( (base->object->flag & GONNA_MOVE) || + (ob->flag & GONNA_MOVE)) { + Bone *bone; + /* If this armature is selected, or if the + * object that is the target of a constraint + * is selected, then lets constantly update + * this pose channel. + */ + bone = get_named_bone(ob->data, chan->name); + if (bone) { + bone->flag &= ~BONE_NOCALC; + ++do_update; + } + } + } + } + } + } + } + + if (do_update) { + figure_bone_nocalc_core(ob, arm); + } + + bone_looper(ob, arm->bonebase.first, NULL, + bone_nocalc2chan_trans_update); + + return do_update; +} + /*** POSE FIGURIN' -- END ***/ @@ -2878,6 +2968,8 @@ static void setbaseflags_for_editing(int mode) /* 0,'g','r','s' */ Base *base; copy_baseflags(); + flag_moving_objects(); + for (base= FIRSTBASE; base; base= base->next) { base->flag &= ~(BA_PARSEL+BA_WASSEL);