New: Option to show the paths of Bones over time.
authorTon Roosendaal <ton@blender.org>
Sun, 23 Oct 2005 10:08:19 +0000 (10:08 +0000)
committerTon Roosendaal <ton@blender.org>
Sun, 23 Oct 2005 10:08:19 +0000 (10:08 +0000)
In PoseMode, press Wkey or use the Pose pulldown menu. It calculates the
positions of all selected Bone end points, over the time as indicated with
the Scene start/end frame. This then is drawn as a path, with little black
dots on every frame, and a white dot on every 10 frames.

Paths are not saved in files, and not calculated automatic yet on changes.

To make this relative fast, but also reliable, I had to add a new method
in the Dependency graph system, to find exactly (and only) these parents
of an Object that influence its position. This is needed because the path
should show the actual global coordinates of the entire animation system.

source/blender/blenkernel/BKE_depsgraph.h
source/blender/blenkernel/intern/action.c
source/blender/blenkernel/intern/armature.c
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenloader/intern/readfile.c
source/blender/include/BIF_poseobject.h
source/blender/makesdna/DNA_action_types.h
source/blender/src/drawarmature.c
source/blender/src/header_view3d.c
source/blender/src/poseobject.c

index 4ac70df66a7a0723a5c743d120e1ff1f3045d5b4..5dd820d5a79a7f89a487c526bc98556d7fbb6afb 100644 (file)
@@ -94,11 +94,20 @@ void        boundbox_deps(void);
 void   draw_all_deps(void);
 
 /* ********** API *************** */
+/* Note that the DAG never executes changes in Objects, only sets flags in Objects */
 
 void   DAG_scene_sort(struct Scene *sce);
+
+               /* flag all objects that need recalc because they're animated */
 void   DAG_scene_update_flags(struct Scene *sce, unsigned int lay);
+               /* flag all objects that need recalc because they're animated, influencing this object only */
+void   DAG_object_update_flags(struct Scene *sce, struct Object *ob, unsigned int lay);
+
+               /* flushes all recalc flags in objects down the dependency tree */
 void   DAG_scene_flush_update(struct Scene *sce, unsigned int lay);
+               /* flushes all recalc flags for this object down the dependency tree */
 void   DAG_object_flush_update(struct Scene *sce, struct Object *ob, short flag);
+
 void   DAG_pose_sort(struct Object *ob);
 
 #endif
index dcb048c5227d1d7307aedbac90944fe2ab918b80..8d61c4ce8658b53c350b205c37e17e2c5f53e437 100644 (file)
@@ -237,6 +237,7 @@ void copy_pose(bPose **dst, bPose *src, int copycon)
                for (pchan=outPose->chanbase.first; pchan; pchan=pchan->next) {
                        copy_constraints(&listb, &pchan->constraints);  // copy_constraints NULLs listb
                        pchan->constraints= listb;
+                       pchan->path= NULL;
                }
        }
        
