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
This commit is contained in:
Dalai Felinto
2025-02-17 23:11:31 +01:00
parent b36f9b3f2e
commit dec8bf8e76
4 changed files with 159 additions and 0 deletions

View File

@@ -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<PointCloud *> get_unique_editable_point_clouds(const bContext &C);

View File

@@ -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
)

View File

@@ -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<StepObject> objects;
};
static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
{
PointCloudUndoStep *us = reinterpret_cast<PointCloudUndoStep *>(us_p);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer);
us->scene_ref.ptr = scene;
new (&us->objects) Array<StepObject>(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<PointCloud *>(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<PointCloudUndoStep *>(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<PointCloud *>(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<PointCloudUndoStep *>(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<PointCloudUndoStep *>(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

View File

@@ -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 */