Animation: Motion Paths Refresh All

Adds a button, Update All Paths, to the Motion Paths property tabs and
will always show. The operator goes through all visible objects and
updates their motion paths.

The current implementation has a subtle functional change. Calculating
or updating motion paths for armature objects (through the Object tab,
not Armature tab) now also updates the paths for its bones. We could
preserve the old behavior but it doesn't seem necessary. It seems more
likely that the animator wants to update both anyways.

Reviewed by: sybren

Maniphest Tasks: T83068

Differential Revision: https://developer.blender.org/D11667
This commit is contained in:
Jan-Willem van Dronkelaar 2021-10-18 14:23:23 +02:00 committed by Sybren A. Stüvel
parent f9113c4be8
commit 4de0e2e771
Notes: blender-bot 2023-02-14 06:00:47 +01:00
Referenced by issue #83068, Motion Paths: Refresh all.
7 changed files with 124 additions and 17 deletions

View File

@ -68,20 +68,25 @@ class MotionPathButtonsPanel:
col.prop(mpath, "frame_start", text="Cache From")
col.prop(mpath, "frame_end", text="To")
row = layout.row(align=True)
col = layout.column(align=True)
row = col.row(align=True)
if bones:
row.operator("pose.paths_update", text="Update Paths", icon='BONE_DATA')
row.operator("pose.paths_clear", text="", icon='X')
else:
row.operator("object.paths_update", text="Update Paths", icon='OBJECT_DATA')
row.operator("object.paths_clear", text="", icon='X')
col.operator("object.paths_update_visible", text="Update All Paths", icon="WORLD")
else:
col = layout.column(align=True)
col.label(text="Nothing to show yet...", icon='ERROR')
if bones:
col.operator("pose.paths_calculate", text="Calculate...", icon='BONE_DATA')
else:
col.operator("object.paths_calculate", text="Calculate...", icon='OBJECT_DATA')
col.operator("object.paths_update_visible", text="Update All Paths", icon="WORLD")
class MotionPathButtonsPanel_display:

View File

@ -3601,6 +3601,8 @@ class VIEW3D_MT_pose_context_menu(Menu):
layout.operator("pose.paths_calculate", text="Calculate Motion Paths")
layout.operator("pose.paths_clear", text="Clear Motion Paths")
layout.operator("pose.paths_update", text="Update Armature Motion Paths")
layout.operator("object.paths_update_visible", text="Update All Motion Paths")
layout.separator()

View File

@ -339,7 +339,16 @@ typedef enum eObjectPathCalcRange {
void ED_objects_recalculate_paths(struct bContext *C,
struct Scene *scene,
eObjectPathCalcRange range);
eObjectPathCalcRange range,
struct ListBase *ld_objects);
void ED_objects_recalculate_paths_selected(struct bContext *C,
struct Scene *scene,
eObjectPathCalcRange range);
void ED_objects_recalculate_paths_visible(struct bContext *C,
struct Scene *scene,
eObjectPathCalcRange range);
/* constraints */
struct ListBase *ED_object_constraint_active_list(struct Object *ob);

View File

