Blender V4.3
grease_pencil_undo.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_string.h"
10#include "BLI_task.hh"
11
12#include "BKE_context.hh"
13#include "BKE_curves.hh"
14#include "BKE_customdata.hh"
15#include "BKE_grease_pencil.hh"
16#include "BKE_lib_id.hh"
17#include "BKE_main.hh"
18#include "BKE_object.hh"
19#include "BKE_undo_system.hh"
20
21#include "CLG_log.h"
22
23#include "DEG_depsgraph.hh"
25
27#include "ED_grease_pencil.hh"
28#include "ED_undo.hh"
29
30#include "MEM_guardedalloc.h"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35#include <iostream>
36#include <ostream>
37
38static CLG_LogRef LOG = {"ed.undo.greasepencil"};
39
41
42/* -------------------------------------------------------------------- */
57/* Store contextual data and status info during undo step encoding or decoding. */
59
67
69 protected:
70 /* Index of this drawing in the original combined array of all drawings in GreasePencil ID. */
71 int index_;
72
73 /* Data from #GreasePencilDrawingBase that needs to be saved in undo steps. */
75
76 protected:
87 const GreasePencilDrawingType drawing_type) const
88 {
89 /* TODO: Maybe that code should rather be part of GreasePencil:: API, together with
90 * `add_empty_drawings` and such? */
91 GreasePencilDrawingBase *drawing = drawings[index_];
92 if (drawing != nullptr) {
93 if (drawing->type == drawing_type) {
94 return;
95 }
96 switch (drawing->type) {
97 case GP_DRAWING:
98 MEM_delete(&reinterpret_cast<GreasePencilDrawing *>(drawing)->wrap());
99 break;
101 MEM_delete(&reinterpret_cast<GreasePencilDrawingReference *>(drawing)->wrap());
102 break;
103 }
104 drawing = nullptr;
105 }
106 if (drawing == nullptr) {
107 switch (drawing_type) {
108 case GP_DRAWING:
109 drawings[index_] = reinterpret_cast<GreasePencilDrawingBase *>(
110 MEM_new<bke::greasepencil::Drawing>(__func__));
111 break;
113 drawings[index_] = reinterpret_cast<GreasePencilDrawingBase *>(
114 MEM_new<bke::greasepencil::DrawingReference>(__func__));
115 break;
116 }
117 }
118 }
119};
120
122 bke::CurvesGeometry geometry_;
123
124 public:
125 void encode(const GreasePencilDrawing &drawing_geometry,
126 const int64_t drawing_index,
127 StepEncodeStatus & /*encode_status*/)
128 {
129 BLI_assert(drawing_index >= 0 && drawing_index < INT32_MAX);
130 index_ = int(drawing_index);
131
132 flag_ = drawing_geometry.base.flag;
133 geometry_ = drawing_geometry.geometry.wrap();
134 }
135
136 void decode(GreasePencil &grease_pencil, StepDecodeStatus & /*decode_status*/) const
137 {
138 MutableSpan<GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
140 BLI_assert(drawings[index_]->type == GP_DRAWING);
141
142 GreasePencilDrawing &drawing_geometry = *reinterpret_cast<GreasePencilDrawing *>(
143 drawings[index_]);
144
145 drawing_geometry.base.flag = flag_;
146 drawing_geometry.geometry.wrap() = geometry_;
147
148 /* TODO: Check if there is a way to tell if both stored and current geometry are still the
149 * same, to avoid recomputing the caches all the time for all drawings? */
150 drawing_geometry.wrap().tag_topology_changed();
151 }
152};
153
155 UndoRefID_GreasePencil grease_pencil_ref_ = {};
156
157 public:
158 void encode(const GreasePencilDrawingReference &drawing_reference,
159 const int64_t drawing_index,
160 StepEncodeStatus & /*encode_status*/)
161 {
162 BLI_assert(drawing_index >= 0 && drawing_index < INT32_MAX);
163 index_ = int(drawing_index);
164
165 flag_ = drawing_reference.base.flag;
166 grease_pencil_ref_.ptr = drawing_reference.id_reference;
167 }
168
169 void decode(GreasePencil &grease_pencil, StepDecodeStatus &decode_status) const
170 {
171 MutableSpan<GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
173 BLI_assert(drawings[index_]->type == GP_DRAWING_REFERENCE);
174
175 GreasePencilDrawingReference &drawing_reference =
176 *reinterpret_cast<GreasePencilDrawingReference *>(drawings[index_]);
177 drawing_reference.base.flag = flag_;
178
179 if (drawing_reference.id_reference != grease_pencil_ref_.ptr) {
180 id_us_min(reinterpret_cast<ID *>(drawing_reference.id_reference));
181 drawing_reference.id_reference = grease_pencil_ref_.ptr;
182 id_us_plus(reinterpret_cast<ID *>(drawing_reference.id_reference));
183 decode_status.needs_relationships_update = true;
184 }
185 }
186
187 void foreach_id_ref(UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
188 {
189 foreach_ID_ref_fn(user_data, reinterpret_cast<UndoRefID *>(&grease_pencil_ref_));
190 }
191};
192
194 public:
195 UndoRefID_Object obedit_ref = {};
196
197 private:
198 Array<StepDrawingGeometry> drawings_geometry_;
199 Array<StepDrawingReference> drawings_reference_;
200
201 int layers_num_ = 0;
203 std::string active_node_name_;
204 CustomData layers_data_ = {};
205
206 private:
207 void encode_drawings(const GreasePencil &grease_pencil, StepEncodeStatus &encode_status)
208 {
209 const Span<const GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
210
211 int64_t drawings_geometry_num = 0;
212 int64_t drawings_reference_num = 0;
213 for (const int64_t idx : drawings.index_range()) {
214 const GreasePencilDrawingBase &drawing = *drawings[idx];
215 switch (drawing.type) {
216 case GP_DRAWING:
217 drawings_geometry_num++;
218 break;
220 drawings_reference_num++;
221 break;
222 }
223 }
224
225 drawings_geometry_.reinitialize(drawings_geometry_num);
226 drawings_reference_.reinitialize(drawings_reference_num);
227
228 int drawings_geometry_idx = 0;
229 int drawings_reference_idx = 0;
230 for (const int64_t idx : drawings.index_range()) {
231 const GreasePencilDrawingBase &drawing = *drawings[idx];
232 switch (drawing.type) {
233 case GP_DRAWING:
234 drawings_geometry_[drawings_geometry_idx++].encode(
235 reinterpret_cast<const GreasePencilDrawing &>(drawing), idx, encode_status);
236 break;
238 drawings_reference_[drawings_reference_idx++].encode(
239 reinterpret_cast<const GreasePencilDrawingReference &>(drawing), idx, encode_status);
240 break;
241 }
242 }
243 }
244
245 void decode_drawings(GreasePencil &grease_pencil, StepDecodeStatus &decode_status) const
246 {
247 const int drawing_array_num = int(drawings_geometry_.size() + drawings_reference_.size());
248 grease_pencil.resize_drawings(drawing_array_num);
249
250 for (const StepDrawingGeometry &drawing : drawings_geometry_) {
251 drawing.decode(grease_pencil, decode_status);
252 }
253 for (const StepDrawingReference &drawing : drawings_reference_) {
254 drawing.decode(grease_pencil, decode_status);
255 }
256 }
257
258 void encode_layers(const GreasePencil &grease_pencil, StepEncodeStatus & /*encode_status*/)
259 {
260 layers_num_ = int(grease_pencil.layers().size());
261
263 &grease_pencil.layers_data, &layers_data_, eCustomDataMask(CD_MASK_ALL), layers_num_);
264
265 if (grease_pencil.active_node != nullptr) {
266 active_node_name_ = grease_pencil.get_active_node()->name();
267 }
268
269 root_group_ = grease_pencil.root_group();
270 }
271
272 void decode_layers(GreasePencil &grease_pencil, StepDecodeStatus & /*decode_status*/) const
273 {
274 if (grease_pencil.root_group_ptr) {
275 MEM_delete(&grease_pencil.root_group());
276 }
277 grease_pencil.set_active_node(nullptr);
278
279 grease_pencil.root_group_ptr = MEM_new<bke::greasepencil::LayerGroup>(__func__, root_group_);
280 BLI_assert(layers_num_ == grease_pencil.layers().size());
281
282 if (!active_node_name_.empty()) {
283 if (bke::greasepencil::TreeNode *active_node = grease_pencil.root_group().find_node_by_name(
284 active_node_name_))
285 {
286 grease_pencil.set_active_node(active_node);
287 }
288 }
289
290 CustomData_free(&grease_pencil.layers_data, layers_num_);
292 &layers_data_, &grease_pencil.layers_data, eCustomDataMask(CD_MASK_ALL), layers_num_);
293 }
294
295 public:
297 {
298 CustomData_free(&layers_data_, layers_num_);
299 }
300
301 void encode(Object *ob, StepEncodeStatus &encode_status)
302 {
303 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
304 this->obedit_ref.ptr = ob;
305
306 this->encode_drawings(grease_pencil, encode_status);
307 this->encode_layers(grease_pencil, encode_status);
308 }
309
310 void decode(StepDecodeStatus &decode_status) const
311 {
312 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(this->obedit_ref.ptr->data);
313
314 this->decode_drawings(grease_pencil, decode_status);
315 this->decode_layers(grease_pencil, decode_status);
316
317 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
318 }
319
320 void foreach_id_ref(UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
321 {
322 foreach_ID_ref_fn(user_data, reinterpret_cast<UndoRefID *>(&this->obedit_ref));
323 for (StepDrawingReference &drawing_ref : drawings_reference_) {
324 drawing_ref.foreach_id_ref(foreach_ID_ref_fn, user_data);
325 }
326 }
327};
328
335
336static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
337{
338 GreasePencilUndoStep *us = reinterpret_cast<GreasePencilUndoStep *>(us_p);
339 StepEncodeStatus encode_status;
340
341 Scene *scene = CTX_data_scene(C);
342 ViewLayer *view_layer = CTX_data_view_layer(C);
344
345 us->scene_ref.ptr = scene;
346 new (&us->objects) Array<StepObject>(objects.size());
347
348 threading::parallel_for(us->objects.index_range(), 8, [&](const IndexRange range) {
349 for (const int64_t i : range) {
350 Object *ob = objects[i];
351 us->objects[i].encode(ob, encode_status);
352 }
353 });
354
355 bmain->is_memfile_undo_flush_needed = true;
356
357 return true;
358}
359
360static void step_decode(
361 bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir /*dir*/, bool /*is_final*/)
362{
363 GreasePencilUndoStep *us = reinterpret_cast<GreasePencilUndoStep *>(us_p);
364 StepDecodeStatus decode_status;
365
366 Scene *scene = CTX_data_scene(C);
367 ViewLayer *view_layer = CTX_data_view_layer(C);
368
370 CTX_wm_manager(C), us->scene_ref.ptr, &scene, &view_layer);
372 view_layer,
373 &us->objects.first().obedit_ref.ptr,
374 uint(us->objects.size()),
375 sizeof(decltype(us->objects)::value_type));
376
377 BLI_assert(BKE_object_is_in_editmode(us->objects.first().obedit_ref.ptr));
378
379 for (const StepObject &step_object : us->objects) {
380 step_object.decode(decode_status);
381 }
382
383 if (decode_status.needs_relationships_update) {
385 }
386
388 scene, view_layer, us->objects.first().obedit_ref.ptr, us_p->name, &LOG);
389
390 bmain->is_memfile_undo_flush_needed = true;
391
393}
394
395static void step_free(UndoStep *us_p)
396{
397 GreasePencilUndoStep *us = reinterpret_cast<GreasePencilUndoStep *>(us_p);
398 us->objects.~Array();
399}
400
401static void foreach_ID_ref(UndoStep *us_p,
402 UndoTypeForEachIDRefFn foreach_ID_ref_fn,
403 void *user_data)
404{
405 GreasePencilUndoStep *us = reinterpret_cast<GreasePencilUndoStep *>(us_p);
406
407 foreach_ID_ref_fn(user_data, reinterpret_cast<UndoRefID *>(&us->scene_ref));
408 for (StepObject &object : us->objects) {
409 object.foreach_id_ref(foreach_ID_ref_fn, user_data);
410 }
411}
412
415} // namespace blender::ed::greasepencil::undo
416
418{
419 using namespace blender::ed;
420
421 ut->name = "Edit GreasePencil";
422 ut->poll = greasepencil::grease_pencil_edit_poll;
423 ut->step_encode = greasepencil::undo::step_encode;
424 ut->step_decode = greasepencil::undo::step_decode;
425 ut->step_free = greasepencil::undo::step_free;
426
427 ut->step_foreach_ID_ref = greasepencil::undo::foreach_ID_ref;
428
430
432}
Scene * CTX_data_scene(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
void CustomData_init_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, int totelem)
void CustomData_free(CustomData *data, int totelem)
Low-level operations for grease pencil.
void id_us_plus(ID *id)
Definition lib_id.cc:351
void id_us_min(ID *id)
Definition lib_id.cc:359
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
@ UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE
void(*)(void *user_data, UndoRefID *id_ref) UndoTypeForEachIDRefFn
eUndoStepDir
#define BLI_assert(a)
Definition BLI_assert.h:50
unsigned int uint
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define CD_MASK_ALL
GreasePencilDrawingType
@ GP_DRAWING_REFERENCE
void ED_undo_object_set_active_or_warn(Scene *scene, ViewLayer *view_layer, Object *ob, const char *info, CLG_LogRef *log)
Definition ed_undo.cc:790
void ED_undo_object_editmode_restore_helper(Scene *scene, ViewLayer *view_layer, Object **object_array, uint object_array_len, uint object_array_stride)
Definition ed_undo.cc:826
blender::Vector< Object * > ED_undo_editmode_objects_from_view_layer(const Scene *scene, ViewLayer *view_layer)
Definition ed_undo.cc:871
void ED_undo_object_editmode_validate_scene_from_windows(wmWindowManager *wm, const Scene *scene_ref, Scene **scene_p, ViewLayer **view_layer_p)
Definition ed_undo.cc:809
Read Guarded memory(de)allocation.
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
int64_t size() const
Definition BLI_array.hh:245
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
void decode_valid_drawingtype_at_index_ensure(MutableSpan< GreasePencilDrawingBase * > drawings, const GreasePencilDrawingType drawing_type) const
void encode(const GreasePencilDrawing &drawing_geometry, const int64_t drawing_index, StepEncodeStatus &)
void decode(GreasePencil &grease_pencil, StepDecodeStatus &) const
void foreach_id_ref(UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
void encode(const GreasePencilDrawingReference &drawing_reference, const int64_t drawing_index, StepEncodeStatus &)
void decode(GreasePencil &grease_pencil, StepDecodeStatus &decode_status) const
void foreach_id_ref(UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
void decode(StepDecodeStatus &decode_status) const
void encode(Object *ob, StepEncodeStatus &encode_status)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void ED_undosys_type_grease_pencil(UndoType *ut)
static CLG_LogRef LOG
static void foreach_ID_ref(UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
static void step_free(UndoStep *us_p)
static void step_decode(bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir, bool)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
float wrap(float value, float max, float min)
Definition node_math.h:71
#define INT32_MAX
Definition stdint.h:137
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
GreasePencilDrawingBase base
GreasePencilLayerTreeNode * active_node
GreasePencilLayerTreeGroup * root_group_ptr
Definition DNA_ID.h:413
bool is_memfile_undo_flush_needed
Definition BKE_main.hh:165
char name[64]
void(* step_foreach_ID_ref)(UndoStep *us, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
const char * name
void(* step_free)(UndoStep *us)
bool(* poll)(struct bContext *C)
void(* step_decode)(bContext *C, Main *bmain, UndoStep *us, eUndoStepDir dir, bool is_final)
bool(* step_encode)(bContext *C, Main *bmain, UndoStep *us)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)