Motion Paths GUI Cleanup

This commit refactors the way that the Motion Paths GUI works. The key problems
this tries to address are:
1) Mode error - Confusion about whether we're dealing with the Object or Pose
level Motion Paths panel
2) Display settings vs Baking Settings

In line with the original design intentions for the 2.5/6 Properties Editor,
I've now split out the actual baking-related settings away from the Properties
Editor:
* Now, when clicking "Calculate Paths" from the toolbar, you'll be prompted with
a dialog to select the start/end frames (and for bones, whether to bake from
heads or tails). This is less confusing than relying on firstly setting the
range via the display range settings (and baking using that), since many people
apparently only used the "around current" mode, and were confused why things
weren't working

* Added a display of the frame ranges of the current baked Motion Path on the
active Object/Bone. This makes it clearer/easier to debug if the path suddenly
starts disappearing after a certain frame.

* Replaced Calculate/Clear Paths in the panels with a single "Update" button if
there's already a baked Motion Path.

Hopefully these changes (in combination with some of the other bugfixes) will
make it more obvious how everything works.
This commit is contained in:
Joshua Leung
2012-05-01 16:19:13 +00:00
parent fcb84663cd
commit ffc9fcb1a1
8 changed files with 152 additions and 54 deletions

View File

@@ -31,16 +31,18 @@ class MotionPathButtonsPanel():
bl_label = "Motion Paths" bl_label = "Motion Paths"
bl_options = {'DEFAULT_CLOSED'} bl_options = {'DEFAULT_CLOSED'}
def draw_settings(self, context, avs, bones=False): def draw_settings(self, context, avs, mpath, bones=False):
layout = self.layout layout = self.layout
mps = avs.motion_path mps = avs.motion_path
# Display Range
layout.prop(mps, "type", expand=True) layout.prop(mps, "type", expand=True)
split = layout.split() split = layout.split()
col = split.column() col = split.column()
col.label(text="Display Range:")
sub = col.column(align=True) sub = col.column(align=True)
if (mps.type == 'CURRENT_FRAME'): if (mps.type == 'CURRENT_FRAME'):
sub.prop(mps, "frame_before", text="Before") sub.prop(mps, "frame_before", text="Before")
@@ -50,16 +52,44 @@ class MotionPathButtonsPanel():
sub.prop(mps, "frame_end", text="End") sub.prop(mps, "frame_end", text="End")
sub.prop(mps, "frame_step", text="Step") sub.prop(mps, "frame_step", text="Step")
if bones:
col.row().prop(mps, "bake_location", expand=True)
col = split.column() col = split.column()
col.label(text="Display:")
col.prop(mps, "show_frame_numbers", text="Frame Numbers")
col.prop(mps, "show_keyframe_highlight", text="Keyframes")
if bones: if bones:
col.prop(mps, "show_keyframe_action_all", text="+ Non-Grouped Keyframes") col.label(text="Cache for Bone:")
col.prop(mps, "show_keyframe_numbers", text="Keyframe Numbers") else:
col.label(text="Cache:")
if mpath:
sub = col.column(align=True)
sub.enabled = False
sub.prop(mpath, "frame_start", text="From")
sub.prop(mpath, "frame_end", text="To")
sub = col.column() # align=True
sub.operator_context = 'EXEC_DEFAULT'
if bones:
col.operator("pose.paths_calculate", text="Update", icon='BONE_DATA')
else:
col.operator("object.paths_calculate", text="Update", icon='OBJECT_DATA')
else:
col.label(text="Not available yet...", icon='ERROR')
col.label(text="Calculate Paths first", icon='INFO')
# Display Settings
split = layout.split()
col = split.column()
col.label(text="Show:")
col.prop(mps, "show_frame_numbers", text="Frame Numbers")
col = split.column()
col.prop(mps, "show_keyframe_highlight", text="Keyframes")
sub = col.column()
sub.enabled = mps.show_keyframe_highlight
if bones:
sub.prop(mps, "show_keyframe_action_all", text="+ Non-Grouped Keyframes")
sub.prop(mps, "show_keyframe_numbers", text="Keyframe Numbers")
# FIXME: this panel still needs to be ported so that it will work correctly with animviz # FIXME: this panel still needs to be ported so that it will work correctly with animviz

View File