@@ -245,11 +246,13 @@ void copy_pose(bPose **dst, bPose *src, int copycon)
 
 void free_pose_channels(bPose *pose) 
 {
-       bPoseChannel *chan;
+       bPoseChannel *pchan;
        
        if (pose->chanbase.first){
-               for (chan = pose->chanbase.first; chan; chan=chan->next){
-                       free_constraints(&chan->constraints);
+               for (pchan = pose->chanbase.first; pchan; pchan=pchan->next){
+                       if(pchan->path)
+                               MEM_freeN(pchan->path);
+                       free_constraints(&pchan->constraints);
                }
                BLI_freelistN (&pose->chanbase);
        }
index 5b8899e7fa727cb60d40f982135b3c3e9ac1edb1..c1d24bf5deb53ba58f2f1d909feed888ba89a869 100644 (file)
@@ -963,6 +963,8 @@ void armature_rebuild_pose(Object *ob, bArmature *arm)
        for(pchan= pose->chanbase.first; pchan; pchan= next) {
                next= pchan->next;
                if(pchan->bone==NULL) {
+                       if(pchan->path)
+                               MEM_freeN(pchan->path);
                        free_constraints(&pchan->constraints);
                        BLI_freelinkN(&pose->chanbase, pchan);
                }
index 5e2b36a19dfee0eb6153b6c79480c6700258942d..f8ebf92d874f8fb3547b5360c9e4517d653231a2 100644 (file)
@@ -1564,6 +1564,69 @@ void DAG_object_flush_update(Scene *sce, Object *ob, short flag)
        DAG_scene_flush_update(sce, sce->lay);
 }
 
+/* recursively descends tree, each node only checked once */
+/* node is checked to be of type object */
+static int parent_check_node(DagNode *node, int curtime)
+{
+       DagAdjList *itA;
+       
+       node->lasttime= curtime;
+       
+       if(node->color==DAG_GRAY)
+               return DAG_GRAY;
+       
+       for(itA = node->child; itA; itA= itA->next) {
+               if(itA->node->type==ID_OB) {
+                       
+                       if(itA->node->color==DAG_GRAY)
+                               return DAG_GRAY;
+
+                       /* descend if not done */
+                       if(itA->node->lasttime!=curtime) {
+                               itA->node->color= parent_check_node(itA->node, curtime);
+                       
+                               if(itA->node->color==DAG_GRAY)
+                                       return DAG_GRAY;
+                       }
+               }
+       }
+       
+       return DAG_WHITE;
+}
+
+/* all nodes that influence this object get tagged, for calculating the exact
+   position of this object at a given timeframe */
+void DAG_object_update_flags(Scene *sce, Object *ob, unsigned int lay)
+{
+       DagNode *node;
+       DagAdjList *itA;
+       
+       /* tag nodes unchecked */
+       for(node = sce->theDag->DagNode.first; node; node= node->next) 
+               node->color = DAG_WHITE;
+       
+       node = dag_get_node(sce->theDag, ob);
+       node->color = DAG_GRAY;
+       
+       sce->theDag->time++;
+       node= sce->theDag->DagNode.first;
+       for(itA = node->child; itA; itA= itA->next) {
+               if(itA->node->type==ID_OB && itA->node->lasttime!=sce->theDag->time)
+                       itA->node->color= parent_check_node(itA->node, sce->theDag->time);
+       }
+       
+       /* set recalcs and flushes */
+       DAG_scene_update_flags(sce, lay);
+       
+       /* now we clear recalcs, unless color is set */
+       for(node = sce->theDag->DagNode.first; node; node= node->next) {
+               if(node->type==ID_OB && node->color==DAG_WHITE) {
+                       Object *ob= node->ob;
+                       ob->recalc= 0;
+               }
+       }
+}
+
 /* ******************* DAG FOR ARMATURE POSE ***************** */
 
 /* we assume its an armature with pose */
index 154161e6a7df57d73d0ea226077e66fbaac30b1c..d139af27d2551c64d78583acfd51582287a74679 100644 (file)
@@ -2259,19 +2259,20 @@ static void lib_link_object(FileData *fd, Main *main)
 
 static void direct_link_pose(FileData *fd, bPose *pose) {
 
-       bPoseChannel *chan;
+       bPoseChannel *pchan;
 
        if (!pose)
                return;
 
        link_list(fd, &pose->chanbase);
 
-       for (chan = pose->chanbase.first; chan; chan=chan->next) {
-               chan->bone= NULL;
-               chan->parent= newdataadr(fd, chan->parent);
-               chan->child= newdataadr(fd, chan->child);
-               direct_link_constraints(fd, &chan->constraints);
-               chan->iktree.first= chan->iktree.last= NULL;
+       for (pchan = pose->chanbase.first; pchan; pchan=pchan->next) {
+               pchan->bone= NULL;
+               pchan->parent= newdataadr(fd, pchan->parent);
+               pchan->child= newdataadr(fd, pchan->child);
+               direct_link_constraints(fd, &pchan->constraints);
+               pchan->iktree.first= pchan->iktree.last= NULL;
+               pchan->path= NULL;
        }
 
 }
index 2842da796bfd2f615e059567ffc988b5cd62e137..7ff4c0a516ba9b5d59f7253c6af426459f1f444d 100644 (file)
@@ -60,6 +60,9 @@ void paste_posebuf (int flip);
 
 void pose_adds_vgroups(struct Object *meshobj);
 
+void pose_calculate_path(struct Object *ob);
+void pose_clear_paths(struct Object *ob);
+
 void pose_flip_names(void);
 void pose_activate_flipped_bone(void);
 
index 6c26e86f88ff53c48c44ac73e27e3b9c56f411d9..06066200ead1a609de86365fb9902a24e807ebf4 100644 (file)
@@ -48,7 +48,7 @@ typedef struct bPoseChannel {
        short                           flag;           /* dynamic, for detecting transform changes */
        short                           constflag;  /* for quick detecting which constraints affect this channel */
        short                           ikflag;         /* settings for IK bones */
-       short                           pad;
+       short                           pathlen;        /* for drawing paths, the amount of frames */
        
        struct Bone                     *bone;          /* set on read file or rebuild pose */
        struct bPoseChannel *parent;    /* set on read file or rebuild pose */
@@ -70,6 +70,8 @@ typedef struct bPoseChannel {
        float           stiffness[3];                           /* DOF stiffness */
        float           ikstretch;
        
+       float           *path;                          /* totpath x 3 x float */
+       
 } bPoseChannel;
 
 
index 2154ac866aa059b8f81e062487f8156d1995aaaa..112731b5814a94990f3b7c51c0062052b18ce6bd 100644 (file)
@@ -1661,6 +1661,46 @@ static void draw_ebones(Object *ob, int dt)
        }
 }
 
+/* in view space */
+static void draw_pose_paths(Object *ob)
+{
+       bPoseChannel *pchan;
+       float *fp;
+       int a;
+       
+       if(G.vd->zbuf) glDisable(GL_DEPTH_TEST);
+       
+       glPushMatrix();
+       glLoadMatrixf(G.vd->viewmat);
+       
+       for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+               if(pchan->path) {
+                       
+                       BIF_ThemeColorBlend(TH_WIRE, TH_BACK, 0.7);
+                       glBegin(GL_LINE_STRIP);
+                       for(a=0, fp= pchan->path; a<pchan->pathlen; a++, fp+=3)
+                               glVertex3fv(fp);
+                       glEnd();
+                       
+                       glPointSize(1.0);
+                       BIF_ThemeColor(TH_WIRE);
+                       glBegin(GL_POINTS);
+                       for(a=0, fp= pchan->path; a<pchan->pathlen; a++, fp+=3)
+                               glVertex3fv(fp);
+                       glEnd();
+                       
+                       BIF_ThemeColor(TH_TEXT_HI);
+                       glBegin(GL_POINTS);
+                       for(a=0, fp= pchan->path; a<pchan->pathlen; a+=10, fp+=30)
+                               glVertex3fv(fp);
+                       glEnd();
+               }
+       }
+       
+       if(G.vd->zbuf) glEnable(GL_DEPTH_TEST);
+       glPopMatrix();
+}
+
 /* called from drawobject.c */
 void draw_armature(Base *base, int dt)
 {
@@ -1697,9 +1737,12 @@ void draw_armature(Base *base, int dt)
                                        arm->flag |= ARM_POSEMODE;
                                else if(G.f & G_WEIGHTPAINT)
                                        arm->flag |= ARM_POSEMODE;
+                               
+                               draw_pose_paths(ob);
                        }                       
                        draw_pose_channels(base, dt);
                        arm->flag &= ~ARM_POSEMODE; 
+                       
                }
        }
        /* restore */
