forked from bartvdbraak/blender
New: Option to show the paths of Bones over time.
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.
This commit is contained in:
parent
12ab0f64f4
commit
de655553ab
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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, "");
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user