@ -1125,12 +1125,51 @@ static eAnimvizCalcRange object_path_convert_range(eObjectPathCalcRange range)
return ANIMVIZ_CALC_RANGE_FULL;
}
void ED_objects_recalculate_paths_selected(bContext *C, Scene *scene, eObjectPathCalcRange range)
{
ListBase selected_objects = {NULL, NULL};
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
BLI_addtail(&selected_objects, BLI_genericNodeN(ob));
}
CTX_DATA_END;
ED_objects_recalculate_paths(C, scene, range, &selected_objects);
BLI_freelistN(&selected_objects);
}
void ED_objects_recalculate_paths_visible(bContext *C, Scene *scene, eObjectPathCalcRange range)
{
ListBase visible_objects = {NULL, NULL};
CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
BLI_addtail(&visible_objects, BLI_genericNodeN(ob));
}
CTX_DATA_END;
ED_objects_recalculate_paths(C, scene, range, &visible_objects);
BLI_freelistN(&visible_objects);
}
static bool has_object_motion_paths(Object *ob)
{
return (ob->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0;
}
static bool has_pose_motion_paths(Object *ob)
{
return ob->pose && (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0;
}
/* For the objects with animation: update paths for those that have got them
* This should selectively update paths that exist...
*
* To be called from various tools that do incremental updates
*/
void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRange range)
void ED_objects_recalculate_paths(bContext *C,
Scene *scene,
eObjectPathCalcRange range,
ListBase *ld_objects)
{
/* Transform doesn't always have context available to do update. */
if (C == NULL) {
@ -1141,13 +1180,20 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang
ViewLayer *view_layer = CTX_data_view_layer(C);
ListBase targets = {NULL, NULL};
/* loop over objects in scene */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
LISTBASE_FOREACH (LinkData *, link, ld_objects) {
Object *ob = link->data;
/* set flag to force recalc, then grab path(s) from object */
ob->avs.recalc |= ANIMVIZ_RECALC_PATHS;
if (has_object_motion_paths(ob)) {
ob->avs.recalc |= ANIMVIZ_RECALC_PATHS;
}
if (has_pose_motion_paths(ob)) {
ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS;
}
animviz_get_object_motionpaths(ob, &targets);
}
CTX_DATA_END;
Depsgraph *depsgraph;
bool free_depsgraph = false;
@ -1172,12 +1218,13 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang
if (range != OBJECT_PATH_CALC_RANGE_CURRENT_FRAME) {
/* Tag objects for copy on write - so paths will draw/redraw
* For currently frame only we update evaluated object directly. */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
if (ob->mpath) {
LISTBASE_FOREACH (LinkData *, link, ld_objects) {
Object *ob = link->data;
if (has_object_motion_paths(ob) || has_pose_motion_paths(ob)) {
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
}
CTX_DATA_END;
}
/* Free temporary depsgraph. */
@ -1229,10 +1276,10 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op)
CTX_DATA_END;
/* calculate the paths for objects that have them (and are tagged to get refreshed) */
ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_FULL);
ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL);
/* notifiers for updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL);
return OPERATOR_FINISHED;
}
@ -1298,10 +1345,10 @@ static int object_update_paths_exec(bContext *C, wmOperator *UNUSED(op))
}
/* calculate the paths for objects that have them (and are tagged to get refreshed) */
ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_FULL);
ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL);
/* notifiers for updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL);
return OPERATOR_FINISHED;
}
@ -1311,7 +1358,7 @@ void OBJECT_OT_paths_update(wmOperatorType *ot)
/* identifiers */
ot->name = "Update Object Paths";
ot->idname = "OBJECT_OT_paths_update";
ot->description = "Recalculate paths for selected objects";
ot->description = "Recalculate motion paths for selected objects";
/* api callbacks */
ot->exec = object_update_paths_exec;
@ -1323,6 +1370,47 @@ void OBJECT_OT_paths_update(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Update All Motion Paths Operator
* \{ */
static bool object_update_all_paths_poll(bContext *UNUSED(C))
{
return true;
}
static int object_update_all_paths_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
if (scene == NULL) {
return OPERATOR_CANCELLED;
}
ED_objects_recalculate_paths_visible(C, scene, OBJECT_PATH_CALC_RANGE_FULL);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE | ND_TRANSFORM, NULL);
return OPERATOR_FINISHED;
}
void OBJECT_OT_paths_update_visible(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Update All Object Paths";
ot->idname = "OBJECT_OT_paths_update_visible";
ot->description = "Recalculate all visible motion paths for objects and poses";
/* api callbacks */
ot->exec = object_update_all_paths_exec;
ot->poll = object_update_all_paths_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Clear Motion Paths Operator
* \{ */

View File

@ -84,6 +84,7 @@ void OBJECT_OT_paths_calculate(struct wmOperatorType *ot);
void OBJECT_OT_paths_update(struct wmOperatorType *ot);
void OBJECT_OT_paths_clear(struct wmOperatorType *ot);
void OBJECT_OT_paths_range_update(struct wmOperatorType *ot);
void OBJECT_OT_paths_update_visible(struct wmOperatorType *ot);
void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot);
void OBJECT_OT_move_to_collection(struct wmOperatorType *ot);

View File

@ -62,6 +62,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_paths_update);
WM_operatortype_append(OBJECT_OT_paths_clear);
WM_operatortype_append(OBJECT_OT_paths_range_update);
WM_operatortype_append(OBJECT_OT_paths_update_visible);
WM_operatortype_append(OBJECT_OT_forcefield_toggle);
WM_operatortype_append(OBJECT_OT_transfer_mode);

View File

@ -910,7 +910,8 @@ void recalcData_objects(TransInfo *t)
if (motionpath_update) {
/* Update motion paths once for all transformed objects. */
ED_objects_recalculate_paths(t->context, t->scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME);
ED_objects_recalculate_paths_selected(
t->context, t->scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME);
}
if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) {
@ -994,7 +995,7 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t)
/* Update motion paths once for all transformed objects. */
const eObjectPathCalcRange range = canceled ? OBJECT_PATH_CALC_RANGE_CURRENT_FRAME :
OBJECT_PATH_CALC_RANGE_CHANGED;
ED_objects_recalculate_paths(C, t->scene, range);
ED_objects_recalculate_paths_selected(C, t->scene, range);
}
clear_trans_object_base_flags(t);