Blender V4.3
sculpt_undo.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 by Nicholas Bishop. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
30#include "sculpt_undo.hh"
31
32#include <mutex>
33
34#include "MEM_guardedalloc.h"
35
36#include "CLG_log.h"
37
38#include "BLI_array.hh"
41#include "BLI_listbase.h"
42#include "BLI_map.hh"
43#include "BLI_string.h"
44#include "BLI_utildefines.h"
45#include "BLI_vector.hh"
46
47#include "DNA_key_types.h"
48#include "DNA_object_types.h"
49#include "DNA_scene_types.h"
50#include "DNA_screen_types.h"
51
52#include "BKE_attribute.hh"
53#include "BKE_ccg.hh"
54#include "BKE_context.hh"
55#include "BKE_customdata.hh"
56#include "BKE_global.hh"
57#include "BKE_key.hh"
58#include "BKE_layer.hh"
59#include "BKE_main.hh"
60#include "BKE_mesh.hh"
61#include "BKE_multires.hh"
62#include "BKE_object.hh"
63#include "BKE_paint.hh"
64#include "BKE_scene.hh"
65#include "BKE_subdiv_ccg.hh"
66#include "BKE_subsurf.hh"
67#include "BKE_undo_system.hh"
68
69/* TODO(sergey): Ideally should be no direct call to such low level things. */
70#include "BKE_subdiv_eval.hh"
71
72#include "DEG_depsgraph.hh"
73
74#include "WM_api.hh"
75#include "WM_types.hh"
76
77#include "ED_geometry.hh"
78#include "ED_object.hh"
79#include "ED_sculpt.hh"
80#include "ED_undo.hh"
81
82#include "bmesh.hh"
83#include "mesh_brush_common.hh"
84#include "paint_hide.hh"
85#include "paint_intern.hh"
86#include "sculpt_automask.hh"
87#include "sculpt_color.hh"
88#include "sculpt_dyntopo.hh"
89#include "sculpt_face_set.hh"
90#include "sculpt_intern.hh"
91
92static CLG_LogRef LOG = {"ed.sculpt.undo"};
93
95
96/* Implementation of undo system for objects in sculpt mode.
97 *
98 * Each undo step in sculpt mode consists of list of nodes, each node contains a flat array of data
99 * related to the step type.
100 *
101 * Node type used for undo depends on specific operation and active sculpt mode ("regular" or
102 * dynamic topology).
103 *
104 * Regular sculpt brushes will use Position, HideVert, HideFace, Mask, Face Set * nodes. These
105 * nodes are created for every BVH node which is affected by the brush. The undo push for the node
106 * happens BEFORE modifications. This makes the operation undo to work in the following way: for
107 * every node in the undo step swap happens between node in the undo stack and the corresponding
108 * value in the BVH. This is how redo is possible after undo.
109 *
110 * The COORDS, HIDDEN or MASK type of nodes contains arrays of the corresponding values.
111 *
112 * Operations like Symmetrize are using GEOMETRY type of nodes which pushes the entire state of the
113 * mesh to the undo stack. This node contains all CustomData layers.
114 *
115 * The tricky aspect of this undo node type is that it stores mesh before and after modification.
116 * This allows the undo system to both undo and redo the symmetrize operation within the
117 * pre-modified-push of other node type behavior, but it uses more memory that it seems it should
118 * be.
119 *
120 * The dynamic topology undo nodes are handled somewhat separately from all other ones and the idea
121 * there is to store log of operations: which vertices and faces have been added or removed.
122 *
123 * Begin of dynamic topology sculpting mode have own node type. It contains an entire copy of mesh
124 * since just enabling the dynamic topology mode already does modifications on it.
125 *
126 * End of dynamic topology and symmetrize in this mode are handled in a special manner as well. */
127
128#define NO_ACTIVE_LAYER bke::AttrDomain::Auto
129
163
170
171/* Storage of geometry for the undo node.
172 * Is used as a storage for either original or modified geometry. */
174 /* Is used for sanity check, helping with ensuring that two and only two
175 * geometry pushes happened in the undo stack. */
177
188};
189
190struct Node;
191
192struct StepData {
198
200 std::string object_name;
201
204
205 /* The number of vertices in the entire mesh. */
207 /* The number of face corners in the entire mesh. */
209
214
217
218 /* Geometry modification operations.
219 *
220 * Original geometry is stored before some modification is run and is used to restore state of
221 * the object when undoing the operation
222 *
223 * Modified geometry is stored after the modification and is used to redo the modification. */
227
228 /* bmesh */
230
231 /* Geometry at the bmesh enter moment. */
233
235
236 std::mutex nodes_mutex;
237
248
251
252 size_t undo_size;
253};
254
257 /* NOTE: will split out into list for multi-object-sculpt-mode. */
259
260 /* Active color attribute at the start of this undo step. */
262
263 /* Active color attribute at the end of this undo step. */
265};
266
268{
269 UndoStack *ustack = ED_undo_stack_get();
271 return reinterpret_cast<SculptUndoStep *>(us);
272}
273
275{
276 if (SculptUndoStep *us = get_active_step()) {
277 return &us->data;
278 }
279 return nullptr;
280}
281
282static bool use_multires_undo(const StepData &step_data, const SculptSession &ss)
283{
284 return step_data.mesh_grids_num != 0 && ss.subdiv_ccg != nullptr;
285}
286
287static bool topology_matches(const StepData &step_data, const Object &object)
288{
289 const SculptSession &ss = *object.sculpt;
290 if (use_multires_undo(step_data, ss)) {
291 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
292 return subdiv_ccg.grids_num == step_data.mesh_grids_num &&
293 subdiv_ccg.grid_size == step_data.grid_size;
294 }
295 Mesh &mesh = *static_cast<Mesh *>(object.data);
296 return mesh.verts_num == step_data.mesh_verts_num;
297}
298
300{
301 return std::any_of(indices.begin(), indices.end(), [&](const int i) { return data[i]; });
302}
303
305 Depsgraph &depsgraph,
306 const StepData &step_data,
307 Object &object)
308{
309 SculptSession &ss = *object.sculpt;
310 if (ss.shapekey_active && ss.shapekey_active->name != step_data.active_shape_key_name) {
311 /* Shape key has been changed before calling undo operator. */
312
313 Key *key = BKE_key_from_object(&object);
314 KeyBlock *kb = key ? BKE_keyblock_find_name(key, step_data.active_shape_key_name.c_str()) :
315 nullptr;
316
317 if (kb) {
318 object.shapenr = BLI_findindex(&key->block, kb) + 1;
319
322 }
323 else {
324 /* Key has been removed -- skip this undo node. */
325 return false;
326 }
327 }
328 return true;
329}
330
331static void update_shapekeys(const Object &ob, KeyBlock *kb, const Span<float3> new_positions)
332{
333 const Mesh &mesh = *static_cast<Mesh *>(ob.data);
334 const int kb_act_idx = ob.shapenr - 1;
335
336 /* For relative keys editing of base should update other keys. */
337 if (std::optional<Array<bool>> dependent = BKE_keyblock_get_dependent_keys(mesh.key, kb_act_idx))
338 {
339 float(*offsets)[3] = BKE_keyblock_convert_to_vertcos(&ob, kb);
340
341 /* Calculate key coord offsets (from previous location). */
342 for (int i = 0; i < mesh.verts_num; i++) {
343 sub_v3_v3v3(offsets[i], new_positions[i], offsets[i]);
344 }
345
346 int currkey_i;
347 /* Apply offsets on other keys. */
348 LISTBASE_FOREACH_INDEX (KeyBlock *, currkey, &mesh.key->block, currkey_i) {
349 if ((currkey != kb) && (*dependent)[currkey_i]) {
350 BKE_keyblock_update_from_offset(&ob, currkey, offsets);
351 }
352 }
353
354 MEM_freeN(offsets);
355 }
356
357 /* Apply new coords on active key block, no need to re-allocate kb->data here! */
359 &ob, kb, reinterpret_cast<const float(*)[3]>(new_positions.data()));
360}
361
362static void restore_position_mesh(const Depsgraph &depsgraph,
363 Object &object,
364 const Span<std::unique_ptr<Node>> unodes,
365 MutableSpan<bool> modified_verts)
366{
367 Mesh &mesh = *static_cast<Mesh *>(object.data);
368 const SculptSession &ss = *object.sculpt;
369
370 /* Ideally, we would use the #PositionDeformData::deform method to perform the reverse
371 * deformation based on the evaluated positions, however this causes odd behavior.
372 * For now, this is a modified version of older code that depends on an extra `orig_position`
373 * array stored inside the #Node to perform swaps correctly.
374 *
375 * See #128859 for more detail.
376 */
379 MutableSpan orig_positions = mesh.vert_positions_for_write();
380 if (ss.shapekey_active) {
381 threading::parallel_for(unodes.index_range(), 1, [&](const IndexRange range) {
382 for (const int node_i : range) {
383 Node &unode = *unodes[node_i];
384 const Span<int> verts = unode.vert_indices.as_span().take_front(unode.unique_verts_num);
385 for (const int i : verts.index_range()) {
386 std::swap(unode.position[i], eval_mut[verts[i]]);
387 }
388
389 modified_verts.fill_indices(verts, true);
390 }
391 });
392
393 float(*vertCos)[3] = BKE_keyblock_convert_to_vertcos(&object, ss.shapekey_active);
394 MutableSpan key_positions(reinterpret_cast<float3 *>(vertCos), ss.shapekey_active->totelem);
395
396 threading::parallel_for(unodes.index_range(), 1, [&](const IndexRange range) {
397 for (const int node_i : range) {
398 Node &unode = *unodes[node_i];
399 const Span<int> verts = unode.vert_indices.as_span().take_front(unode.unique_verts_num);
400 for (const int i : verts.index_range()) {
401 std::swap(unode.orig_position[i], key_positions[verts[i]]);
402 }
403 }
404 });
405
406 update_shapekeys(object, ss.shapekey_active, key_positions);
407
408 if (ss.shapekey_active == mesh.key->refkey) {
409 mesh.vert_positions_for_write().copy_from(key_positions);
410 }
411
412 MEM_freeN(vertCos);
413 }
414 else {
415 threading::parallel_for(unodes.index_range(), 1, [&](const IndexRange range) {
416 for (const int node_i : range) {
417 Node &unode = *unodes[node_i];
418 const Span<int> verts = unode.vert_indices.as_span().take_front(unode.unique_verts_num);
419 for (const int i : verts.index_range()) {
420 std::swap(unode.position[i], eval_mut[verts[i]]);
421 }
422
423 modified_verts.fill_indices(verts, true);
424 }
425 });
426
427 if (orig_positions.data() != eval_mut.data()) {
428 threading::parallel_for(unodes.index_range(), 1, [&](const IndexRange range) {
429 for (const int node_i : range) {
430 Node &unode = *unodes[node_i];
431 const Span<int> verts = unode.vert_indices.as_span().take_front(
432 unode.unique_verts_num);
433 for (const int i : verts.index_range()) {
434 std::swap(unode.orig_position[i], orig_positions[verts[i]]);
435 }
436 }
437 });
438 }
439 }
440 }
441 else {
442 PositionDeformData position_data(depsgraph, object);
443 threading::EnumerableThreadSpecific<Vector<float3>> all_tls;
444 threading::parallel_for(unodes.index_range(), 1, [&](const IndexRange range) {
445 Vector<float3> &translations = all_tls.local();
446 for (const int i : range) {
447 Node &unode = *unodes[i];
448 const Span<int> verts = unode.vert_indices.as_span().take_front(unode.unique_verts_num);
449 translations.resize(verts.size());
450 translations_from_new_positions(
451 unode.position.as_span().take_front(unode.unique_verts_num),
452 verts,
453 position_data.eval,
454 translations);
455
456 gather_data_mesh(position_data.eval,
457 verts,
458 unode.position.as_mutable_span().take_front(unode.unique_verts_num));
459
460 position_data.deform(translations, verts);
461
462 modified_verts.fill_indices(verts, true);
463 }
464 });
465 }
466}
467
469 const CCGKey &key,
470 Node &unode,
471 MutableSpan<bool> modified_grids)
472{
473 const Span<int> grids = unode.grids;
474 MutableSpan<float3> undo_position = unode.position;
475
476 for (const int i : grids.index_range()) {
477 MutableSpan data = positions.slice(bke::ccg::grid_range(key, grids[i]));
478 MutableSpan undo_data = undo_position.slice(bke::ccg::grid_range(key, i));
479 for (const int offset : data.index_range()) {
480 std::swap(data[offset], undo_data[offset]);
481 }
482 }
483
484 modified_grids.fill_indices(grids, true);
485}
486
488 Node &unode,
489 MutableSpan<bool> modified_vertices)
490{
491 Mesh &mesh = *static_cast<Mesh *>(object.data);
492 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
494 ".hide_vert", bke::AttrDomain::Point);
495 for (const int i : unode.vert_indices.index_range().take_front(unode.unique_verts_num)) {
496 const int vert = unode.vert_indices[i];
497 if (unode.vert_hidden[i].test() != hide_vert.span[vert]) {
498 unode.vert_hidden[i].set(!unode.vert_hidden[i].test());
499 hide_vert.span[vert] = !hide_vert.span[vert];
500 modified_vertices[vert] = true;
501 }
502 }
503 hide_vert.finish();
504}
505
507 Node &unode,
508 MutableSpan<bool> modified_grids)
509{
510 if (unode.grid_hidden.is_empty()) {
512 return;
513 }
514
515 BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg);
516 const Span<int> grids = unode.grids;
517 for (const int i : grids.index_range()) {
518 /* Swap the two bit spans. */
520 MutableBoundedBitSpan b = grid_hidden[grids[i]];
521 for (const int j : a.index_range()) {
522 const bool value_a = a[j];
523 const bool value_b = b[j];
524 a[j].set(value_b);
525 b[j].set(value_a);
526 }
527 }
528
529 modified_grids.fill_indices(grids, true);
530}
531
532static void restore_hidden_face(Object &object, Node &unode, MutableSpan<bool> modified_faces)
533{
534 Mesh &mesh = *static_cast<Mesh *>(object.data);
535 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
536 bke::SpanAttributeWriter hide_poly = attributes.lookup_or_add_for_write_span<bool>(
537 ".hide_poly", bke::AttrDomain::Face);
538
539 const Span<int> face_indices = unode.face_indices;
540
541 for (const int i : face_indices.index_range()) {
542 const int face = face_indices[i];
543 if (unode.face_hidden[i].test() != hide_poly.span[face]) {
544 unode.face_hidden[i].set(!unode.face_hidden[i].test());
545 hide_poly.span[face] = !hide_poly.span[face];
546 modified_faces[face] = true;
547 }
548 }
549 hide_poly.finish();
550}
551
552static void restore_color(Object &object, StepData &step_data, MutableSpan<bool> modified_vertices)
553{
554 Mesh &mesh = *static_cast<Mesh *>(object.data);
556
557 for (std::unique_ptr<Node> &unode : step_data.nodes) {
558 if (color_attribute.domain == bke::AttrDomain::Point && !unode->col.is_empty()) {
560 unode->vert_indices.as_span().take_front(unode->unique_verts_num),
561 color_attribute.span,
562 unode->col);
563 }
564 else if (color_attribute.domain == bke::AttrDomain::Corner && !unode->loop_col.is_empty()) {
565 color::swap_gathered_colors(unode->corner_indices, color_attribute.span, unode->loop_col);
566 }
567
568 modified_vertices.fill_indices(unode->vert_indices.as_span(), true);
569 }
570
571 color_attribute.finish();
572}
573
574static void restore_mask_mesh(Object &object, Node &unode, MutableSpan<bool> modified_vertices)
575{
577
578 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
580 ".sculpt_mask", bke::AttrDomain::Point);
581
582 const Span<int> index = unode.vert_indices.as_span().take_front(unode.unique_verts_num);
583
584 for (const int i : index.index_range()) {
585 const int vert = index[i];
586 if (mask.span[vert] != unode.mask[i]) {
587 std::swap(mask.span[vert], unode.mask[i]);
588 modified_vertices[vert] = true;
589 }
590 }
591
592 mask.finish();
593}
594
595static void restore_mask_grids(Object &object, Node &unode, MutableSpan<bool> modified_grids)
596{
597 SculptSession &ss = *object.sculpt;
598 SubdivCCG *subdiv_ccg = ss.subdiv_ccg;
599 MutableSpan<float> masks = subdiv_ccg->masks;
600
601 const CCGKey key = BKE_subdiv_ccg_key_top_level(*subdiv_ccg);
602
603 const Span<int> grids = unode.grids;
604 MutableSpan<float> undo_mask = unode.mask;
605
606 for (const int i : grids.index_range()) {
607 MutableSpan data = masks.slice(bke::ccg::grid_range(key, grids[i]));
608 MutableSpan undo_data = undo_mask.slice(bke::ccg::grid_range(key, i));
609 for (const int offset : data.index_range()) {
610 std::swap(data[offset], undo_data[offset]);
611 }
612 }
613
614 modified_grids.fill_indices(unode.grids.as_span(), true);
615}
616
617static bool restore_face_sets(Object &object,
618 Node &unode,
619 MutableSpan<bool> modified_face_set_faces)
620{
621 const Span<int> face_indices = unode.face_indices;
622
624 *static_cast<Mesh *>(object.data));
625 bool modified = false;
626 for (const int i : face_indices.index_range()) {
627 const int face = face_indices[i];
628 if (unode.face_sets[i] == face_sets.span[face]) {
629 continue;
630 }
631 std::swap(unode.face_sets[i], face_sets.span[face]);
632 modified_face_set_faces[face] = true;
633 modified = true;
634 }
635 face_sets.finish();
636 return modified;
637}
638
639static void bmesh_restore_generic(StepData &step_data, Object &object, SculptSession &ss)
640{
641 if (step_data.applied) {
642 BM_log_undo(ss.bm, ss.bm_log);
643 step_data.applied = false;
644 }
645 else {
646 BM_log_redo(ss.bm, ss.bm_log);
647 step_data.applied = true;
648 }
649
650 if (step_data.type == Type::Mask) {
651 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
652 IndexMaskMemory memory;
653 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
654 pbvh.tag_masks_changed(node_mask);
655 }
656 else {
660 }
661}
662
663/* Create empty sculpt BMesh and enable logging. */
664static void bmesh_enable(Object &object, StepData &step_data)
665{
666 SculptSession &ss = *object.sculpt;
667 Mesh *mesh = static_cast<Mesh *>(object.data);
668
671
672 /* Create empty BMesh and enable logging. */
673 BMeshCreateParams bmesh_create_params{};
674 bmesh_create_params.use_toolflags = false;
675
676 ss.bm = BM_mesh_create(&bm_mesh_allocsize_default, &bmesh_create_params);
677 BM_data_layer_add_named(ss.bm, &ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
678
680
681 /* Restore the BMLog using saved entries. */
683}
684
686 StepData &step_data,
687 Object &object,
688 SculptSession &ss)
689{
690 if (step_data.applied) {
691 dyntopo::disable(C, &step_data);
692 step_data.applied = false;
693 }
694 else {
695 bmesh_enable(object, step_data);
696
697 /* Restore the mesh from the first log entry. */
698 BM_log_redo(ss.bm, ss.bm_log);
699
700 step_data.applied = true;
701 }
702}
703
704static void bmesh_restore_end(bContext *C, StepData &step_data, Object &object, SculptSession &ss)
705{
706 if (step_data.applied) {
707 bmesh_enable(object, step_data);
708
709 /* Restore the mesh from the last log entry. */
710 BM_log_undo(ss.bm, ss.bm_log);
711
712 step_data.applied = false;
713 }
714 else {
715 /* Disable dynamic topology sculpting. */
716 dyntopo::disable(C, nullptr);
717 step_data.applied = true;
718 }
719}
720
722{
723 const Mesh *mesh = static_cast<const Mesh *>(object.data);
724
725 BLI_assert(!geometry->is_initialized);
726 geometry->is_initialized = true;
727
729 &mesh->vert_data, &geometry->vert_data, CD_MASK_MESH.vmask, mesh->verts_num);
731 &mesh->edge_data, &geometry->edge_data, CD_MASK_MESH.emask, mesh->edges_num);
733 &mesh->corner_data, &geometry->corner_data, CD_MASK_MESH.lmask, mesh->corners_num);
735 &mesh->face_data, &geometry->face_data, CD_MASK_MESH.pmask, mesh->faces_num);
736 implicit_sharing::copy_shared_pointer(mesh->face_offset_indices,
737 mesh->runtime->face_offsets_sharing_info,
738 &geometry->face_offset_indices,
739 &geometry->face_offsets_sharing_info);
740
741 geometry->totvert = mesh->verts_num;
742 geometry->totedge = mesh->edges_num;
743 geometry->totloop = mesh->corners_num;
744 geometry->faces_num = mesh->faces_num;
745}
746
748{
749 BLI_assert(geometry->is_initialized);
750
752
753 mesh->verts_num = geometry->totvert;
754 mesh->edges_num = geometry->totedge;
755 mesh->corners_num = geometry->totloop;
756 mesh->faces_num = geometry->faces_num;
757 mesh->totface_legacy = 0;
758
760 &geometry->vert_data, &mesh->vert_data, CD_MASK_MESH.vmask, geometry->totvert);
762 &geometry->edge_data, &mesh->edge_data, CD_MASK_MESH.emask, geometry->totedge);
764 &geometry->corner_data, &mesh->corner_data, CD_MASK_MESH.lmask, geometry->totloop);
766 &geometry->face_data, &mesh->face_data, CD_MASK_MESH.pmask, geometry->faces_num);
768 geometry->face_offsets_sharing_info,
769 &mesh->face_offset_indices,
770 &mesh->runtime->face_offsets_sharing_info);
771}
772
774{
775 CustomData_free(&geometry->vert_data, geometry->totvert);
776 CustomData_free(&geometry->edge_data, geometry->totedge);
777 CustomData_free(&geometry->corner_data, geometry->totloop);
778 CustomData_free(&geometry->face_data, geometry->faces_num);
779 implicit_sharing::free_shared_data(&geometry->face_offset_indices,
780 &geometry->face_offsets_sharing_info);
781}
782
783static void restore_geometry(StepData &step_data, Object &object)
784{
785 if (step_data.geometry_clear_pbvh) {
788 }
789
790 Mesh *mesh = static_cast<Mesh *>(object.data);
791
792 if (step_data.applied) {
794 step_data.applied = false;
795 }
796 else {
798 step_data.applied = true;
799 }
800}
801
802/* Handle all dynamic-topology updates
803 *
804 * Returns true if this was a dynamic-topology undo step, otherwise
805 * returns false to indicate the non-dyntopo code should run. */
806static int bmesh_restore(
807 bContext *C, Depsgraph &depsgraph, StepData &step_data, Object &object, SculptSession &ss)
808{
809 switch (step_data.type) {
812 bmesh_restore_begin(C, step_data, object, ss);
813 return true;
814
815 case Type::DyntopoEnd:
817 bmesh_restore_end(C, step_data, object, ss);
818 return true;
819 default:
820 if (ss.bm_log) {
822 bmesh_restore_generic(step_data, object, ss);
823 return true;
824 }
825 break;
826 }
827
828 return false;
829}
830
835
837{
838 return get_step_data()->bm_entry;
839}
840
841/* Geometry updates (such as Apply Base, for example) will re-evaluate the object and refine its
842 * Subdiv descriptor. Upon undo it is required that mesh, grids, and subdiv all stay consistent
843 * with each other. This means that when geometry coordinate changes the undo should refine the
844 * subdiv to the new coarse mesh coordinates. Tricky part is: this needs to happen without using
845 * dependency graph tag: tagging object for geometry update will either loose sculpted data from
846 * the sculpt grids, or will wrongly "commit" them to the CD_MDISPS.
847 *
848 * So what we do instead is do minimum object evaluation to get base mesh coordinates for the
849 * multires modifier input. While this is expensive, it is less expensive than dependency graph
850 * evaluation and is only happening when geometry coordinates changes on undo.
851 *
852 * Note that the dependency graph is ensured to be evaluated prior to the undo step is decoded,
853 * so if the object's modifier stack references other object it is all fine. */
854static void refine_subdiv(Depsgraph *depsgraph,
855 SculptSession &ss,
856 Object &object,
857 bke::subdiv::Subdiv *subdiv)
858{
860 depsgraph, &object, ss.multires.modifier);
861
863 subdiv, static_cast<const Mesh *>(object.data), deformed_verts);
864}
865
866static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
867{
868 Scene *scene = CTX_data_scene(C);
869 ViewLayer *view_layer = CTX_data_view_layer(C);
871 BKE_view_layer_synced_ensure(scene, view_layer);
872 Object &object = *BKE_view_layer_active_object_get(view_layer);
873 if (step_data.object_name != object.id.name) {
874 return;
875 }
876 SculptSession &ss = *object.sculpt;
877 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
878
879 /* Restore pivot. */
880 ss.pivot_pos = step_data.pivot_pos;
881 ss.pivot_rot = step_data.pivot_rot;
882
883 if (bmesh_restore(C, *depsgraph, step_data, object, ss)) {
884 return;
885 }
886
887 /* Switching to sculpt mode does not push a particular type.
888 * See #124484. */
889 if (step_data.type == Type::None && step_data.nodes.is_empty()) {
890 return;
891 }
892
893 /* Adding multires via the `subdivision_set` operator results in the subsequent undo step
894 * not correctly performing a global undo step; we exit early here to avoid crashing.
895 * See: #131478 */
896 const bool multires_undo_step = use_multires_undo(step_data, ss);
897 if ((multires_undo_step && pbvh.type() != bke::pbvh::Type::Grids) ||
898 (!multires_undo_step && pbvh.type() != bke::pbvh::Type::Mesh))
899 {
900 CLOG_WARN(&LOG,
901 "Undo step type and sculpt geometry type do not match: skipping undo state restore");
902 return;
903 }
904
905 const bool tag_update = ID_REAL_USERS(object.data) > 1 ||
906 !BKE_sculptsession_use_pbvh_draw(&object, rv3d) || ss.shapekey_active ||
908
909 switch (step_data.type) {
910 case Type::None: {
912 break;
913 }
914 case Type::Position: {
915 IndexMaskMemory memory;
916 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
917
919 if (!topology_matches(step_data, object)) {
920 return;
921 }
922
923 if (use_multires_undo(step_data, ss)) {
925 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
926 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
927
928 Array<bool> modified_grids(subdiv_ccg.grids_num, false);
929 for (std::unique_ptr<Node> &unode : step_data.nodes) {
930 restore_position_grids(subdiv_ccg.positions, key, *unode, modified_grids);
931 }
932 const IndexMask changed_nodes = IndexMask::from_predicate(
933 node_mask, GrainSize(1), memory, [&](const int i) {
934 return indices_contain_true(modified_grids, nodes[i].grids());
935 });
936 pbvh.tag_positions_changed(changed_nodes);
938 }
939 else {
941 if (!restore_active_shape_key(*C, *depsgraph, step_data, object)) {
942 return;
943 }
944 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
945 Array<bool> modified_verts(mesh.verts_num, false);
946 restore_position_mesh(*depsgraph, object, step_data.nodes, modified_verts);
947
948 const IndexMask changed_nodes = IndexMask::from_predicate(
949 node_mask, GrainSize(1), memory, [&](const int i) {
950 return indices_contain_true(modified_verts, nodes[i].all_verts());
951 });
952 pbvh.tag_positions_changed(changed_nodes);
953 }
954
955 if (tag_update) {
956 Mesh &mesh = *static_cast<Mesh *>(object.data);
957 mesh.tag_positions_changed();
959 }
960 else {
961 Mesh &mesh = *static_cast<Mesh *>(object.data);
962 /* The BVH normals recalculation that will happen later (caused by
963 * `pbvh.tag_positions_changed`) won't recalculate the face corner normals.
964 * We need to manually clear that cache. */
965 mesh.runtime->corner_normals_cache.tag_dirty();
966 }
967 bke::pbvh::update_bounds(*depsgraph, object, pbvh);
969 break;
970 }
971 case Type::HideVert: {
972 IndexMaskMemory memory;
973 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
974
976 if (!topology_matches(step_data, object)) {
977 return;
978 }
979
980 if (use_multires_undo(step_data, ss)) {
982 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
983 Array<bool> modified_grids(subdiv_ccg.grids_num, false);
984 for (std::unique_ptr<Node> &unode : step_data.nodes) {
985 restore_vert_visibility_grids(subdiv_ccg, *unode, modified_grids);
986 }
987 const IndexMask changed_nodes = IndexMask::from_predicate(
988 node_mask, GrainSize(1), memory, [&](const int i) {
989 return indices_contain_true(modified_grids, nodes[i].grids());
990 });
991 pbvh.tag_visibility_changed(changed_nodes);
992 }
993 else {
995 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
996 Array<bool> modified_verts(mesh.verts_num, false);
997 for (std::unique_ptr<Node> &unode : step_data.nodes) {
998 restore_vert_visibility_mesh(object, *unode, modified_verts);
999 }
1000 const IndexMask changed_nodes = IndexMask::from_predicate(
1001 node_mask, GrainSize(1), memory, [&](const int i) {
1002 return indices_contain_true(modified_verts, nodes[i].all_verts());
1003 });
1004 pbvh.tag_visibility_changed(changed_nodes);
1005 }
1006
1008 bke::pbvh::update_visibility(object, pbvh);
1009 if (BKE_sculpt_multires_active(scene, &object)) {
1011 }
1012 break;
1013 }
1014 case Type::HideFace: {
1015 IndexMaskMemory memory;
1016 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1017
1019 if (!topology_matches(step_data, object)) {
1020 return;
1021 }
1022
1023 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
1024 Array<bool> modified_faces(mesh.faces_num, false);
1025 for (std::unique_ptr<Node> &unode : step_data.nodes) {
1026 restore_hidden_face(object, *unode, modified_faces);
1027 }
1028
1029 if (use_multires_undo(step_data, ss)) {
1031 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
1032 const IndexMask changed_nodes = IndexMask::from_predicate(
1033 node_mask, GrainSize(1), memory, [&](const int i) {
1034 Vector<int> faces_vector;
1036 subdiv_ccg, nodes[i], faces_vector);
1037 return indices_contain_true(modified_faces, faces);
1038 });
1039 pbvh.tag_visibility_changed(changed_nodes);
1040 }
1041 else {
1043 const IndexMask changed_nodes = IndexMask::from_predicate(
1044 node_mask, GrainSize(1), memory, [&](const int i) {
1045 return indices_contain_true(modified_faces, nodes[i].faces());
1046 });
1047 pbvh.tag_visibility_changed(changed_nodes);
1048 }
1049
1051 bke::pbvh::update_visibility(object, pbvh);
1052 break;
1053 }
1054 case Type::Mask: {
1055 IndexMaskMemory memory;
1056 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1057
1059 if (!topology_matches(step_data, object)) {
1060 return;
1061 }
1062
1063 if (use_multires_undo(step_data, ss)) {
1065 Array<bool> modified_grids(ss.subdiv_ccg->grids_num, false);
1066 for (std::unique_ptr<Node> &unode : step_data.nodes) {
1067 restore_mask_grids(object, *unode, modified_grids);
1068 }
1069 const IndexMask changed_nodes = IndexMask::from_predicate(
1070 node_mask, GrainSize(1), memory, [&](const int i) {
1071 return indices_contain_true(modified_grids, nodes[i].grids());
1072 });
1073 bke::pbvh::update_mask_grids(*ss.subdiv_ccg, changed_nodes, pbvh);
1074 pbvh.tag_masks_changed(changed_nodes);
1075 }
1076 else {
1078 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
1079 Array<bool> modified_verts(mesh.verts_num, false);
1080 for (std::unique_ptr<Node> &unode : step_data.nodes) {
1081 restore_mask_mesh(object, *unode, modified_verts);
1082 }
1083 const IndexMask changed_nodes = IndexMask::from_predicate(
1084 node_mask, GrainSize(1), memory, [&](const int i) {
1085 return indices_contain_true(modified_verts, nodes[i].all_verts());
1086 });
1087 bke::pbvh::update_mask_mesh(mesh, changed_nodes, pbvh);
1088 pbvh.tag_masks_changed(changed_nodes);
1089 }
1090 break;
1091 }
1092 case Type::FaceSet: {
1093 IndexMaskMemory memory;
1094 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1095
1097 if (!topology_matches(step_data, object)) {
1098 return;
1099 }
1100
1101 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
1102 Array<bool> modified_faces(mesh.faces_num, false);
1103 for (std::unique_ptr<Node> &unode : step_data.nodes) {
1104 restore_face_sets(object, *unode, modified_faces);
1105 }
1106 if (use_multires_undo(step_data, ss)) {
1108 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
1109 const IndexMask changed_nodes = IndexMask::from_predicate(
1110 node_mask, GrainSize(1), memory, [&](const int i) {
1111 Vector<int> faces_vector;
1113 subdiv_ccg, nodes[i], faces_vector);
1114 return indices_contain_true(modified_faces, faces);
1115 });
1116 pbvh.tag_face_sets_changed(changed_nodes);
1117 }
1118 else {
1120 const IndexMask changed_nodes = IndexMask::from_predicate(
1121 node_mask, GrainSize(1), memory, [&](const int i) {
1122 return indices_contain_true(modified_faces, nodes[i].faces());
1123 });
1124 pbvh.tag_face_sets_changed(changed_nodes);
1125 }
1126 break;
1127 }
1128 case Type::Color: {
1129 IndexMaskMemory memory;
1130 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1131
1133 if (!topology_matches(step_data, object)) {
1134 return;
1135 }
1136
1138
1139 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
1140 Array<bool> modified_verts(mesh.verts_num, false);
1141 restore_color(object, step_data, modified_verts);
1142 const IndexMask changed_nodes = IndexMask::from_predicate(
1143 node_mask, GrainSize(1), memory, [&](const int i) {
1144 return indices_contain_true(modified_verts, nodes[i].all_verts());
1145 });
1146 pbvh.tag_attribute_changed(changed_nodes, mesh.active_color_attribute);
1147 break;
1148 }
1149 case Type::Geometry: {
1150 BLI_assert(!ss.bm);
1151
1152 restore_geometry(step_data, object);
1154 if (SubdivCCG *subdiv_ccg = ss.subdiv_ccg) {
1155 refine_subdiv(depsgraph, ss, object, subdiv_ccg->subdiv);
1156 }
1157 break;
1158 }
1159 case Type::DyntopoBegin:
1160 case Type::DyntopoEnd:
1161 /* Handled elsewhere. */
1163 break;
1164 }
1165
1167 if (tag_update) {
1169 }
1170}
1171
1172static void free_step_data(StepData &step_data)
1173{
1177 if (step_data.bm_entry) {
1178 BM_log_entry_drop(step_data.bm_entry);
1179 }
1180 step_data.~StepData();
1181}
1182
1189static const Node *get_node(const bke::pbvh::Node *node, const Type type)
1190{
1191 StepData *step_data = get_step_data();
1192 if (!step_data) {
1193 return nullptr;
1194 }
1195 if (step_data->type != type) {
1196 return nullptr;
1197 }
1198 /* This access does not need to be locked because this function is not expected to be called
1199 * while the per-node undo data is being pushed. In other words, this must not be called
1200 * concurrently with #push_node.*/
1201 std::unique_ptr<Node> *node_ptr = step_data->undo_nodes_by_pbvh_node.lookup_ptr(node);
1202 if (!node_ptr) {
1203 return nullptr;
1204 }
1205 return node_ptr->get();
1206}
1207
1208static void store_vert_visibility_grids(const SubdivCCG &subdiv_ccg,
1209 const bke::pbvh::GridsNode &node,
1210 Node &unode)
1211{
1212 const BitGroupVector<> grid_hidden = subdiv_ccg.grid_hidden;
1213 if (grid_hidden.is_empty()) {
1214 return;
1215 }
1216
1217 const Span<int> grid_indices = node.grids();
1218 unode.grid_hidden = BitGroupVector<0>(grid_indices.size(), grid_hidden.group_size());
1219 for (const int i : grid_indices.index_range()) {
1220 unode.grid_hidden[i].copy_from(grid_hidden[grid_indices[i]]);
1221 }
1222}
1223
1224static void store_positions_mesh(const Depsgraph &depsgraph, const Object &object, Node &unode)
1225{
1226 const SculptSession &ss = *object.sculpt;
1228 unode.vert_indices.as_span(),
1229 unode.position.as_mutable_span());
1231 unode.vert_indices.as_span(),
1232 unode.normal.as_mutable_span());
1233
1234 if (ss.deform_modifiers_active) {
1235 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
1236 const Span<float3> orig_positions = ss.shapekey_active ? Span(static_cast<const float3 *>(
1237 ss.shapekey_active->data),
1238 mesh.verts_num) :
1239 mesh.vert_positions();
1240
1242 orig_positions, unode.vert_indices.as_span(), unode.orig_position.as_mutable_span());
1243 }
1244}
1245
1246static void store_positions_grids(const SubdivCCG &subdiv_ccg, Node &unode)
1247{
1249 subdiv_ccg, subdiv_ccg.positions.as_span(), unode.grids, unode.position.as_mutable_span());
1251 subdiv_ccg, subdiv_ccg.normals.as_span(), unode.grids, unode.normal.as_mutable_span());
1252}
1253
1254static void store_vert_visibility_mesh(const Mesh &mesh, const bke::pbvh::Node &node, Node &unode)
1255{
1256 const bke::AttributeAccessor attributes = mesh.attributes();
1257 const VArraySpan<bool> hide_vert = *attributes.lookup<bool>(".hide_vert",
1259 if (hide_vert.is_empty()) {
1260 return;
1261 }
1262
1263 const Span<int> verts = static_cast<const bke::pbvh::MeshNode &>(node).all_verts();
1264 for (const int i : verts.index_range()) {
1265 unode.vert_hidden[i].set(hide_vert[verts[i]]);
1266 }
1267}
1268
1269static void store_face_visibility(const Mesh &mesh, Node &unode)
1270{
1271 const bke::AttributeAccessor attributes = mesh.attributes();
1272 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1273 if (hide_poly.is_empty()) {
1274 unode.face_hidden.fill(false);
1275 return;
1276 }
1277 const Span<int> faces = unode.face_indices;
1278 for (const int i : faces.index_range()) {
1279 unode.face_hidden[i].set(hide_poly[faces[i]]);
1280 }
1281}
1282
1283static void store_mask_mesh(const Mesh &mesh, Node &unode)
1284{
1285 const bke::AttributeAccessor attributes = mesh.attributes();
1286 const VArraySpan mask = *attributes.lookup<float>(".sculpt_mask", bke::AttrDomain::Point);
1287 if (mask.is_empty()) {
1288 unode.mask.fill(0.0f);
1289 }
1290 else {
1292 }
1293}
1294
1295static void store_mask_grids(const SubdivCCG &subdiv_ccg, Node &unode)
1296{
1297 if (!subdiv_ccg.masks.is_empty()) {
1299 subdiv_ccg, subdiv_ccg.masks.as_span(), unode.grids, unode.mask.as_mutable_span());
1300 }
1301 else {
1302 unode.mask.fill(0.0f);
1303 }
1304}
1305
1306static void store_color(const Mesh &mesh, const bke::pbvh::MeshNode &node, Node &unode)
1307{
1308 const OffsetIndices<int> faces = mesh.faces();
1309 const Span<int> corner_verts = mesh.corner_verts();
1310 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1312 const GVArraySpan colors(*color_attribute);
1313
1314 /* NOTE: even with loop colors we still store (derived)
1315 * vertex colors for original data lookup. */
1316 const Span<int> verts = node.verts();
1317 unode.col.reinitialize(verts.size());
1319 faces, corner_verts, vert_to_face_map, colors, color_attribute.domain, verts, unode.col);
1320
1321 if (color_attribute.domain == bke::AttrDomain::Corner) {
1322 for (const int face : node.faces()) {
1323 for (const int corner : faces[face]) {
1324 unode.corner_indices.append(corner);
1325 }
1326 }
1327 unode.loop_col.reinitialize(unode.corner_indices.size());
1328 color::gather_colors(colors, unode.corner_indices, unode.loop_col);
1329 }
1330}
1331
1333{
1334 if (!step_data.geometry_original.is_initialized) {
1335 return &step_data.geometry_original;
1336 }
1337
1339
1340 return &step_data.geometry_modified;
1341}
1342
1343static void geometry_push(const Object &object)
1344{
1345 StepData *step_data = get_step_data();
1346
1347 step_data->type = Type::Geometry;
1348
1349 step_data->applied = false;
1350 step_data->geometry_clear_pbvh = true;
1351
1352 NodeGeometry *geometry = geometry_get(*step_data);
1354}
1355
1356static void store_face_sets(const Mesh &mesh, Node &unode)
1357{
1358 const bke::AttributeAccessor attributes = mesh.attributes();
1359 const VArraySpan face_sets = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
1360 if (face_sets.is_empty()) {
1361 unode.face_sets.fill(1);
1362 }
1363 else {
1364 gather_data_mesh(face_sets, unode.face_indices.as_span(), unode.face_sets.as_mutable_span());
1365 }
1366}
1367
1368static void fill_node_data_mesh(const Depsgraph &depsgraph,
1369 const Object &object,
1370 const bke::pbvh::MeshNode &node,
1371 const Type type,
1372 Node &unode)
1373{
1374 const SculptSession &ss = *object.sculpt;
1375 const Mesh &mesh = *static_cast<Mesh *>(object.data);
1376
1377 unode.vert_indices = node.all_verts();
1378 unode.unique_verts_num = node.verts().size();
1379
1380 const int verts_num = unode.vert_indices.size();
1381
1382 const bool need_faces = ELEM(type, Type::FaceSet, Type::HideFace);
1383 if (need_faces) {
1384 unode.face_indices = node.faces();
1385 }
1386
1387 switch (type) {
1388 case Type::None:
1390 break;
1391 case Type::Position: {
1392 unode.position.reinitialize(verts_num);
1393 /* Needed for original data lookup. */
1394 unode.normal.reinitialize(verts_num);
1395 if (ss.deform_modifiers_active) {
1396 unode.orig_position.reinitialize(verts_num);
1397 }
1398 store_positions_mesh(depsgraph, object, unode);
1399 break;
1400 }
1401 case Type::HideVert: {
1402 unode.vert_hidden.resize(unode.vert_indices.size());
1403 store_vert_visibility_mesh(mesh, node, unode);
1404 break;
1405 }
1406 case Type::HideFace: {
1407 unode.face_hidden.resize(unode.face_indices.size());
1409 break;
1410 }
1411 case Type::Mask: {
1412 unode.mask.reinitialize(verts_num);
1413 store_mask_mesh(mesh, unode);
1414 break;
1415 }
1416 case Type::Color: {
1417 store_color(mesh, node, unode);
1418 break;
1419 }
1420 case Type::DyntopoBegin:
1421 case Type::DyntopoEnd:
1422 /* Dyntopo should be handled elsewhere. */
1424 break;
1425 case Type::Geometry:
1426 /* See #geometry_push. */
1428 break;
1429 case Type::FaceSet: {
1430 unode.face_sets.reinitialize(unode.face_indices.size());
1431 store_face_sets(mesh, unode);
1432 break;
1433 }
1434 }
1435}
1436
1437static void fill_node_data_grids(const Object &object,
1438 const bke::pbvh::GridsNode &node,
1439 const Type type,
1440 Node &unode)
1441{
1442 const SculptSession &ss = *object.sculpt;
1443 const Mesh &base_mesh = *static_cast<const Mesh *>(object.data);
1444 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
1445
1446 unode.grids = node.grids();
1447
1448 const int grid_area = subdiv_ccg.grid_size * subdiv_ccg.grid_size;
1449 const int verts_num = unode.grids.size() * grid_area;
1450
1451 const bool need_faces = ELEM(type, Type::FaceSet, Type::HideFace);
1452 if (need_faces) {
1454 }
1455
1456 switch (type) {
1457 case Type::None:
1459 break;
1460 case Type::Position: {
1461 unode.position.reinitialize(verts_num);
1462 /* Needed for original data lookup. */
1463 unode.normal.reinitialize(verts_num);
1464 store_positions_grids(subdiv_ccg, unode);
1465 break;
1466 }
1467 case Type::HideVert: {
1468 store_vert_visibility_grids(subdiv_ccg, node, unode);
1469 break;
1470 }
1471 case Type::HideFace: {
1472 unode.face_hidden.resize(unode.face_indices.size());
1473 store_face_visibility(base_mesh, unode);
1474 break;
1475 }
1476 case Type::Mask: {
1477 unode.mask.reinitialize(verts_num);
1478 store_mask_grids(subdiv_ccg, unode);
1479 break;
1480 }
1481 case Type::Color: {
1483 break;
1484 }
1485 case Type::DyntopoBegin:
1486 case Type::DyntopoEnd:
1487 /* Dyntopo should be handled elsewhere. */
1489 break;
1490 case Type::Geometry:
1491 /* See #geometry_push. */
1493 break;
1494 case Type::FaceSet: {
1495 unode.face_sets.reinitialize(unode.face_indices.size());
1496 store_face_sets(base_mesh, unode);
1497 break;
1498 }
1499 }
1500}
1501
1506BLI_NOINLINE static void bmesh_push(const Object &object,
1507 const bke::pbvh::BMeshNode *node,
1508 Type type)
1509{
1510 StepData *step_data = get_step_data();
1511 const SculptSession &ss = *object.sculpt;
1512
1513 std::scoped_lock lock(step_data->nodes_mutex);
1514
1515 Node *unode = step_data->nodes.is_empty() ? nullptr : step_data->nodes.first().get();
1516
1517 if (unode == nullptr) {
1518 step_data->nodes.append(std::make_unique<Node>());
1519 unode = step_data->nodes.last().get();
1520
1521 step_data->type = type;
1522 step_data->applied = true;
1523
1524 if (type == Type::DyntopoEnd) {
1525 step_data->bm_entry = BM_log_entry_add(ss.bm_log);
1527 }
1528 else if (type == Type::DyntopoBegin) {
1529 /* Store a copy of the mesh's current vertices, loops, and
1530 * faces. A full copy like this is needed because entering
1531 * dynamic-topology immediately does topological edits
1532 * (converting faces to triangles) that the BMLog can't
1533 * fully restore from. */
1536
1537 step_data->bm_entry = BM_log_entry_add(ss.bm_log);
1538 BM_log_all_added(ss.bm, ss.bm_log);
1539 }
1540 else {
1541 step_data->bm_entry = BM_log_entry_add(ss.bm_log);
1542 }
1543 }
1544
1545 if (node) {
1546 const int cd_vert_mask_offset = CustomData_get_offset_named(
1547 &ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
1548
1549 /* The vertices and node aren't changed, though pointers to them are stored in the log. */
1550 bke::pbvh::BMeshNode *node_mut = const_cast<bke::pbvh::BMeshNode *>(node);
1551
1552 switch (type) {
1553 case Type::None:
1555 break;
1556 case Type::Position:
1557 case Type::Mask:
1558 /* Before any vertex values get modified, ensure their
1559 * original positions are logged. */
1560 for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(node_mut)) {
1561 BM_log_vert_before_modified(ss.bm_log, vert, cd_vert_mask_offset);
1562 }
1563 for (BMVert *vert : BKE_pbvh_bmesh_node_other_verts(node_mut)) {
1564 BM_log_vert_before_modified(ss.bm_log, vert, cd_vert_mask_offset);
1565 }
1566 break;
1567
1568 case Type::HideFace:
1569 case Type::HideVert: {
1570 for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(node_mut)) {
1571 BM_log_vert_before_modified(ss.bm_log, vert, cd_vert_mask_offset);
1572 }
1573 for (BMVert *vert : BKE_pbvh_bmesh_node_other_verts(node_mut)) {
1574 BM_log_vert_before_modified(ss.bm_log, vert, cd_vert_mask_offset);
1575 }
1576
1577 for (BMFace *f : BKE_pbvh_bmesh_node_faces(node_mut)) {
1579 }
1580 break;
1581 }
1582
1583 case Type::DyntopoBegin:
1584 case Type::DyntopoEnd:
1585 case Type::Geometry:
1586 case Type::FaceSet:
1587 case Type::Color:
1588 break;
1589 }
1590 }
1591}
1592
1597static Node *ensure_node(StepData &step_data, const bke::pbvh::Node &node, bool &r_new)
1598{
1599 std::scoped_lock lock(step_data.nodes_mutex);
1600 r_new = false;
1601 std::unique_ptr<Node> &unode = step_data.undo_nodes_by_pbvh_node.lookup_or_add_cb(&node, [&]() {
1602 std::unique_ptr<Node> unode = std::make_unique<Node>();
1603 r_new = true;
1604 return unode;
1605 });
1606 return unode.get();
1607}
1608
1609void push_node(const Depsgraph &depsgraph,
1610 const Object &object,
1611 const bke::pbvh::Node *node,
1612 Type type)
1613{
1614 SculptSession &ss = *object.sculpt;
1615 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1616 if (ss.bm || ELEM(type, Type::DyntopoBegin, Type::DyntopoEnd)) {
1617 bmesh_push(object, static_cast<const bke::pbvh::BMeshNode *>(node), type);
1618 return;
1619 }
1620
1621 StepData *step_data = get_step_data();
1622 BLI_assert(ELEM(step_data->type, Type::None, type));
1623 step_data->type = type;
1624
1625 bool newly_added;
1626 Node *unode = ensure_node(*step_data, *node, newly_added);
1627 if (!newly_added) {
1628 /* The node was already filled with data for this undo step. */
1629 return;
1630 }
1631
1632 ss.needs_flush_to_id = 1;
1633
1634 switch (pbvh.type()) {
1637 depsgraph, object, static_cast<const bke::pbvh::MeshNode &>(*node), type, *unode);
1638 break;
1640 fill_node_data_grids(object, static_cast<const bke::pbvh::GridsNode &>(*node), type, *unode);
1641 break;
1644 break;
1645 }
1646}
1647
1648void push_nodes(const Depsgraph &depsgraph,
1649 Object &object,
1650 const IndexMask &node_mask,
1651 const Type type)
1652{
1653 SculptSession &ss = *object.sculpt;
1654
1655 ss.needs_flush_to_id = 1;
1656
1657 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1658 if (ss.bm || ELEM(type, Type::DyntopoBegin, Type::DyntopoEnd)) {
1660 node_mask.foreach_index([&](const int i) { bmesh_push(object, &nodes[i], type); });
1661 return;
1662 }
1663
1664 StepData *step_data = get_step_data();
1665 BLI_assert(ELEM(step_data->type, Type::None, type));
1666 step_data->type = type;
1667
1668 switch (pbvh.type()) {
1669 case bke::pbvh::Type::Mesh: {
1672 node_mask.foreach_index([&](const int i) {
1673 bool newly_added;
1674 Node *unode = ensure_node(*step_data, nodes[i], newly_added);
1675 if (newly_added) {
1676 nodes_to_fill.append({&nodes[i], unode});
1677 }
1678 });
1679 threading::parallel_for(nodes_to_fill.index_range(), 1, [&](const IndexRange range) {
1680 for (const auto &[node, unode] : nodes_to_fill.as_span().slice(range)) {
1681 fill_node_data_mesh(depsgraph, object, *node, type, *unode);
1682 }
1683 });
1684 break;
1685 }
1689 node_mask.foreach_index([&](const int i) {
1690 bool newly_added;
1691 Node *unode = ensure_node(*step_data, nodes[i], newly_added);
1692 if (newly_added) {
1693 nodes_to_fill.append({&nodes[i], unode});
1694 }
1695 });
1696 threading::parallel_for(nodes_to_fill.index_range(), 1, [&](const IndexRange range) {
1697 for (const auto &[node, unode] : nodes_to_fill.as_span().slice(range)) {
1698 fill_node_data_grids(object, *node, type, *unode);
1699 }
1700 });
1701 break;
1702 }
1705 break;
1706 }
1707 }
1708}
1709
1710static void save_active_attribute(Object &object, SculptAttrRef *attr)
1711{
1713 attr->was_set = true;
1714 attr->domain = NO_ACTIVE_LAYER;
1715 attr->name[0] = 0;
1716 if (!mesh) {
1717 return;
1718 }
1719 const char *name = mesh->active_color_attribute;
1720 const bke::AttributeAccessor attributes = mesh->attributes();
1721 const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(name);
1722 if (!meta_data) {
1723 return;
1724 }
1725 if (!(ATTR_DOMAIN_AS_MASK(meta_data->domain) & ATTR_DOMAIN_MASK_COLOR) ||
1726 !(CD_TYPE_AS_MASK(meta_data->data_type) & CD_MASK_COLOR_ALL))
1727 {
1728 return;
1729 }
1730 attr->domain = meta_data->domain;
1731 STRNCPY(attr->name, name);
1732 attr->type = meta_data->data_type;
1733}
1734
1739{
1740 us->data.object_name = ob.id.name;
1741
1742 if (!us->active_color_start.was_set) {
1744 }
1745
1746 /* Set end attribute in case push_end is not called,
1747 * so we don't end up with corrupted state.
1748 */
1749 if (!us->active_color_end.was_set) {
1751 us->active_color_end.was_set = false;
1752 }
1753
1754 const SculptSession &ss = *ob.sculpt;
1755
1756 us->data.pivot_pos = ss.pivot_pos;
1757 us->data.pivot_rot = ss.pivot_rot;
1758
1759 if (const KeyBlock *key = BKE_keyblock_from_object(&ob)) {
1760 us->data.active_shape_key_name = key->name;
1761 }
1762}
1763
1764void push_begin_ex(const Scene & /*scene*/, Object &ob, const char *name)
1765{
1766 UndoStack *ustack = ED_undo_stack_get();
1767
1768 /* If possible, we need to tag the object and its geometry data as 'changed in the future' in
1769 * the previous undo step if it's a memfile one. */
1771 ED_undosys_stack_memfile_id_changed_tag(ustack, static_cast<ID *>(ob.data));
1772
1773 /* Special case, we never read from this. */
1774 bContext *C = nullptr;
1775
1777 ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT);
1778 us->data.object_name = ob.id.name;
1779
1780 if (!us->active_color_start.was_set) {
1782 }
1783
1784 /* Set end attribute in case push_end is not called,
1785 * so we don't end up with corrupted state.
1786 */
1787 if (!us->active_color_end.was_set) {
1789 us->active_color_end.was_set = false;
1790 }
1791
1792 const SculptSession &ss = *ob.sculpt;
1793 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1794
1795 switch (pbvh.type()) {
1796 case bke::pbvh::Type::Mesh: {
1797 const Mesh &mesh = *static_cast<const Mesh *>(ob.data);
1798 us->data.mesh_verts_num = mesh.verts_num;
1799 us->data.mesh_corners_num = mesh.corners_num;
1800 break;
1801 }
1805 break;
1806 }
1808 break;
1809 }
1810 }
1811
1812 /* Store sculpt pivot. */
1813 us->data.pivot_pos = ss.pivot_pos;
1814 us->data.pivot_rot = ss.pivot_rot;
1815
1816 if (const KeyBlock *key = BKE_keyblock_from_object(&ob)) {
1817 us->data.active_shape_key_name = key->name;
1818 }
1819}
1820
1821void push_begin(const Scene &scene, Object &ob, const wmOperator *op)
1822{
1823 push_begin_ex(scene, ob, op->type->name);
1824}
1825
1826void push_enter_sculpt_mode(const Scene & /*scene*/, Object &ob, const wmOperator *op)
1827{
1828 UndoStack *ustack = ED_undo_stack_get();
1829
1830 /* If possible, we need to tag the object and its geometry data as 'changed in the future' in
1831 * the previous undo step if it's a memfile one. */
1833 ED_undosys_stack_memfile_id_changed_tag(ustack, static_cast<ID *>(ob.data));
1834
1835 /* Special case, we never read from this. */
1836 bContext *C = nullptr;
1837
1838 SculptUndoStep *us = reinterpret_cast<SculptUndoStep *>(
1840 save_common_data(ob, us);
1841}
1842
1843static size_t node_size_in_bytes(const Node &node)
1844{
1845 size_t size = sizeof(Node);
1846 size += node.position.as_span().size_in_bytes();
1847 size += node.orig_position.as_span().size_in_bytes();
1848 size += node.normal.as_span().size_in_bytes();
1849 size += node.col.as_span().size_in_bytes();
1850 size += node.mask.as_span().size_in_bytes();
1851 size += node.loop_col.as_span().size_in_bytes();
1854 size += node.vert_hidden.size() / 8;
1855 size += node.face_hidden.size() / 8;
1856 size += node.grids.as_span().size_in_bytes();
1857 size += node.grid_hidden.all_bits().size() / 8;
1858 size += node.face_sets.as_span().size_in_bytes();
1860 return size;
1861}
1862
1863void push_end_ex(Object &ob, const bool use_nested_undo)
1864{
1865 StepData *step_data = get_step_data();
1866
1867 /* Move undo node storage from map to vector. */
1868 step_data->nodes.reserve(step_data->undo_nodes_by_pbvh_node.size());
1869 for (std::unique_ptr<Node> &node : step_data->undo_nodes_by_pbvh_node.values()) {
1870 step_data->nodes.append(std::move(node));
1871 }
1872 step_data->undo_nodes_by_pbvh_node.clear_and_shrink();
1873
1874 /* We don't need normals in the undo stack. */
1875 for (std::unique_ptr<Node> &unode : step_data->nodes) {
1876 unode->normal = {};
1877 }
1878
1880 step_data->nodes.index_range(),
1881 16,
1882 0,
1883 [&](const IndexRange range, size_t size) {
1884 for (const int i : range) {
1885 size += node_size_in_bytes(*step_data->nodes[i]);
1886 }
1887 return size;
1888 },
1889 std::plus<size_t>());
1890
1891 /* We could remove this and enforce all callers run in an operator using 'OPTYPE_UNDO'. */
1892 wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
1893 if (wm->op_undo_depth == 0 || use_nested_undo) {
1894 UndoStack *ustack = ED_undo_stack_get();
1895 BKE_undosys_step_push(ustack, nullptr, nullptr);
1896 if (wm->op_undo_depth == 0) {
1898 }
1900 }
1901
1902 UndoStack *ustack = ED_undo_stack_get();
1903 SculptUndoStep *us = (SculptUndoStep *)BKE_undosys_stack_init_or_active_with_type(
1904 ustack, BKE_UNDOSYS_TYPE_SCULPT);
1905
1906 save_active_attribute(ob, &us->active_color_end);
1907}
1908
1910{
1911 push_end_ex(ob, false);
1912}
1913
1914/* -------------------------------------------------------------------- */
1917
1919{
1920 if (attr->domain == bke::AttrDomain::Auto) {
1921 return;
1922 }
1923
1926
1927 SculptAttrRef existing;
1928 save_active_attribute(*ob, &existing);
1929
1931 CustomDataLayer *layer = BKE_attribute_find(owner, attr->name, attr->type, attr->domain);
1932
1933 /* Temporary fix for #97408. This is a fundamental
1934 * bug in the undo stack; the operator code needs to push
1935 * an extra undo step before running an operator if a
1936 * non-memfile undo system is active.
1937 *
1938 * For now, detect if the layer does exist but with a different
1939 * domain and just unconvert it.
1940 */
1941 if (!layer) {
1944 if (layer) {
1946 mesh, attr->name, eCustomDataType(attr->type), attr->domain, nullptr))
1947 {
1948 layer = BKE_attribute_find(owner, attr->name, attr->type, attr->domain);
1949 }
1950 }
1951 }
1952
1953 if (!layer) {
1954 /* Memfile undo killed the layer; re-create it. */
1955 mesh->attributes_for_write().add(
1956 attr->name, attr->domain, attr->type, bke::AttributeInitDefaultValue());
1957 layer = BKE_attribute_find(owner, attr->name, attr->type, attr->domain);
1959 }
1960
1961 if (layer) {
1963 }
1964}
1965
1966static void step_encode_init(bContext * /*C*/, UndoStep *us_p)
1967{
1968 SculptUndoStep *us = (SculptUndoStep *)us_p;
1969 new (&us->data) StepData();
1970}
1971
1972static bool step_encode(bContext * /*C*/, Main *bmain, UndoStep *us_p)
1973{
1974 /* Dummy, encoding is done along the way by adding tiles
1975 * to the current 'SculptUndoStep' added by encode_init. */
1976 SculptUndoStep *us = (SculptUndoStep *)us_p;
1977 us->step.data_size = us->data.undo_size;
1978
1979 Node *unode = us->data.nodes.is_empty() ? nullptr : us->data.nodes.last().get();
1980 if (unode && us->data.type == Type::DyntopoEnd) {
1981 us->step.use_memfile_step = true;
1982 }
1983 us->step.is_applied = true;
1984
1985 if (!us->data.nodes.is_empty()) {
1986 bmain->is_memfile_undo_flush_needed = true;
1987 }
1988
1989 return true;
1990}
1991
1993{
1994 BLI_assert(us->step.is_applied == true);
1995
1997 us->step.is_applied = false;
1998}
1999
2001{
2002 BLI_assert(us->step.is_applied == false);
2003
2005 us->step.is_applied = true;
2006}
2007
2009 Depsgraph *depsgraph,
2010 SculptUndoStep *us,
2011 const bool is_final)
2012{
2013 /* Walk forward over any applied steps of same type,
2014 * then walk back in the next loop, un-applying them. */
2015 SculptUndoStep *us_iter = us;
2016 while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) {
2017 if (us_iter->step.next->is_applied == false) {
2018 break;
2019 }
2020 us_iter = (SculptUndoStep *)us_iter->step.next;
2021 }
2022
2023 while ((us_iter != us) || (!is_final && us_iter == us)) {
2024 BLI_assert(us_iter->step.type == us->step.type); /* Previous loop ensures this. */
2025
2026 set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start);
2028
2029 if (us_iter == us) {
2030 if (us_iter->step.prev && us_iter->step.prev->type == BKE_UNDOSYS_TYPE_SCULPT) {
2031 set_active_layer(C, &((SculptUndoStep *)us_iter->step.prev)->active_color_end);
2032 }
2033 break;
2034 }
2035
2036 us_iter = (SculptUndoStep *)us_iter->step.prev;
2037 }
2038}
2039
2041{
2042 SculptUndoStep *us_iter = us;
2043 while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) {
2044 if (us_iter->step.prev->is_applied == true) {
2045 break;
2046 }
2047 us_iter = (SculptUndoStep *)us_iter->step.prev;
2048 }
2049 while (us_iter && (us_iter->step.is_applied == false)) {
2050 set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_end);
2052
2053 if (us_iter == us) {
2054 set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start);
2055 break;
2056 }
2057 us_iter = (SculptUndoStep *)us_iter->step.next;
2058 }
2059}
2060
2061static void step_decode(
2062 bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir dir, bool is_final)
2063{
2064 /* NOTE: behavior for undo/redo closely matches image undo. */
2065 BLI_assert(dir != STEP_INVALID);
2066
2068
2069 /* Ensure sculpt mode. */
2070 {
2071 Scene *scene = CTX_data_scene(C);
2072 ViewLayer *view_layer = CTX_data_view_layer(C);
2073 BKE_view_layer_synced_ensure(scene, view_layer);
2074 Object *ob = BKE_view_layer_active_object_get(view_layer);
2075 if (ob && (ob->type == OB_MESH)) {
2077 /* Pass. */
2078 }
2079 else {
2080 object::mode_generic_exit(bmain, depsgraph, scene, ob);
2081
2082 /* Sculpt needs evaluated state.
2083 * NOTE: needs to be done here, as #object::mode_generic_exit will usually invalidate
2084 * (some) evaluated data. */
2086
2087 Mesh *mesh = static_cast<Mesh *>(ob->data);
2088 /* Don't add sculpt topology undo steps when reading back undo state.
2089 * The undo steps must enter/exit for us. */
2091 object_sculpt_mode_enter(*bmain, *depsgraph, *scene, *ob, true, nullptr);
2092 }
2093
2094 if (ob->sculpt) {
2095 ob->sculpt->needs_flush_to_id = 1;
2096 }
2097 bmain->is_memfile_undo_flush_needed = true;
2098 }
2099 else {
2100 BLI_assert(0);
2101 return;
2102 }
2103 }
2104
2105 SculptUndoStep *us = (SculptUndoStep *)us_p;
2106 if (dir == STEP_UNDO) {
2107 step_decode_undo(C, depsgraph, us, is_final);
2108 }
2109 else if (dir == STEP_REDO) {
2111 }
2112}
2113
2114static void step_free(UndoStep *us_p)
2115{
2116 SculptUndoStep *us = (SculptUndoStep *)us_p;
2117 free_step_data(us->data);
2118}
2119
2120void geometry_begin(const Scene &scene, Object &ob, const wmOperator *op)
2121{
2122 push_begin(scene, ob, op);
2123 geometry_push(ob);
2124}
2125
2126void geometry_begin_ex(const Scene &scene, Object &ob, const char *name)
2127{
2128 push_begin_ex(scene, ob, name);
2129 geometry_push(ob);
2130}
2131
2133{
2134 geometry_push(ob);
2135 push_end(ob);
2136}
2137
2139{
2140 ut->name = "Sculpt";
2141 ut->poll = nullptr; /* No poll from context for now. */
2145 ut->step_free = step_free;
2146
2148
2149 ut->step_size = sizeof(SculptUndoStep);
2150}
2151
2153
2154/* -------------------------------------------------------------------- */
2175
2177{
2179 return false;
2180 }
2181
2182 Object *object = CTX_data_active_object(C);
2183 SculptSession *sculpt_session = object->sculpt;
2184
2185 return sculpt_session->multires.active;
2186}
2187
2188static void push_all_grids(const Depsgraph &depsgraph, Object *object)
2189{
2190 /* It is possible that undo push is done from an object state where there is no tree. This
2191 * happens, for example, when an operation which tagged for geometry update was performed prior
2192 * to the current operation without making any stroke in between.
2193 *
2194 * Skip pushing nodes based on the following logic: on redo Type::Position will
2195 * ensure pbvh::Tree for the new base geometry, which will have same coordinates as if we create
2196 * pbvh::Tree here.
2197 */
2198 const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(*object);
2199 if (pbvh == nullptr) {
2200 return;
2201 }
2202
2203 IndexMaskMemory memory;
2204 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(*pbvh, memory);
2205 push_nodes(depsgraph, *object, node_mask, Type::Position);
2206}
2207
2209{
2210 if (!use_multires_mesh(C)) {
2211 return;
2212 }
2213
2214 const Scene &scene = *CTX_data_scene(C);
2215 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
2216 Object *object = CTX_data_active_object(C);
2217
2218 push_begin_ex(scene, *object, str);
2219
2220 geometry_push(*object);
2221 get_step_data()->geometry_clear_pbvh = false;
2222
2223 push_all_grids(depsgraph, object);
2224}
2225
2227{
2228 if (!use_multires_mesh(C)) {
2229 ED_undo_push(C, str);
2230 return;
2231 }
2232
2233 Object *object = CTX_data_active_object(C);
2234
2235 geometry_push(*object);
2236 get_step_data()->geometry_clear_pbvh = false;
2237
2238 push_end(*object);
2239}
2240
2242
2243} // namespace blender::ed::sculpt_paint::undo
2244
2245namespace blender::ed::sculpt_paint {
2246
2247std::optional<OrigPositionData> orig_position_data_lookup_mesh_all_verts(
2248 const Object & /*object*/, const bke::pbvh::MeshNode &node)
2249{
2250 const undo::Node *unode = undo::get_node(&node, undo::Type::Position);
2251 if (!unode) {
2252 return std::nullopt;
2253 }
2254 return OrigPositionData{unode->position.as_span(), unode->normal.as_span()};
2255}
2256
2257std::optional<OrigPositionData> orig_position_data_lookup_mesh(const Object &object,
2258 const bke::pbvh::MeshNode &node)
2259{
2260 std::optional<OrigPositionData> result = orig_position_data_lookup_mesh_all_verts(object, node);
2261 if (!result) {
2262 return std::nullopt;
2263 }
2264 return OrigPositionData{result->positions.take_front(node.verts().size()),
2265 result->normals.take_front(node.verts().size())};
2266}
2267
2268std::optional<OrigPositionData> orig_position_data_lookup_grids(const Object & /*object*/,
2269 const bke::pbvh::GridsNode &node)
2270{
2271 const undo::Node *unode = undo::get_node(&node, undo::Type::Position);
2272 if (!unode) {
2273 return std::nullopt;
2274 }
2275 return OrigPositionData{unode->position.as_span(), unode->normal.as_span()};
2276}
2277
2279 const Set<BMVert *, 0> &verts,
2280 const MutableSpan<float3> positions,
2282{
2283 int i = 0;
2284 for (const BMVert *vert : verts) {
2285 const float *co;
2286 const float *no;
2287 BM_log_original_vert_data(&const_cast<BMLog &>(bm_log), const_cast<BMVert *>(vert), &co, &no);
2288 if (!positions.is_empty()) {
2289 positions[i] = co;
2290 }
2291 if (!normals.is_empty()) {
2292 normals[i] = no;
2293 }
2294 i++;
2295 }
2296}
2297
2298std::optional<Span<float4>> orig_color_data_lookup_mesh(const Object & /*object*/,
2299 const bke::pbvh::MeshNode &node)
2300{
2301 const undo::Node *unode = undo::get_node(&node, undo::Type::Color);
2302 if (!unode) {
2303 return std::nullopt;
2304 }
2305 return unode->col.as_span();
2306}
2307
2308std::optional<Span<int>> orig_face_set_data_lookup_mesh(const Object & /*object*/,
2309 const bke::pbvh::MeshNode &node)
2310{
2311 const undo::Node *unode = undo::get_node(&node, undo::Type::FaceSet);
2312 if (!unode) {
2313 return std::nullopt;
2314 }
2315 return unode->face_sets.as_span();
2316}
2317
2318std::optional<Span<int>> orig_face_set_data_lookup_grids(const Object & /*object*/,
2319 const bke::pbvh::GridsNode &node)
2320{
2321 const undo::Node *unode = undo::get_node(&node, undo::Type::FaceSet);
2322 if (!unode) {
2323 return std::nullopt;
2324 }
2325 return unode->face_sets.as_span();
2326}
2327
2328std::optional<Span<float>> orig_mask_data_lookup_mesh(const Object & /*object*/,
2329 const bke::pbvh::MeshNode &node)
2330{
2331 const undo::Node *unode = undo::get_node(&node, undo::Type::Mask);
2332 if (!unode) {
2333 return std::nullopt;
2334 }
2335 return unode->mask.as_span();
2336}
2337
2338std::optional<Span<float>> orig_mask_data_lookup_grids(const Object & /*object*/,
2339 const bke::pbvh::GridsNode &node)
2340{
2341 const undo::Node *unode = undo::get_node(&node, undo::Type::Mask);
2342 if (!unode) {
2343 return std::nullopt;
2344 }
2345 return unode->mask.as_span();
2346}
2347
2348} // namespace blender::ed::sculpt_paint
struct CustomDataLayer * BKE_attribute_search_for_write(AttributeOwner &owner, const char *name, eCustomDataMask type, AttrDomainMask domain_mask)
Definition attribute.cc:669
void BKE_id_attributes_active_color_set(struct ID *id, const char *name)
Definition attribute.cc:965
@ ATTR_DOMAIN_MASK_ALL
struct CustomDataLayer * BKE_attribute_find(const AttributeOwner &owner, const char *name, eCustomDataType type, blender::bke::AttrDomain domain)
Definition attribute.cc:611
#define ATTR_DOMAIN_MASK_COLOR
#define ATTR_DOMAIN_AS_MASK(domain)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
void CustomData_init_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, int totelem)
void CustomData_free(CustomData *data, int totelem)
#define CD_TYPE_AS_MASK(_type)
const CustomData_MeshMasks CD_MASK_MESH
#define G_MAIN
KeyBlock * BKE_keyblock_from_object(Object *ob)
Definition key.cc:1905
void BKE_keyblock_update_from_offset(const Object *ob, KeyBlock *kb, const float(*ofs)[3])
Definition key.cc:2447
KeyBlock * BKE_keyblock_find_name(Key *key, const char name[])
Definition key.cc:1932
std::optional< blender::Array< bool > > BKE_keyblock_get_dependent_keys(const Key *key, int index)
Definition key.cc:2578
void BKE_keyblock_update_from_vertcos(const Object *ob, KeyBlock *kb, const float(*vertCos)[3])
Definition key.cc:2297
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1820
float(* BKE_keyblock_convert_to_vertcos(const Object *ob, const KeyBlock *kb))[3]
Definition key.cc:2389
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
void BKE_mesh_clear_geometry(Mesh *mesh)
blender::Array< blender::float3 > BKE_multires_create_deformed_base_mesh_vert_coords(Depsgraph *depsgraph, Object *object, MultiresModifierData *mmd)
Definition multires.cc:244
void multires_mark_as_modified(Depsgraph *depsgraph, Object *object, MultiresModifiedFlags flags)
Definition multires.cc:373
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_original_mesh(const Object *object)
bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const RegionView3D *rv3d)
Definition paint.cc:2862
void BKE_sculptsession_free_deformMats(SculptSession *ss)
Definition paint.cc:2049
MultiresModifierData * BKE_sculpt_multires_active(const Scene *scene, Object *ob)
Definition paint.cc:2316
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2601
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2099
PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
Definition paint.cc:506
const blender::Set< BMFace *, 0 > & BKE_pbvh_bmesh_node_faces(blender::bke::pbvh::BMeshNode *node)
const blender::Set< BMVert *, 0 > & BKE_pbvh_bmesh_node_unique_verts(blender::bke::pbvh::BMeshNode *node)
const blender::Set< BMVert *, 0 > & BKE_pbvh_bmesh_node_other_verts(blender::bke::pbvh::BMeshNode *node)
void BKE_pbvh_sync_visibility_from_verts(Object &object)
Definition pbvh.cc:2530
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2573
CCGKey BKE_subdiv_ccg_key_top_level(const SubdivCCG &subdiv_ccg)
blender::BitGroupVector & BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG &subdiv_ccg)
void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG &subdiv_ccg)
@ MULTIRES_HIDDEN_MODIFIED
@ MULTIRES_COORDS_MODIFIED
@ UNDOTYPE_FLAG_DECODE_ACTIVE_STEP
const UndoType * BKE_UNDOSYS_TYPE_SCULPT
eUndoPushReturn BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name)
eUndoStepDir
@ STEP_INVALID
@ STEP_UNDO
@ STEP_REDO
UndoStep * BKE_undosys_step_push_init_with_type(UndoStack *ustack, bContext *C, const char *name, const UndoType *ut)
#define BKE_undosys_stack_limit_steps_and_memory_defaults(ustack)
UndoStep * BKE_undosys_stack_init_or_active_with_type(UndoStack *ustack, const UndoType *ut)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_NOINLINE
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define ELEM(...)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SHADING
Definition DNA_ID.h:1061
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define ID_REAL_USERS(id)
Definition DNA_ID.h:637
#define MAX_CUSTOMDATA_LAYER_NAME
#define CD_MASK_PROP_ALL
#define CD_MASK_COLOR_ALL
@ CD_PROP_FLOAT
@ ME_SCULPT_DYNAMIC_TOPOLOGY
@ OB_MODE_SCULPT
@ OB_MODE_VERTEX_PAINT
Object is a sort of wrapper for general info.
@ OB_MESH
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:104
UndoStack * ED_undo_stack_get()
Definition ed_undo.cc:455
void ED_undosys_stack_memfile_id_changed_tag(UndoStack *ustack, ID *id)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define ND_DATA
Definition WM_types.hh:475
#define NC_OBJECT
Definition WM_types.hh:346
volatile int lock
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name)
void BM_log_all_added(BMesh *bm, BMLog *log)
Definition bmesh_log.cc:874
void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no)
Definition bmesh_log.cc:988
void BM_log_face_modified(BMLog *log, BMFace *f)
Definition bmesh_log.cc:801
void BM_log_redo(BMesh *bm, BMLog *log)
Definition bmesh_log.cc:738
void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_offset)
Definition bmesh_log.cc:772
void BM_log_entry_drop(BMLogEntry *entry)
Definition bmesh_log.cc:651
BMLog * BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry)
Definition bmesh_log.cc:504
void BM_log_undo(BMesh *bm, BMLog *log)
Definition bmesh_log.cc:717
void BM_log_before_all_removed(BMesh *bm, BMLog *log)
Definition bmesh_log.cc:902
BMLogEntry * BM_log_entry_add(BMLog *log)
Definition bmesh_log.cc:624
const BMAllocTemplate bm_mesh_allocsize_default
Definition bmesh_mesh.cc:29
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
void BM_mesh_normals_update(BMesh *bm)
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
Definition BLI_array.hh:245
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
IndexRange index_range() const
Definition BLI_array.hh:349
void fill(const T &value) const
Definition BLI_array.hh:261
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
bool is_empty() const
Definition BLI_array.hh:253
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
constexpr int64_t size_in_bytes() const
Definition BLI_span.hh:269
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr Span take_front(int64_t n) const
Definition BLI_span.hh:194
int64_t size() const
void append(const T &value)
Span< T > as_span() const
constexpr IndexRange take_front(int64_t n) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr bool is_empty() const
Definition BLI_span.hh:510
constexpr void fill_indices(Span< IndexT > indices, const T &value) const
Definition BLI_span.hh:527
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
Span< T > as_span() const
IndexRange index_range() const
GAttributeReader lookup(const StringRef attribute_id) const
std::optional< AttributeMetaData > lookup_meta_data(const StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
void tag_attribute_changed(const IndexMask &node_mask, StringRef attribute_name)
Definition pbvh.cc:593
void tag_positions_changed(const IndexMask &node_mask)
Definition pbvh.cc:549
Span< NodeT > nodes() const
void tag_face_sets_changed(const IndexMask &node_mask)
Definition pbvh.cc:579
void tag_masks_changed(const IndexMask &node_mask)
Definition pbvh.cc:586
void tag_visibility_changed(const IndexMask &node_mask)
Definition pbvh.cc:562
void foreach_index(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
draw_view in_light_buf[] float
#define str(s)
static ushort indices[]
static float verts[][3]
static float normals[][3]
bool ED_geometry_attribute_convert(Mesh *mesh, const char *name, const eCustomDataType dst_type, const blender::bke::AttrDomain dst_domain, ReportList *reports)
#define LOG(severity)
Definition log.h:33
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static char faces[256]
IndexRange grid_range(const int grid_area, const int grid)
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
void update_bounds(const Depsgraph &depsgraph, const Object &object, Tree &pbvh)
Definition pbvh.cc:1183
void update_mask_mesh(const Mesh &mesh, const IndexMask &node_mask, Tree &pbvh)
Definition pbvh.cc:1230
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2612
void update_visibility(const Object &object, Tree &pbvh)
Definition pbvh.cc:1377
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2502
void update_mask_grids(const SubdivCCG &subdiv_ccg, const IndexMask &node_mask, Tree &pbvh)
Definition pbvh.cc:1261
MutableSpan< float3 > vert_positions_eval_for_write(const Depsgraph &depsgraph, Object &object_orig)
Definition pbvh.cc:2496
void store_bounds_orig(Tree &pbvh)
Definition pbvh.cc:1206
Span< int > node_face_indices_calc_grids(const SubdivCCG &subdiv_ccg, const GridsNode &node, Vector< int > &faces)
Definition pbvh.cc:1583
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2482
bool eval_refine_from_mesh(Subdiv *subdiv, const Mesh *mesh, Span< float3 > coarse_vert_positions)
void mode_generic_exit(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
bke::GSpanAttributeWriter active_color_attribute_for_write(Mesh &mesh)
void gather_colors_vert(OffsetIndices< int > faces, Span< int > corner_verts, GroupedSpan< int > vert_to_face_map, GSpan color_attribute, bke::AttrDomain color_domain, Span< int > verts, MutableSpan< float4 > r_colors)
void gather_colors(GSpan color_attribute, Span< int > indices, MutableSpan< float4 > r_colors)
void swap_gathered_colors(Span< int > indices, GMutableSpan color_attribute, MutableSpan< float4 > r_colors)
bke::GAttributeReader active_color_attribute(const Mesh &mesh)
static void disable(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob, undo::StepData *undo_step)
bke::SpanAttributeWriter< int > ensure_face_sets_mesh(Mesh &mesh)
void sync_all_from_faces(Object &object)
Definition paint_hide.cc:59
static void save_active_attribute(Object &object, SculptAttrRef *attr)
void push_nodes(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, const Type type)
static void store_vert_visibility_grids(const SubdivCCG &subdiv_ccg, const bke::pbvh::GridsNode &node, Node &unode)
static bool use_multires_mesh(bContext *C)
static void geometry_push(const Object &object)
static void store_positions_grids(const SubdivCCG &subdiv_ccg, Node &unode)
static BLI_NOINLINE void bmesh_push(const Object &object, const bke::pbvh::BMeshNode *node, Type type)
static void free_step_data(StepData &step_data)
void push_multires_mesh_begin(bContext *C, const char *str)
void push_multires_mesh_end(bContext *C, const char *str)
static void fill_node_data_mesh(const Depsgraph &depsgraph, const Object &object, const bke::pbvh::MeshNode &node, const Type type, Node &unode)
static size_t node_size_in_bytes(const Node &node)
static bool topology_matches(const StepData &step_data, const Object &object)
static SculptUndoStep * get_active_step()
static void store_face_visibility(const Mesh &mesh, Node &unode)
static void step_encode_init(bContext *, UndoStep *us_p)
static void restore_mask_grids(Object &object, Node &unode, MutableSpan< bool > modified_grids)
void push_node(const Depsgraph &depsgraph, const Object &object, const bke::pbvh::Node *node, Type type)
static bool use_multires_undo(const StepData &step_data, const SculptSession &ss)
void push_begin_ex(const Scene &, Object &ob, const char *name)
static void step_decode_redo_impl(bContext *C, Depsgraph *depsgraph, SculptUndoStep *us)
static void restore_geometry_data(const NodeGeometry *geometry, Mesh *mesh)
static void restore_vert_visibility_grids(SubdivCCG &subdiv_ccg, Node &unode, MutableSpan< bool > modified_grids)
void push_enter_sculpt_mode(const Scene &, Object &ob, const wmOperator *op)
static StepData * get_step_data()
static void store_vert_visibility_mesh(const Mesh &mesh, const bke::pbvh::Node &node, Node &unode)
static void restore_mask_mesh(Object &object, Node &unode, MutableSpan< bool > modified_vertices)
static void store_mask_grids(const SubdivCCG &subdiv_ccg, Node &unode)
static NodeGeometry * geometry_get(StepData &step_data)
static void store_positions_mesh(const Depsgraph &depsgraph, const Object &object, Node &unode)
static void fill_node_data_grids(const Object &object, const bke::pbvh::GridsNode &node, const Type type, Node &unode)
static void geometry_free_data(NodeGeometry *geometry)
static void step_decode_undo(bContext *C, Depsgraph *depsgraph, SculptUndoStep *us, const bool is_final)
void geometry_begin_ex(const Scene &scene, Object &ob, const char *name)
static void step_free(UndoStep *us_p)
static int bmesh_restore(bContext *C, Depsgraph &depsgraph, StepData &step_data, Object &object, SculptSession &ss)
static const Node * get_node(const bke::pbvh::Node *node, const Type type)
static bool step_encode(bContext *, Main *bmain, UndoStep *us_p)
static bool indices_contain_true(const Span< bool > data, const Span< int > indices)
static Node * ensure_node(StepData &step_data, const bke::pbvh::Node &node, bool &r_new)
static void save_common_data(Object &ob, SculptUndoStep *us)
void geometry_begin(const Scene &scene, Object &ob, const wmOperator *op)
static void restore_position_grids(MutableSpan< float3 > positions, const CCGKey &key, Node &unode, MutableSpan< bool > modified_grids)
static void step_decode_undo_impl(bContext *C, Depsgraph *depsgraph, SculptUndoStep *us)
static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
static void restore_hidden_face(Object &object, Node &unode, MutableSpan< bool > modified_faces)
static void step_decode_redo(bContext *C, Depsgraph *depsgraph, SculptUndoStep *us)
static void store_geometry_data(NodeGeometry *geometry, const Object &object)
static void store_mask_mesh(const Mesh &mesh, Node &unode)
static void bmesh_restore_generic(StepData &step_data, Object &object, SculptSession &ss)
static void restore_position_mesh(const Depsgraph &depsgraph, Object &object, const Span< std::unique_ptr< Node > > unodes, MutableSpan< bool > modified_verts)
static bool restore_active_shape_key(bContext &C, Depsgraph &depsgraph, const StepData &step_data, Object &object)
static void refine_subdiv(Depsgraph *depsgraph, SculptSession &ss, Object &object, bke::subdiv::Subdiv *subdiv)
static void store_color(const Mesh &mesh, const bke::pbvh::MeshNode &node, Node &unode)
static void update_shapekeys(const Object &ob, KeyBlock *kb, const Span< float3 > new_positions)
static void restore_vert_visibility_mesh(Object &object, Node &unode, MutableSpan< bool > modified_vertices)
void push_begin(const Scene &scene, Object &ob, const wmOperator *op)
static void bmesh_enable(Object &object, StepData &step_data)
static void bmesh_restore_end(bContext *C, StepData &step_data, Object &object, SculptSession &ss)
void restore_from_bmesh_enter_geometry(const StepData &step_data, Mesh &mesh)
static void store_face_sets(const Mesh &mesh, Node &unode)
static void step_decode(bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir dir, bool is_final)
static void restore_color(Object &object, StepData &step_data, MutableSpan< bool > modified_vertices)
static bool restore_face_sets(Object &object, Node &unode, MutableSpan< bool > modified_face_set_faces)
static void push_all_grids(const Depsgraph &depsgraph, Object *object)
void push_end_ex(Object &ob, const bool use_nested_undo)
static void restore_geometry(StepData &step_data, Object &object)
static void bmesh_restore_begin(bContext *C, StepData &step_data, Object &object, SculptSession &ss)
static void set_active_layer(bContext *C, SculptAttrRef *attr)
void object_sculpt_mode_enter(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob, bool force_dyntopo, ReportList *reports)
std::optional< Span< float > > orig_mask_data_lookup_grids(const Object &object, const bke::pbvh::GridsNode &node)
std::optional< Span< int > > orig_face_set_data_lookup_mesh(const Object &object, const bke::pbvh::MeshNode &node)
void gather_data_grids(const SubdivCCG &subdiv_ccg, Span< T > src, Span< int > grids, MutableSpan< T > node_data)
Definition sculpt.cc:6106
std::optional< OrigPositionData > orig_position_data_lookup_grids(const Object &object, const bke::pbvh::GridsNode &node)
void orig_position_data_gather_bmesh(const BMLog &bm_log, const Set< BMVert *, 0 > &verts, MutableSpan< float3 > positions, MutableSpan< float3 > normals)
std::optional< Span< float4 > > orig_color_data_lookup_mesh(const Object &object, const bke::pbvh::MeshNode &node)
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6096
std::optional< Span< float > > orig_mask_data_lookup_mesh(const Object &object, const bke::pbvh::MeshNode &node)
std::optional< OrigPositionData > orig_position_data_lookup_mesh_all_verts(const Object &object, const bke::pbvh::MeshNode &node)
std::optional< OrigPositionData > orig_position_data_lookup_mesh(const Object &object, const bke::pbvh::MeshNode &node)
std::optional< Span< int > > orig_face_set_data_lookup_grids(const Object &object, const bke::pbvh::GridsNode &node)
void copy_shared_pointer(T *src_ptr, const ImplicitSharingInfo *src_sharing_info, T **r_dst_ptr, const ImplicitSharingInfo **r_dst_sharing_info)
void free_shared_data(T **data, const ImplicitSharingInfo **sharing_info)
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
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:153
VecBase< float, 4 > float4
VecBase< float, 3 > float3
#define NO_ACTIVE_LAYER
CustomData vdata
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
char name[64]
void * data
ListBase block
bool is_memfile_undo_flush_needed
Definition BKE_main.hh:165
struct SculptSession * sculpt
BMLog * bm_log
Definition BKE_paint.hh:402
KeyBlock * shapekey_active
Definition BKE_paint.hh:387
blender::float4 pivot_rot
Definition BKE_paint.hh:463
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:405
struct SculptSession::@251246223064352150223005211001050162321211037052 multires
blender::float3 pivot_pos
Definition BKE_paint.hh:462
char needs_flush_to_id
Definition BKE_paint.hh:497
MultiresModifierData * modifier
Definition BKE_paint.hh:383
bool deform_modifiers_active
Definition BKE_paint.hh:411
blender::Array< blender::float3 > normals
blender::BitGroupVector grid_hidden
blender::Array< float > masks
blender::Array< blender::float3 > positions
size_t data_size
UndoStep * prev
UndoStep * next
const UndoType * type
bool use_memfile_step
void(* step_encode_init)(bContext *C, UndoStep *us)
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)
Span< int > faces() const
Span< int > verts() const
Span< int > all_verts() const
const ImplicitSharingInfo * face_offsets_sharing_info
char name[MAX_CUSTOMDATA_LAYER_NAME]
Vector< std::unique_ptr< Node > > nodes
Map< const bke::pbvh::Node *, std::unique_ptr< Node > > undo_nodes_by_pbvh_node
const char * name
Definition WM_types.hh:990
struct wmOperatorType * type
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_file_tag_modified()
Definition wm_files.cc:171