@@ -308,14 +308,12 @@ class DATA_PT_motion_paths(MotionPathButtonsPanel, Panel):
layout = self.layout layout = self.layout
ob = context.object ob = context.object
avs = ob.pose.animation_visualisation
self.draw_settings(context, ob.pose.animation_visualisation, bones=True) pchan = context.active_pose_bone
mpath = pchan.motion_path if pchan else None
layout.separator() self.draw_settings(context, avs, mpath, bones=True)
split = layout.split()
split.operator("pose.paths_calculate", text="Calculate Paths")
split.operator("pose.paths_clear", text="Clear Paths")
class DATA_PT_onion_skinning(OnionSkinButtonsPanel): # , Panel): # inherit from panel when ready class DATA_PT_onion_skinning(OnionSkinButtonsPanel): # , Panel): # inherit from panel when ready

View File

@@ -299,14 +299,10 @@ class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel):
layout = self.layout layout = self.layout
ob = context.object ob = context.object
avs = ob.animation_visualisation
mpath = ob.motion_path
self.draw_settings(context, ob.animation_visualisation) self.draw_settings(context, avs, mpath)
layout.separator()
row = layout.row()
row.operator("object.paths_calculate", text="Calculate Paths")
row.operator("object.paths_clear", text="Clear Paths")
class OBJECT_PT_onion_skinning(OnionSkinButtonsPanel): # , Panel): # inherit from panel when ready class OBJECT_PT_onion_skinning(OnionSkinButtonsPanel): # , Panel): # inherit from panel when ready

View File

@@ -198,9 +198,7 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports, Scene *scene, Objec
} }
else { else {
/* clear the existing path (as the range has changed), and reallocate below */ /* clear the existing path (as the range has changed), and reallocate below */
if (mpath->points) animviz_free_motionpath_cache(mpath);
MEM_freeN(mpath->points);
mpath->points = NULL;
} }
} }
} }

View File

