Commit: 00eb7594b11a6dfd1f1957a7f037b6ea9a3b1a13
Author: Jeroen Bakker
Date:   Mon May 16 15:42:43 2022 +0200
Branches: master
https://developer.blender.org/rB00eb7594b11a6dfd1f1957a7f037b6ea9a3b1a13

3D Texturing: Undo.

Blender can only support a single undo system per undo step. As 
sculpting/vertex colors are mutual exclusive operations
out approach is just to switch the undo system when painting on an image.

PBVHNodes contain a list of areas that needs to be pushed to the undo system.

Currently the undo code is in sculpt_paint_image. We should eventually support 
undo for color filtering and other nodes.
we might need to place it to its own compile unit so more brushes can invoke 
the same code.

{F13048942}

Reviewed By: brecht

Maniphest Tasks: T97479

Differential Revision: https://developer.blender.org/D14821

===================================================================

M       source/blender/blenkernel/BKE_pbvh_pixels.hh
M       source/blender/blenkernel/intern/pbvh_pixels.cc
M       source/blender/editors/sculpt_paint/sculpt.c
M       source/blender/editors/sculpt_paint/sculpt_paint_image.cc
M       source/blender/editors/space_image/image_undo.c

===================================================================

diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh 
b/source/blender/blenkernel/BKE_pbvh_pixels.hh
index fdfb5fa037e..e73950e6299 100644
--- a/source/blender/blenkernel/BKE_pbvh_pixels.hh
+++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh
@@ -132,12 +132,22 @@ struct UDIMTilePixels {
   }
 };
 
+struct UDIMTileUndo {
+  short tile_number;
+  rcti region;
+
+  UDIMTileUndo(short tile_number, rcti &region) : tile_number(tile_number), 
region(region)
+  {
+  }
+};
+
 struct NodeData {
   struct {
     bool dirty : 1;
   } flags;
 
   Vector<UDIMTilePixels> tiles;
+  Vector<UDIMTileUndo> undo_regions;
   Triangles triangles;
 
   NodeData()
@@ -155,6 +165,23 @@ struct NodeData {
     return nullptr;
   }
 
+  void rebuild_undo_regions()
+  {
+    undo_regions.clear();
+    for (UDIMTilePixels &tile : tiles) {
+      rcti region;
+      BLI_rcti_init_minmax(&region);
+      for (PackedPixelRow &pixel_row : tile.pixel_rows) {
+        BLI_rcti_do_minmax_v(
+            &region, int2(pixel_row.start_image_coordinate.x, 
pixel_row.start_image_coordinate.y));
+        BLI_rcti_do_minmax_v(&region,
+                             int2(pixel_row.start_image_coordinate.x + 
pixel_row.num_pixels + 1,
+                                  pixel_row.start_image_coordinate.y + 1));
+      }
+      undo_regions.append(UDIMTileUndo(tile.tile_number, region));
+    }
+  }
+
   void mark_region(Image &image, const image::ImageTileWrapper &image_tile, 
ImBuf &image_buffer)
   {
     UDIMTilePixels *tile = find_tile_data(image_tile);
diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc 
b/source/blender/blenkernel/intern/pbvh_pixels.cc
index 40742ad36d5..49397797c0d 100644
--- a/source/blender/blenkernel/intern/pbvh_pixels.cc
+++ b/source/blender/blenkernel/intern/pbvh_pixels.cc
@@ -308,6 +308,12 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image 
*image, ImageUser *image
     apply_watertight_check(pbvh, image, image_user);
   }
 
+  /* Rebuild the undo regions. */
+  for (PBVHNode *node : nodes_to_update) {
+    NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
+    node_data->rebuild_undo_regions();
+  }
+
   /* Clear the UpdatePixels flag. */
   for (PBVHNode *node : nodes_to_update) {
     node->flag = static_cast<PBVHNodeFlags>(node->flag & ~PBVH_RebuildPixels);
diff --git a/source/blender/editors/sculpt_paint/sculpt.c 
b/source/blender/editors/sculpt_paint/sculpt.c
index 85ea5d5bfc6..5bb4eb7cebf 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -3248,8 +3248,8 @@ static void do_brush_action(Sculpt *sd,
     }
     nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, 
radius_scale, &totnode);
   }
-
-  if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) {
+  const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, 
ob);
+  if (use_pixels) {
     sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob);
   }
 
@@ -3302,16 +3302,18 @@ static void do_brush_action(Sculpt *sd,
   }
   float location[3];
 
-  SculptThreadedTaskData task_data = {
-      .sd = sd,
-      .ob = ob,
-      .brush = brush,
-      .nodes = nodes,
-  };
+  if (!use_pixels) {
+    SculptThreadedTaskData task_data = {
+        .sd = sd,
+        .ob = ob,
+        .brush = brush,
+        .nodes = nodes,
+    };
 
-  TaskParallelSettings settings;
-  BKE_pbvh_parallel_range_settings(&settings, true, totnode);
-  BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, 
&settings);
+    TaskParallelSettings settings;
+    BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+    BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, 
&settings);
+  }
 
   if (sculpt_brush_needs_normal(ss, brush)) {
     update_sculpt_normal(sd, ob, nodes, totnode);
@@ -5276,6 +5278,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct 
wmOperator *op, const f
     SculptSession *ss = ob->sculpt;
     Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
     Brush *brush = BKE_paint_brush(&sd->paint);
+    ToolSettings *tool_settings = CTX_data_tool_settings(C);
 
     /* NOTE: This should be removed when paint mode is available. Paint mode 
can force based on the
      * canvas it is painting on. (ref. use_sculpt_texture_paint). */
@@ -5293,7 +5296,15 @@ static bool sculpt_stroke_test_start(bContext *C, struct 
wmOperator *op, const f
     SculptCursorGeometryInfo sgi;
     SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
 
-    SCULPT_undo_push_begin(ob, sculpt_tool_name(sd));
+    /* Setup the correct undo system. Image painting and sculpting are mutual 
exclusive.
+     * Color attributes are part of the sculpting undo system. */
+    if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT &&
+        SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) {
+      ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT);
+    }
+    else {
+      SCULPT_undo_push_begin(ob, sculpt_tool_name(sd));
+    }
 
     return true;
   }
