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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
153
source/blender/editors/point_cloud/intern/point_cloud_undo.cc
Normal file
153
source/blender/editors/point_cloud/intern/point_cloud_undo.cc
Normal 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
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user