@@ -197,24 +197,54 @@ void ED_pose_recalculate_paths(Scene *scene, Object *ob)
BLI_freelistN(&targets); BLI_freelistN(&targets);
} }
/* show popup to determine settings */
static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, wmEvent *evt)
{
Object *ob = object_pose_armature_get(CTX_data_active_object(C));
if (ELEM(NULL, ob, ob->pose))
return OPERATOR_CANCELLED;
/* set default settings from existing/stored settings */
{
bAnimVizSettings *avs = &ob->pose->avs;
PointerRNA avs_ptr;
RNA_int_set(op->ptr, "start_frame", avs->path_sf);
RNA_int_set(op->ptr, "end_frame", avs->path_ef);
RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr);
RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location"));
}
/* show popup dialog to allow editing of range... */
// FIXME: hardcoded dimensions here are just arbitrary
return WM_operator_props_dialog_popup(C, op, 200, 200);
}
/* For the object with pose/action: create path curves for selected bones /* For the object with pose/action: create path curves for selected bones
* This recalculates the WHOLE path within the pchan->pathsf and pchan->pathef range * This recalculates the WHOLE path within the pchan->pathsf and pchan->pathef range
*/ */
static int pose_calculate_paths_exec(bContext *C, wmOperator *op) static int pose_calculate_paths_exec(bContext *C, wmOperator *op)
{ {
ScrArea *sa= CTX_wm_area(C); Object *ob = object_pose_armature_get(CTX_data_active_object(C));
Scene *scene= CTX_data_scene(C); Scene *scene= CTX_data_scene(C);
Object *ob;
/* since this call may also be used from the buttons window, we need to check for where to get the object */
if (sa->spacetype == SPACE_BUTS)
ob= ED_object_context(C);
else
ob= object_pose_armature_get(CTX_data_active_object(C));
if (ELEM(NULL, ob, ob->pose)) if (ELEM(NULL, ob, ob->pose))
return OPERATOR_CANCELLED; return OPERATOR_CANCELLED;
/* grab baking settings from operator settings */
{
bAnimVizSettings *avs = &ob->pose->avs;
PointerRNA avs_ptr;
avs->path_sf = RNA_int_get(op->ptr, "start_frame");
avs->path_ef = RNA_int_get(op->ptr, "end_frame");
RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr);
RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location"));
}
/* set up path data for bones being calculated */ /* set up path data for bones being calculated */
CTX_DATA_BEGIN (C, bPoseChannel*, pchan, selected_pose_bones) CTX_DATA_BEGIN (C, bPoseChannel*, pchan, selected_pose_bones)
{ {
@@ -241,11 +271,22 @@ void POSE_OT_paths_calculate(wmOperatorType *ot)
ot->description = "Calculate paths for the selected bones"; ot->description = "Calculate paths for the selected bones";
/* api callbacks */ /* api callbacks */
ot->invoke = pose_calculate_paths_invoke;
ot->exec = pose_calculate_paths_exec; ot->exec = pose_calculate_paths_exec;
ot->poll = ED_operator_posemode; ot->poll = ED_operator_posemode;
/* flags */ /* flags */
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
/* properties */
RNA_def_int(ot->srna, "start_frame", 1, MINAFRAME, MAXFRAME, "Start",
"First frame to calculate bone paths on", MINFRAME, MAXFRAME/2.0);
RNA_def_int(ot->srna, "end_frame", 250, MINAFRAME, MAXFRAME, "End",
"Last frame to calculate bone paths on", MINFRAME, MAXFRAME/2.0);
RNA_def_enum(ot->srna, "bake_location", motionpath_bake_location_items, 0,
"Bake Location",
"Which point on the bones is used when calculating paths");
} }
/* --------- */ /* --------- */
@@ -279,14 +320,7 @@ static void ED_pose_clear_paths(Object *ob)
/* operator callback for this */ /* operator callback for this */
static int pose_clear_paths_exec (bContext *C, wmOperator *UNUSED(op)) static int pose_clear_paths_exec (bContext *C, wmOperator *UNUSED(op))
{ {
ScrArea *sa= CTX_wm_area(C); Object *ob = object_pose_armature_get(CTX_data_active_object(C));
Object *ob;
/* since this call may also be used from the buttons window, we need to check for where to get the object */
if (sa->spacetype == SPACE_BUTS)
ob= ED_object_context(C);
else
ob= object_pose_armature_get(CTX_data_active_object(C));
/* only continue if there's an object */ /* only continue if there's an object */
if (ELEM(NULL, ob, ob->pose)) if (ELEM(NULL, ob, ob->pose))

View File

@@ -1125,14 +1125,43 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene)
BLI_freelistN(&targets); BLI_freelistN(&targets);
} }
/* show popup to determine settings */
static int object_calculate_paths_invoke(bContext *C, wmOperator *op, wmEvent *evt)
{
Object *ob = CTX_data_active_object(C);
if (ob == NULL)
return OPERATOR_CANCELLED;
/* set default settings from existing/stored settings */
{
bAnimVizSettings *avs = &ob->avs;
RNA_int_set(op->ptr, "start_frame", avs->path_sf);
RNA_int_set(op->ptr, "end_frame", avs->path_ef);
}
/* show popup dialog to allow editing of range... */
// FIXME: hardcoded dimensions here are just arbitrary
return WM_operator_props_dialog_popup(C, op, 200, 200);
}
/* Calculate/recalculate whole paths (avs.path_sf to avs.path_ef) */ /* Calculate/recalculate whole paths (avs.path_sf to avs.path_ef) */
static int object_calculate_paths_exec(bContext *C, wmOperator *op) static int object_calculate_paths_exec(bContext *C, wmOperator *op)
{ {
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
int start = RNA_int_get(op->ptr, "start_frame");
int end = RNA_int_get(op->ptr, "end_frame");
/* set up path data for bones being calculated */ /* set up path data for bones being calculated */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects)
{ {
bAnimVizSettings *avs = &ob->avs;
/* grab baking settings from operator settings */
avs->path_sf = start;
avs->path_ef = end;
/* verify that the selected object has the appropriate settings */ /* verify that the selected object has the appropriate settings */
animviz_verify_motionpaths(op->reports, scene, ob, NULL); animviz_verify_motionpaths(op->reports, scene, ob, NULL);
} }
@@ -1155,11 +1184,18 @@ void OBJECT_OT_paths_calculate(wmOperatorType *ot)
ot->description = "Calculate motion paths for the selected objects"; ot->description = "Calculate motion paths for the selected objects";
/* api callbacks */ /* api callbacks */
ot->invoke = object_calculate_paths_invoke;
ot->exec = object_calculate_paths_exec; ot->exec = object_calculate_paths_exec;
ot->poll = ED_operator_object_active_editable; ot->poll = ED_operator_object_active_editable;
/* flags */ /* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_int(ot->srna, "start_frame", 1, MINAFRAME, MAXFRAME, "Start",
"First frame to calculate object paths on", MINFRAME, MAXFRAME/2.0);
RNA_def_int(ot->srna, "end_frame", 250, MINAFRAME, MAXFRAME, "End",
"Last frame to calculate object paths on", MINFRAME, MAXFRAME/2.0);
} }
/* --------- */ /* --------- */

View File

@@ -70,6 +70,8 @@ extern EnumPropertyItem fmodifier_type_items[];
extern EnumPropertyItem nla_mode_extend_items[]; extern EnumPropertyItem nla_mode_extend_items[];
extern EnumPropertyItem nla_mode_blend_items[]; extern EnumPropertyItem nla_mode_blend_items[];
extern EnumPropertyItem motionpath_bake_location_items[];
extern EnumPropertyItem event_value_items[]; extern EnumPropertyItem event_value_items[];
extern EnumPropertyItem event_type_items[]; extern EnumPropertyItem event_type_items[];
extern EnumPropertyItem operator_return_items[]; extern EnumPropertyItem operator_return_items[];

View File

@@ -39,6 +39,14 @@
#include "WM_types.h" #include "WM_types.h"
/* Which part of bone(s) get baked */
// TODO: icons?
EnumPropertyItem motionpath_bake_location_items[] = {
{MOTIONPATH_BAKE_HEADS, "HEADS", 0, "Heads", "Calculate bone paths from heads"},
{0, "TAILS", 0, "Tails", "Calculate bone paths from tails"},
//{MOTIONPATH_BAKE_CENTERS, "CENTROID", 0, "Centers", "Calculate bone paths from center of mass"},
{0, NULL, 0, NULL, NULL}};
#ifdef RNA_RUNTIME #ifdef RNA_RUNTIME
static PointerRNA rna_AnimViz_onion_skinning_get(PointerRNA *ptr) static PointerRNA rna_AnimViz_onion_skinning_get(PointerRNA *ptr)
@@ -241,10 +249,6 @@ static void rna_def_animviz_paths(BlenderRNA *brna)
"Display Paths of poses within a fixed number of frames around the current frame"}, "Display Paths of poses within a fixed number of frames around the current frame"},
{MOTIONPATH_TYPE_RANGE, "RANGE", 0, "In Range", "Display Paths of poses within specified range"}, {MOTIONPATH_TYPE_RANGE, "RANGE", 0, "In Range", "Display Paths of poses within specified range"},
{0, NULL, 0, NULL, NULL}}; {0, NULL, 0, NULL, NULL}};
static const EnumPropertyItem prop_location_items[] = {
{MOTIONPATH_BAKE_HEADS, "HEADS", 0, "Heads", "Calculate bone paths from heads"},
{0, "TAILS", 0, "Tails", "Calculate bone paths from tails"},
{0, NULL, 0, NULL, NULL}};
srna = RNA_def_struct(brna, "AnimVizMotionPaths", NULL); srna = RNA_def_struct(brna, "AnimVizMotionPaths", NULL);
RNA_def_struct_sdna(srna, "bAnimVizSettings"); RNA_def_struct_sdna(srna, "bAnimVizSettings");
@@ -260,7 +264,7 @@ static void rna_def_animviz_paths(BlenderRNA *brna)
prop = RNA_def_property(srna, "bake_location", PROP_ENUM, PROP_NONE); prop = RNA_def_property(srna, "bake_location", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "path_bakeflag"); RNA_def_property_enum_bitflag_sdna(prop, NULL, "path_bakeflag");
RNA_def_property_enum_items(prop, prop_location_items); RNA_def_property_enum_items(prop, motionpath_bake_location_items);
RNA_def_property_ui_text(prop, "Bake Location", "When calculating Bone Paths, use Head or Tips"); RNA_def_property_ui_text(prop, "Bake Location", "When calculating Bone Paths, use Head or Tips");
RNA_def_property_update(prop, NC_SPACE|ND_SPACE_VIEW3D, NULL); /* XXX since this is only for 3d-view drawing */ RNA_def_property_update(prop, NC_SPACE|ND_SPACE_VIEW3D, NULL); /* XXX since this is only for 3d-view drawing */