@@ -5420,7 +5431,13 @@ static void sculpt_stroke_done(const bContext *C, struct 
PaintStroke *UNUSED(str
   SCULPT_cache_free(ss->cache);
   ss->cache = NULL;
 
-  SCULPT_undo_push_end(ob);
+  if (brush && brush->sculpt_tool == SCULPT_TOOL_PAINT &&
+      SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) {
+    ED_image_undo_push_end();
+  }
+  else {
+    SCULPT_undo_push_end(ob);
+  }
 
   if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
     SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc 
b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
index df1ccc0fbe9..b0c33a65bc9 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
@@ -360,6 +360,86 @@ static void do_paint_pixels(void *__restrict userdata,
   node_data.flags.dirty |= pixels_updated;
 }
 
+static void undo_region_tiles(
+    ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int 
*th)
+{
+  int srcx = 0, srcy = 0;
+  IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h);
+  *tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS);
+  *th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS);
+  *tx = (x >> ED_IMAGE_UNDO_TILE_BITS);
+  *ty = (y >> ED_IMAGE_UNDO_TILE_BITS);
+}
+
+static void push_undo(const NodeData &node_data,
+                      Image &image,
+                      ImageUser &image_user,
+                      const image::ImageTileWrapper &image_tile,
+                      ImBuf &image_buffer,
+                      ImBuf **tmpibuf)
+{
+  for (const UDIMTileUndo &tile_undo : node_data.undo_regions) {
+    if (tile_undo.tile_number != image_tile.get_tile_number()) {
+      continue;
+    }
+    int tilex, tiley, tilew, tileh;
+    ListBase *undo_tiles = ED_image_paint_tile_list_get();
+    undo_region_tiles(&image_buffer,
+                      tile_undo.region.xmin,
+                      tile_undo.region.ymin,
+                      BLI_rcti_size_x(&tile_undo.region),
+                      BLI_rcti_size_y(&tile_undo.region),
+                      &tilex,
+                      &tiley,
+                      &tilew,
+                      &tileh);
+    for (int ty = tiley; ty <= tileh; ty++) {
+      for (int tx = tilex; tx <= tilew; tx++) {
+        ED_image_paint_tile_push(undo_tiles,
+                                 &image,
+                                 &image_buffer,
+                                 tmpibuf,
+                                 &image_user,
+                                 tx,
+                                 ty,
+                                 nullptr,
+                                 nullptr,
+                                 true,
+                                 true);
+      }
+    }
+  }
+}
+
+static void do_push_undo_tile(void *__restrict userdata,
+                              const int n,
+                              const TaskParallelTLS *__restrict UNUSED(tls))
+{
+  TexturePaintingUserData *data = static_cast<TexturePaintingUserData 
*>(userdata);
+  PBVHNode *node = data->nodes[n];
+
+  NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node);
+  Image *image = data->image_data.image;
+  ImageUser *image_user = data->image_data.image_user;
+
+  ImBuf *tmpibuf = nullptr;
+  ImageUser local_image_user = *image_user;
+  LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
+    image::ImageTileWrapper image_tile(tile);
+    local_image_user.tile = image_tile.get_tile_number();
+    ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &local_image_user, 
nullptr);
+    if (image_buffer == nullptr) {
+      continue;
+    }
+
+    push_undo(node_data, *image, *image_user, image_tile, *image_buffer, 
&tmpibuf);
+    BKE_image_release_ibuf(image, image_buffer, nullptr);
+  }
+  if (tmpibuf) {
+    IMB_freeImBuf(tmpibuf);
+  }
+}
+
 static void do_mark_dirty_regions(void *__restrict userdata,
                                   const int n,
                                   const TaskParallelTLS *__restrict 
UNUSED(tls))
@@ -421,6 +501,7 @@ void SCULPT_do_paint_brush_image(
 
   TaskParallelSettings settings;
   BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+  BLI_task_parallel_range(0, totnode, &data, do_push_undo_tile, &settings);
   BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings);
 
   TaskParallelSettings settings_flush;
diff --git a/source/blender/editors/space_image/image_undo.c 
b/source/blender/editors/space_image/image_undo.c
index c3a48abcae1..1fd9fde084b 100644
--- a/source/blender/editors/space_image/image_undo.c
+++ b/source/blender/editors/space_image/image_undo.c
@@ -1040,7 +1040,7 @@ static ImageUndoStep *image_undo_push_begin(const char 
*name, int paint_mode)
   bContext *C = NULL; /* special case, we never read from this. */
   UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, 
BKE_UNDOSYS_TYPE_IMAGE);
   ImageUndoStep *us = (ImageUndoStep *)us_p;
-  BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D));
+  BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, 
PAINT_MODE_SCULPT));
   us->paint_mode = pa

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
List details, subscription details or unsubscribe:
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to