diff --git a/scripts/startup/bl_ui/properties_physics_geometry_nodes.py b/scripts/startup/bl_ui/properties_physics_geometry_nodes.py index c51fba5f035..25eb0371efb 100644 --- a/scripts/startup/bl_ui/properties_physics_geometry_nodes.py +++ b/scripts/startup/bl_ui/properties_physics_geometry_nodes.py @@ -27,10 +27,14 @@ class PHYSICS_PT_geometry_nodes(Panel): layout = self.layout if len(context.selected_editable_objects) > 1: + calc_text = iface_("Calculate Selected to Frame") bake_text = iface_("Bake Selected") else: + calc_text = iface_("Calculate to Frame") bake_text = iface_("Bake") + layout.operator("object.simulation_nodes_cache_calculate_to_frame", text=calc_text).selected = True + row = layout.row(align=True) row.operator("object.simulation_nodes_cache_bake", text=bake_text).selected = True row.operator("object.simulation_nodes_cache_delete", text="", icon='TRASH').selected = True diff --git a/source/blender/editors/object/object_bake_simulation.cc b/source/blender/editors/object/object_bake_simulation.cc index e68679f976d..ba6c78eb317 100644 --- a/source/blender/editors/object/object_bake_simulation.cc +++ b/source/blender/editors/object/object_bake_simulation.cc @@ -51,6 +51,148 @@ namespace blender::ed::object::bake_simulation { +static bool calculate_to_frame_poll(bContext *C) +{ + if (!ED_operator_object_active(C)) { + return false; + } + return true; +} + +struct CalculateSimulationJob { + wmWindowManager *wm; + Main *bmain; + Depsgraph *depsgraph; + Scene *scene; + Vector objects; + int start_frame; + int end_frame; +}; + +static void calculate_simulation_job_startjob(void *customdata, + bool *stop, + bool *do_update, + float *progress) +{ + using namespace bke::sim; + + CalculateSimulationJob &job = *static_cast(customdata); + G.is_rendering = true; + G.is_break = false; + WM_set_locked_interface(job.wm, true); + + Vector objects_to_calc; + for (Object *object : job.objects) { + if (!BKE_id_is_editable(job.bmain, &object->id)) { + continue; + } + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = reinterpret_cast(md); + if (nmd->simulation_cache != nullptr) { + nmd->simulation_cache->reset(); + } + } + } + objects_to_calc.append(object); + } + + *progress = 0.0f; + *do_update = true; + + const float frame_step_size = 1.0f; + const float progress_per_frame = 1.0f / + (float(job.end_frame - job.start_frame + 1) / frame_step_size); + const int old_frame = job.scene->r.cfra; + + for (float frame_f = job.start_frame; frame_f <= job.end_frame; frame_f += frame_step_size) { + const SubFrame frame{frame_f}; + + if (G.is_break || (stop != nullptr && *stop)) { + break; + } + + job.scene->r.cfra = frame.frame(); + job.scene->r.subframe = frame.subframe(); + + BKE_scene_graph_update_for_newframe(job.depsgraph); + + *progress += progress_per_frame; + *do_update = true; + } + + job.scene->r.cfra = old_frame; + DEG_time_tag_update(job.bmain); + + *progress = 1.0f; + *do_update = true; +} + +static void calculate_simulation_job_endjob(void *customdata) +{ + CalculateSimulationJob &job = *static_cast(customdata); + WM_set_locked_interface(job.wm, false); + G.is_rendering = false; + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr); +} + +static int calculate_to_frame_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + wmWindowManager *wm = CTX_wm_manager(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Main *bmain = CTX_data_main(C); + + CalculateSimulationJob *job = MEM_new(__func__); + job->wm = wm; + job->bmain = bmain; + job->depsgraph = depsgraph; + job->scene = scene; + job->start_frame = scene->r.sfra; + job->end_frame = scene->r.cfra; + + if (RNA_boolean_get(op->ptr, "selected")) { + CTX_DATA_BEGIN (C, Object *, object, selected_objects) { + job->objects.append(object); + } + CTX_DATA_END; + } + else { + if (Object *object = CTX_data_active_object(C)) { + job->objects.append(object); + } + } + + wmJob *wm_job = WM_jobs_get(wm, + CTX_wm_window(C), + CTX_data_scene(C), + "Bake Simulation Nodes", + WM_JOB_PROGRESS, + WM_JOB_TYPE_CALCULATE_SIMULATION_NODES); + + WM_jobs_customdata_set( + wm_job, job, [](void *job) { MEM_delete(static_cast(job)); }); + WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER); + WM_jobs_callbacks(wm_job, + calculate_simulation_job_startjob, + nullptr, + nullptr, + calculate_simulation_job_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int calculate_to_frame_modal(bContext *C, wmOperator * /*op*/, const wmEvent * /*event*/) +{ + if (!WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_CALCULATE_SIMULATION_NODES)) + { + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + } + return OPERATOR_PASS_THROUGH; +} + static bool bake_simulation_poll(bContext *C) { if (!ED_operator_object_active(C)) { @@ -315,6 +457,26 @@ static int delete_baked_simulation_exec(bContext *C, wmOperator *op) } // namespace blender::ed::object::bake_simulation +void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot) +{ + using namespace blender::ed::object::bake_simulation; + + ot->name = "Calculate Simulation to Frame"; + ot->description = + "Calculate simulations in geometry nodes modifiers from the start to current frame"; + ot->idname = __func__; + + ot->invoke = calculate_to_frame_invoke; + ot->modal = calculate_to_frame_modal; + ot->poll = calculate_to_frame_poll; + + RNA_def_boolean(ot->srna, + "selected", + false, + "Selected", + "Calculate all selected objects instead of just the active object"); +} + void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot) { using namespace blender::ed::object::bake_simulation; diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 78d0a3c83c1..73b3527bee2 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -340,6 +340,7 @@ void OBJECT_OT_bake(wmOperatorType *ot); /* object_bake_simulation.cc */ +void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot); void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot); void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 5785ddee5a7..4e58de014e9 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -260,6 +260,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_bake_image); WM_operatortype_append(OBJECT_OT_bake); + WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_calculate_to_frame); WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_bake); WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_delete); WM_operatortype_append(OBJECT_OT_drop_named_material); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 1734c2d0e64..74b0f30995d 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1516,6 +1516,7 @@ typedef enum eWM_JobType { WM_JOB_TYPE_LINEART, WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL, WM_JOB_TYPE_SEQ_DRAG_DROP_PREVIEW, + WM_JOB_TYPE_CALCULATE_SIMULATION_NODES, WM_JOB_TYPE_BAKE_SIMULATION_NODES, /* add as needed, bake, seq proxy build * if having hard coded values is a problem */