From dec8bf8e76d7d0dea152d93acee803b8bb1ed266 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 17 Feb 2025 23:11:31 +0100 Subject: [PATCH] Point Cloud: Implement edit mode undo system Implement a basic undo system making use of implicit sharing to avoid copies for stored attribute arrays. Functionally this has no benefit over memfile undo, but current undo system architecture requires each edit mode to implement an undo type. (Reviewed and co-authored by Hans Goudey) Pull Request: https://projects.blender.org/blender/blender/pulls/134623 --- .../blender/editors/include/ED_point_cloud.hh | 2 + .../editors/point_cloud/CMakeLists.txt | 2 + .../point_cloud/intern/point_cloud_undo.cc | 153 ++++++++++++++++++ .../blender/editors/undo/undo_system_types.cc | 2 + 4 files changed, 159 insertions(+) create mode 100644 source/blender/editors/point_cloud/intern/point_cloud_undo.cc diff --git a/source/blender/editors/include/ED_point_cloud.hh b/source/blender/editors/include/ED_point_cloud.hh index 2453e17dfec..888081bcfc6 100644 --- a/source/blender/editors/include/ED_point_cloud.hh +++ b/source/blender/editors/include/ED_point_cloud.hh @@ -22,6 +22,7 @@ struct ARegion; struct bContext; struct PointCloud; struct rcti; +struct UndoType; struct wmKeyConfig; struct wmOperatorType; namespace blender::bke { @@ -37,6 +38,7 @@ namespace blender::ed::point_cloud { void operatortypes_point_cloud(); void operatormacros_point_cloud(); void keymap_point_cloud(wmKeyConfig *keyconf); +void undosys_type_register(UndoType *ut); VectorSet get_unique_editable_point_clouds(const bContext &C); diff --git a/source/blender/editors/point_cloud/CMakeLists.txt b/source/blender/editors/point_cloud/CMakeLists.txt index 1b73e9fd7ee..455efa76736 100644 --- a/source/blender/editors/point_cloud/CMakeLists.txt +++ b/source/blender/editors/point_cloud/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRC intern/duplicate.cc intern/point_cloud_ops.cc intern/point_cloud_selection.cc + intern/point_cloud_undo.cc ) set(LIB @@ -26,6 +27,7 @@ set(LIB PRIVATE bf::depsgraph PRIVATE bf::dna PRIVATE bf::functions + PRIVATE bf::intern::clog PRIVATE bf::intern::guardedalloc PRIVATE bf::windowmanager ) diff --git a/source/blender/editors/point_cloud/intern/point_cloud_undo.cc b/source/blender/editors/point_cloud/intern/point_cloud_undo.cc new file mode 100644 index 00000000000..a2b061b8424 --- /dev/null +++ b/source/blender/editors/point_cloud/intern/point_cloud_undo.cc @@ -0,0 +1,153 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edpointcloud + */ + +#include "BLI_task.hh" + +#include "BKE_context.hh" +#include "BKE_customdata.hh" +#include "BKE_main.hh" +#include "BKE_object.hh" +#include "BKE_pointcloud.hh" +#include "BKE_undo_system.hh" + +#include "CLG_log.h" + +#include "DEG_depsgraph.hh" + +#include "ED_point_cloud.hh" +#include "ED_undo.hh" + +#include "WM_api.hh" +#include "WM_types.hh" + +static CLG_LogRef LOG = {"ed.undo.point_cloud"}; + +namespace blender::ed::point_cloud { +namespace undo { + +/* -------------------------------------------------------------------- */ +/** \name Implements ED Undo System + * + * \note This is similar for all edit-mode types. + * \{ */ + +struct StepObject { + UndoRefID_Object obedit_ref = {}; + CustomData custom_data = {}; + int totpoint = 0; +}; + +struct PointCloudUndoStep { + UndoStep step; + /** See #ED_undo_object_editmode_validate_scene_from_windows code comment for details. */ + UndoRefID_Scene scene_ref = {}; + Array objects; +}; + +static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p) +{ + PointCloudUndoStep *us = reinterpret_cast(us_p); + + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Vector objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer); + + us->scene_ref.ptr = scene; + new (&us->objects) Array(objects.size()); + + threading::parallel_for(us->objects.index_range(), 8, [&](const IndexRange range) { + for (const int i : range) { + Object *ob = objects[i]; + StepObject &object = us->objects[i]; + PointCloud &point_cloud = *static_cast(ob->data); + object.obedit_ref.ptr = ob; + CustomData_init_from( + &point_cloud.pdata, &object.custom_data, CD_MASK_ALL, point_cloud.totpoint); + object.totpoint = point_cloud.totpoint; + } + }); + + bmain->is_memfile_undo_flush_needed = true; + + return true; +} + +static void step_decode( + bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir /*dir*/, bool /*is_final*/) +{ + PointCloudUndoStep *us = reinterpret_cast(us_p); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + ED_undo_object_editmode_validate_scene_from_windows( + CTX_wm_manager(C), us->scene_ref.ptr, &scene, &view_layer); + ED_undo_object_editmode_restore_helper(scene, + view_layer, + &us->objects.first().obedit_ref.ptr, + us->objects.size(), + sizeof(decltype(us->objects)::value_type)); + + BLI_assert(BKE_object_is_in_editmode(us->objects.first().obedit_ref.ptr)); + + for (const StepObject &object : us->objects) { + PointCloud &point_cloud = *static_cast(object.obedit_ref.ptr->data); + CustomData_free(&point_cloud.pdata); + CustomData_init_from(&object.custom_data, &point_cloud.pdata, CD_MASK_ALL, object.totpoint); + point_cloud.totpoint = object.totpoint; + DEG_id_tag_update(&point_cloud.id, ID_RECALC_GEOMETRY); + } + + ED_undo_object_set_active_or_warn( + scene, view_layer, us->objects.first().obedit_ref.ptr, us_p->name, &LOG); + + bmain->is_memfile_undo_flush_needed = true; + + WM_event_add_notifier(C, NC_GEOM | ND_DATA, nullptr); +} + +static void step_free(UndoStep *us_p) +{ + PointCloudUndoStep *us = reinterpret_cast(us_p); + for (StepObject &object : us->objects) { + CustomData_free(&object.custom_data); + } + us->objects.~Array(); +} + +static void foreach_ID_ref(UndoStep *us_p, + UndoTypeForEachIDRefFn foreach_ID_ref_fn, + void *user_data) +{ + PointCloudUndoStep *us = reinterpret_cast(us_p); + + foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->scene_ref)); + for (const StepObject &object : us->objects) { + foreach_ID_ref_fn(user_data, ((UndoRefID *)&object.obedit_ref)); + } +} + +/** \} */ + +} // namespace undo + +void undosys_type_register(UndoType *ut) +{ + ut->name = "Edit Point Cloud"; + ut->poll = editable_point_cloud_in_edit_mode_poll; + ut->step_encode = undo::step_encode; + ut->step_decode = undo::step_decode; + ut->step_free = undo::step_free; + + ut->step_foreach_ID_ref = undo::foreach_ID_ref; + + ut->flags = UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE; + + ut->step_size = sizeof(undo::PointCloudUndoStep); +} + +} // namespace blender::ed::point_cloud diff --git a/source/blender/editors/undo/undo_system_types.cc b/source/blender/editors/undo/undo_system_types.cc index 91e4201d46f..1b2e8672b30 100644 --- a/source/blender/editors/undo/undo_system_types.cc +++ b/source/blender/editors/undo/undo_system_types.cc @@ -17,6 +17,7 @@ #include "ED_mesh.hh" #include "ED_paint.hh" #include "ED_particle.hh" +#include "ED_point_cloud.hh" #include "ED_sculpt.hh" #include "ED_text.hh" #include "ED_undo.hh" @@ -37,6 +38,7 @@ void ED_undosys_type_init() BKE_undosys_type_append(ED_mball_undosys_type); BKE_undosys_type_append(ED_mesh_undosys_type); BKE_undosys_type_append(curves::undosys_type_register); + BKE_undosys_type_append(point_cloud::undosys_type_register); BKE_undosys_type_append(ED_undosys_type_grease_pencil); /* Paint Modes */