index 2294e7e002e7b5081ce392f6ee1b5dc983f54647..35caac2811704270a03305755598d2278bdea742 100644 (file)
@@ -3230,6 +3230,12 @@ static void do_view3d_pose_armaturemenu(void *arg, int event)
        case 9:
                pose_flip_names();
                break;
+       case 10:
+               pose_calculate_path(OBACT);
+               break;
+       case 11:
+               pose_clear_paths(OBACT);
+               break;
        }
        allqueue(REDRAWVIEW3D, 0);
 }
@@ -3291,14 +3297,17 @@ static uiBlock *view3d_pose_armaturemenu(void *arg_unused)
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Paste Pose",                             0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Paste Flipped Pose",                             0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");   
 
-       uiDefBut(block, SEPR, 0, "", 0, yco-=6, 
-                        menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+       uiDefBut(block, SEPR, 0, "", 0, yco-=6,  menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
        
        uiDefIconTextBlockBut(block, view3d_pose_armature_showhidemenu, 
                                                  NULL, ICON_RIGHTARROW_THIN,   "Show/Hide Bones", 0, yco-=20, 120, 19, "");
 
-       uiDefBut(block, SEPR, 0, "", 0, yco-=6, 
-                        menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+       uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+       
+       uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Calculate Paths|W",                      0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 10, "");
+       uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Clear All Paths|W",                      0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 11, "");
+       
+       uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
        
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Copy Attributes...|Ctrl C",                      0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 5, "");
        uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Flip L/R Names|W",                       0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 9, "");
index 8d3d776eb91e5bbd88f9e2272fff6fc4fbb0b039..bc3012f343ca8d342025f47132a7327ebbc41f96 100644 (file)
@@ -184,6 +184,82 @@ int pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
        return 0;
 }
 
+/* ********************************************** */
+
+/* for the object with pose/action: create path curves for selected bones */
+void pose_calculate_path(Object *ob)
+{
+       bPoseChannel *pchan;
+       Base *base;
+       float *fp;
+       int cfra;
+       
+       if(ob==NULL || ob->pose==NULL)
+               return;
+       
+       if(EFRA<=SFRA) return;
+       
+       DAG_object_update_flags(G.scene, ob, screen_view3d_layers());
+       
+       /* malloc the path blocks */
+       for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+               if(pchan->bone && (pchan->bone->flag & BONE_SELECTED)) {
+                       pchan->pathlen= EFRA-SFRA;
+                       if(pchan->path)
+                               MEM_freeN(pchan->path);
+                       pchan->path= MEM_callocN(3*pchan->pathlen*sizeof(float), "pchan path");
+               }
+       }
+       
+       cfra= CFRA;
+       for(CFRA=SFRA; CFRA<EFRA; CFRA++) {
+               
+               /* do all updates */
+               for(base= FIRSTBASE; base; base= base->next) {
+                       if(base->object->recalc) {
+                               int temp= base->object->recalc;
+                               object_handle_update(base->object);
+                               base->object->recalc= temp;
+                       }
+               }
+               
+               for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+                       if(pchan->bone && (pchan->bone->flag & BONE_SELECTED)) {
+                               if(pchan->path) {
+                                       fp= pchan->path+3*(CFRA-SFRA);
+                                       VECCOPY(fp, pchan->pose_tail);
+                                       Mat4MulVecfl(ob->obmat, fp);
+                               }
+                       }
+               }
+       }
+       
+       CFRA= cfra;
+       allqueue(REDRAWVIEW3D, 0);      /* recalc tags are still there */
+}
+
+
+/* for the object with pose/action: clear all path curves */
+void pose_clear_paths(Object *ob)
+{
+       bPoseChannel *pchan;
+       
+       if(ob==NULL || ob->pose==NULL)
+               return;
+       
+       /* free the path blocks */
+       for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+               if(pchan->path) {
+                       MEM_freeN(pchan->path);
+                       pchan->path= NULL;
+               }
+       }
+       
+       allqueue(REDRAWVIEW3D, 0);
+}
+
+
+
 void pose_select_constraint_target(void)
 {
        Object *ob= OBACT;
@@ -229,13 +305,19 @@ void pose_special_editmenu(void)
        if(!ob && !ob->pose) return;
        if(ob==G.obedit || (ob->flag & OB_POSEMODE)==0) return;
        
-       nr= pupmenu("Specials%t|Select Constraint Target%x1|Flip Left-Right Names%x2");
+       nr= pupmenu("Specials%t|Select Constraint Target%x1|Flip Left-Right Names%x2|Calculate Paths%x3|Clear All Paths%x4");
        if(nr==1) {
                pose_select_constraint_target();
        }
        else if(nr==2) {
                pose_flip_names();
        }
+       else if(nr==3) {
+               pose_calculate_path(ob);
+       }
+       else if(nr==4) {
+               pose_clear_paths(ob);
+       }
 }
 
 /* context: active object, active channel, optional selected channel */