Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Eisel <julian@blender.org>2020-07-01 18:13:57 +0300
committerJulian Eisel <julian@blender.org>2020-07-01 18:13:57 +0300
commit0829cebeb024095c268f190c34daa8ae9a5a224c (patch)
tree12ee5a4a1c2a32e12eff47c8eb9bb0ed217791c1 /source/blender/editors/sculpt_paint
parentcfde6ebf450594faa57c4bfeaecff10fe512c91b (diff)
parent42be3964eb201180f6b0fa1ff6ce43b8c3845bc2 (diff)
Merge branch 'master' into asset-uuid--archivedasset-uuid--archived
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt2
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c13
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c20
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c5
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c687
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c40
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c323
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c17
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h40
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c477
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c26
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c50
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c11
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c68
23 files changed, 1644 insertions, 191 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index b8754953741..ed87d524627 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -64,10 +64,12 @@ set(SRC
sculpt_detail.c
sculpt_dyntopo.c
sculpt_face_set.c
+ sculpt_filter_color.c
sculpt_filter_mask.c
sculpt_filter_mesh.c
sculpt_mask_expand.c
sculpt_multiplane_scrape.c
+ sculpt_paint_color.c
sculpt_pose.c
sculpt_smooth.c
sculpt_transform.c
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 4222a466a7b..1291de04634 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -1441,7 +1441,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
* cursor won't be tagged to update, so always initialize the preview chain if it is
* null before drawing it. */
if (update_previews || !ss->pose_ik_chain_preview) {
- BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false, false);
/* Free the previous pose brush preview. */
if (ss->pose_ik_chain_preview) {
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 08af3bdd16c..7f64fdf3501 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -1301,7 +1301,7 @@ static bool brush_colors_flip_poll(bContext *C)
else {
Object *ob = CTX_data_active_object(C);
if (ob != NULL) {
- if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) {
+ if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT | OB_MODE_SCULPT)) {
return true;
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 5e3204b6d5a..dfb8f03fa6e 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -811,7 +811,7 @@ static bool project_paint_PickColor(
}
/**
- * Check if 'pt' is infront of the 3 verts on the Z axis (used for screenspace occlusion test)
+ * Check if 'pt' is in front of the 3 verts on the Z axis (used for screen-space occlusion test)
* \return
* - `0`: no occlusion
* - `-1`: no occlusion but 2D intersection is true
@@ -836,7 +836,7 @@ static int project_paint_occlude_ptv(const float pt[3],
}
/* From here on we know there IS an intersection */
- /* if ALL of the verts are infront of us then we know it intersects ? */
+ /* if ALL of the verts are in front of us then we know it intersects ? */
if (v1[2] < pt[2] && v2[2] < pt[2] && v3[2] < pt[2]) {
return 1;
}
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index c32e496f4f5..6e0402fc6e0 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -146,12 +146,11 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
PBVHNode **nodes;
int totnode;
bool multires;
- Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
mode = RNA_enum_get(op->ptr, "mode");
value = RNA_float_get(op->ptr, "value");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@@ -169,7 +168,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_flood_fill_task_cb, &settings);
if (multires) {
@@ -313,7 +312,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *
/* Transform the clip planes in object space. */
ED_view3d_clipping_calc(&bb, clip_planes, vc->region, vc->obact, rect);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@@ -344,7 +343,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings);
if (nodes) {
@@ -500,7 +499,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@@ -533,7 +532,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
data.task_data.value = value;
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings);
if (nodes) {
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index 0f54d5e0821..191ae1da343 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -304,7 +304,10 @@ static bool palette_extract_img_poll(bContext *C)
{
SpaceLink *sl = CTX_wm_space_data(C);
if ((sl != NULL) && (sl->spacetype == SPACE_IMAGE)) {
- return true;
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Image *image = sima->image;
+ ImageUser iuser = sima->iuser;
+ return BKE_image_has_ibuf(image, &iuser);
}
return false;
@@ -326,16 +329,16 @@ static int palette_extract_img_exec(bContext *C, wmOperator *op)
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
- if (ibuf->rect) {
+ if (ibuf && ibuf->rect) {
/* Extract all colors. */
+ const int range = (int)pow(10.0f, threshold);
for (int row = 0; row < ibuf->y; row++) {
for (int col = 0; col < ibuf->x; col++) {
float color[4];
IMB_sampleImageAtLocation(ibuf, (float)col, (float)row, false, color);
- const float range = pow(10.0f, threshold);
- color[0] = truncf(color[0] * range) / range;
- color[1] = truncf(color[1] * range) / range;
- color[2] = truncf(color[2] * range) / range;
+ for (int i = 0; i < 3; i++) {
+ color[i] = truncf(color[i] * range) / range;
+ }
uint key = rgb_to_cpack(color[0], color[1], color[2]);
if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) {
@@ -360,6 +363,8 @@ static int palette_extract_img_exec(bContext *C, wmOperator *op)
static void PALETTE_OT_extract_from_image(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
/* identifiers */
ot->name = "Extract Palette from Image";
ot->idname = "PALETTE_OT_extract_from_image";
@@ -373,7 +378,8 @@ static void PALETTE_OT_extract_from_image(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4);
+ prop = RNA_def_int(ot->srna, "threshold", 1, 1, 1, "Threshold", "", 1, 1);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* Sort Palette color by Hue and Saturation. */
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 2c6f708d82a..447d5373a48 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -909,7 +909,7 @@ PaintStroke *paint_stroke_new(bContext *C,
stroke->zoom_2d = max_ff(zoomx, zoomy);
if (stroke->stroke_mode == BRUSH_STROKE_INVERT) {
- if (br->flag & (BRUSH_CURVE)) {
+ if (br->flag & BRUSH_CURVE) {
RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL);
}
}
@@ -1467,8 +1467,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
else if (first_modal ||
/* regular dabs */
- (!(br->flag & (BRUSH_AIRBRUSH)) &&
- (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) ||
+ (!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) ||
/* airbrush */
((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
event->customdata == stroke->timer)) {
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 6de54b3ae6a..7ac778630ac 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -1101,12 +1101,12 @@ static void vertex_paint_init_session(Depsgraph *depsgraph,
BLI_assert(ob->sculpt == NULL);
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = object_mode;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
}
static void vertex_paint_init_stroke(Depsgraph *depsgraph, Object *ob)
{
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
}
static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
@@ -1425,10 +1425,10 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
}
}
- /* Weightpaint works by overriding colors in mesh,
- * so need to make sure we recalc on enter and
+ /* Weight-paint works by overriding colors in mesh,
+ * so need to make sure we recalculate on enter and
* exit (exit needs doing regardless because we
- * should redeform).
+ * should re-deform).
*/
DEG_id_tag_update(&me->id, 0);
@@ -2171,7 +2171,7 @@ static void calculate_average_weight(SculptThreadedTaskData *data,
data->custom_data = accum;
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (data->sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings);
uint accum_len = 0;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 75c88047914..fb7ae4d017d 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -30,6 +30,7 @@
#include "BLI_gsqueue.h"
#include "BLI_hash.h"
#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@@ -73,6 +74,8 @@
#include "DEG_depsgraph.h"
+#include "IMB_colormanagement.h"
+
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
@@ -153,6 +156,21 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
return NULL;
}
+const float *SCULPT_vertex_color_get(SculptSession *ss, int index)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES:
+ if (ss->vcol) {
+ return ss->vcol[index].color;
+ }
+ break;
+ case PBVH_BMESH:
+ case PBVH_GRIDS:
+ break;
+ }
+ return NULL;
+}
+
void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
@@ -180,6 +198,23 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
}
}
+static const float *sculpt_vertex_persistent_co_get(SculptSession *ss, int index)
+{
+ if (ss->persistent_base) {
+ return ss->persistent_base[index].co;
+ }
+ return SCULPT_vertex_co_get(ss, index);
+}
+
+static void sculpt_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3])
+{
+ if (ss->persistent_base) {
+ copy_v3_v3(no, ss->persistent_base[index].no);
+ return;
+ }
+ SCULPT_vertex_normal_get(ss, index, no);
+}
+
float SCULPT_vertex_mask_get(SculptSession *ss, int index)
{
BMVert *v;
@@ -633,6 +668,13 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
}
}
}
+
+ if (ss->fake_neighbors.use_fake_neighbors) {
+ BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
+ if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
+ sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ }
+ }
}
static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
@@ -665,6 +707,13 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x);
}
+ if (ss->fake_neighbors.use_fake_neighbors) {
+ BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
+ if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
+ sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ }
+ }
+
if (neighbors.coords != neighbors.coords_fixed) {
MEM_freeN(neighbors.coords);
}
@@ -821,7 +870,7 @@ int SCULPT_nearest_vertex_get(
nvtd.nearest_vertex_distance_squared = FLT_MAX;
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = nearest_vertex_get_reduce;
settings.userdata_chunk = &nvtd;
settings.userdata_chunk_size = sizeof(NearestVertexTLSData);
@@ -998,6 +1047,8 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool)
SCULPT_TOOL_LAYER,
SCULPT_TOOL_POSE,
SCULPT_TOOL_CLOTH,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_SMEAR,
SCULPT_TOOL_DRAW_FACE_SETS);
}
@@ -1045,9 +1096,7 @@ typedef enum StrokeFlags {
/* Initialize a SculptOrigVertData for accessing original vertex data;
* handles BMesh, mesh, and multires. */
-static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
- Object *ob,
- SculptUndoNode *unode)
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode)
{
SculptSession *ss = ob->sculpt;
BMesh *bm = ss->bm;
@@ -1062,6 +1111,7 @@ static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
data->coords = data->unode->co;
data->normals = data->unode->no;
data->vmasks = data->unode->mask;
+ data->colors = data->unode->col;
}
}
@@ -1071,7 +1121,7 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *
{
SculptUndoNode *unode;
unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
- sculpt_orig_vert_data_unode_init(data, ob, unode);
+ SCULPT_orig_vert_data_unode_init(data, ob, unode);
}
/* Update a SculptOrigVertData for a particular vertex from the PBVH
@@ -1087,6 +1137,9 @@ void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter
orig_data->no = orig_data->normals[iter->i];
}
}
+ else if (orig_data->unode->type == SCULPT_UNDO_COLOR) {
+ orig_data->col = orig_data->colors[iter->i];
+ }
else if (orig_data->unode->type == SCULPT_UNDO_MASK) {
if (orig_data->bm_log) {
orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert);
@@ -1247,7 +1300,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- sculpt_orig_vert_data_unode_init(&orig_data, data->ob, unode);
+ SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
@@ -1265,6 +1318,9 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
else if (orig_data.unode->type == SCULPT_UNDO_MASK) {
*vd.mask = orig_data.mask;
}
+ else if (orig_data.unode->type == SCULPT_UNDO_COLOR) {
+ copy_v4_v4(vd.col, orig_data.col);
+ }
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@@ -1288,7 +1344,7 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
/**
* Disable OpenMP when dynamic-topology is enabled. Otherwise, new entries might be inserted by
- * #sculpt_undo_push_node() into the GHash used internally by #BM_log_original_vert_co()
+ * #SCULPT_undo_push_node() into the #GHash used internally by #BM_log_original_vert_co()
* by a different thread. See T33787. */
SculptThreadedTaskData data = {
.sd = sd,
@@ -1298,9 +1354,11 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP) && !ss->bm, totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true && !ss->bm, totnode);
BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings);
+ BKE_pbvh_node_color_buffer_free(ss->pbvh);
+
MEM_SAFE_FREE(nodes);
}
@@ -1478,6 +1536,9 @@ bool SCULPT_brush_test_cube(SculptBrushTest *test,
{
float side = M_SQRT1_2;
float local_co[3];
+ float i_local[4][4];
+
+ invert_m4_m4(i_local, local);
if (sculpt_brush_test_clipping(test, co)) {
return false;
@@ -1924,7 +1985,7 @@ static void calc_area_center(
AreaNormalCenterTLSData anctd = {{{0}}};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = calc_area_normal_and_center_reduce;
settings.userdata_chunk = &anctd;
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData);
@@ -1953,8 +2014,7 @@ void SCULPT_calc_area_normal(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3])
{
const Brush *brush = BKE_paint_brush(&sd->paint);
- bool use_threading = (sd->flags & SCULPT_USE_OPENMP);
- SCULPT_pbvh_calc_area_normal(brush, ob, nodes, totnode, use_threading, r_area_no);
+ SCULPT_pbvh_calc_area_normal(brush, ob, nodes, totnode, true, r_area_no);
}
/* Expose 'calc_area_normal' externally. */
@@ -2024,7 +2084,7 @@ static void calc_area_normal_and_center(
AreaNormalCenterTLSData anctd = {{{0}}};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = calc_area_normal_and_center_reduce;
settings.userdata_chunk = &anctd;
settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData);
@@ -2116,6 +2176,11 @@ static float brush_strength(const Sculpt *sd,
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_SLIDE_RELAX:
return alpha * pressure * overlap * feather * 2.0f;
+ case SCULPT_TOOL_PAINT:
+ final_pressure = pressure * pressure;
+ return final_pressure * overlap * feather;
+ case SCULPT_TOOL_SMEAR:
+ return pressure * overlap * feather;
case SCULPT_TOOL_CLAY_STRIPS:
/* Clay Strips needs less strength to compensate the curve. */
final_pressure = powf(pressure, 1.5f);
@@ -2577,11 +2642,6 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob)
}
}
-/* Note: uses after-struct allocated mem to store actual cache... */
-typedef struct SculptDoBrushSmoothGridDataChunk {
- size_t tmpgrid_size;
-} SculptDoBrushSmoothGridDataChunk;
-
typedef struct {
SculptSession *ss;
const float *ray_start;
@@ -2689,7 +2749,7 @@ static void bmesh_topology_rake(
.strength = factor,
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings);
}
@@ -2746,7 +2806,7 @@ static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int tot
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings);
}
@@ -2834,7 +2894,7 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings);
}
@@ -2911,7 +2971,7 @@ static void do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings);
}
@@ -3098,7 +3158,7 @@ static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
if (ss->cache->alt_smooth) {
for (int i = 0; i < 4; i++) {
BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings);
@@ -3308,7 +3368,7 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings);
}
@@ -3422,7 +3482,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings);
}
@@ -3495,7 +3555,7 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings);
}
@@ -3604,7 +3664,7 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings);
}
@@ -3811,7 +3871,7 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings);
}
@@ -3933,7 +3993,7 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings);
}
@@ -4006,7 +4066,7 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings);
}
@@ -4080,7 +4140,7 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings);
}
@@ -4093,7 +4153,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
- const bool use_persistent_base = ss->layer_base && brush->flag & BRUSH_PERSISTENT;
+ const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
@@ -4123,7 +4183,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
const int vi = vd.index;
float *disp_factor;
if (use_persistent_base) {
- disp_factor = &ss->layer_base[vi].disp;
+ disp_factor = &ss->persistent_base[vi].disp;
}
else {
disp_factor = &ss->cache->layer_displacement_factor[vi];
@@ -4153,9 +4213,9 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
float normal[3];
if (use_persistent_base) {
- copy_v3_v3(normal, ss->layer_base[vi].no);
+ sculpt_vertex_persistent_normal_get(ss, vi, normal);
mul_v3_fl(normal, brush->height);
- madd_v3_v3v3fl(final_co, ss->layer_base[vi].co, normal, *disp_factor);
+ madd_v3_v3v3fl(final_co, sculpt_vertex_persistent_co_get(ss, vi), normal, *disp_factor);
}
else {
normal_short_to_float_v3(normal, orig_data.no);
@@ -4196,7 +4256,7 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings);
}
@@ -4263,7 +4323,7 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings);
}
@@ -4388,7 +4448,7 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings);
}
@@ -4539,7 +4599,7 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
ClaySampleData csd = {{0}};
TaskParallelSettings sample_settings;
- BKE_pbvh_parallel_range_settings(&sample_settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode);
sample_settings.func_reduce = calc_clay_surface_reduce;
sample_settings.userdata_chunk = &csd;
sample_settings.userdata_chunk_size = sizeof(ClaySampleData);
@@ -4570,7 +4630,7 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings);
}
@@ -4677,6 +4737,17 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
mul_v3_fl(temp, displace);
add_v3_v3(area_co, temp);
+ /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the
+ * vertices. When in Add mode, vertices that are below the plane and inside the cube are move
+ * towards the plane. In this situation, there may be cases where a vertex is outside the cube
+ * but below the plane, so won't be deformed, causing artifacts. In order to prevent these
+ * artifacts, this displaces the test cube space in relation to the plane in order to
+ * deform more vertices that may be below it. */
+ /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were set
+ * by doing multiple tests using the default "Clay Strips" brush preset. */
+ float area_co_displaced[3];
+ madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f);
+
/* Init brush local space matrix. */
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
mat[0][3] = 0.0f;
@@ -4684,13 +4755,19 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
mat[1][3] = 0.0f;
copy_v3_v3(mat[2], area_no);
mat[2][3] = 0.0f;
- copy_v3_v3(mat[3], ss->cache->location);
+ copy_v3_v3(mat[3], area_co_displaced);
mat[3][3] = 1.0f;
normalize_m4(mat);
/* Scale brush local space matrix. */
scale_m4_fl(scale, ss->cache->radius);
mul_m4_m4m4(tmat, mat, scale);
+
+ /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in
+ * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices
+ * during big deformation while keeping the surface as uniform as possible. */
+ mul_v3_fl(tmat[2], 1.25f);
+
invert_m4_m4(mat, tmat);
SculptThreadedTaskData data = {
@@ -4704,7 +4781,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings);
}
@@ -4798,7 +4875,7 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings);
}
@@ -4891,7 +4968,7 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings);
}
@@ -5064,7 +5141,7 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings);
}
@@ -5136,7 +5213,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings);
}
@@ -5255,23 +5332,23 @@ static void do_brush_action_task_cb(void *__restrict userdata,
/* Face Sets modifications do a single undo push */
if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
+ BKE_pbvh_node_mark_redraw(data->nodes[n]);
/* Draw face sets in smooth mode moves the vertices. */
if (ss->cache->alt_smooth) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_node_mark_update(data->nodes[n]);
}
}
- else {
- SCULPT_undo_push_node(data->ob,
- data->nodes[n],
- data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK :
- SCULPT_UNDO_COORDS);
- }
-
- if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
+ else if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
}
+ else if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+ BKE_pbvh_node_mark_update_color(data->nodes[n]);
+ }
else {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_node_mark_update(data->nodes[n]);
}
}
@@ -5282,7 +5359,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
int totnode;
PBVHNode **nodes;
- /* Build a list of all nodes that are potentially within the brush's area of influence. */
+ /* Check for unsupported features. */
+ PBVHType type = BKE_pbvh_type(ss->pbvh);
+ if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) {
+ return;
+ }
+
+ if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) {
+ return;
+ }
+
+ /* Build a list of all nodes that are potentially within the brush's area of influence */
/* These brushes need to update all nodes as they are not constrained by the brush radius */
/* Elastic deform needs all nodes to avoid artifacts as the effect of the brush is not
@@ -5322,7 +5409,14 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
* and the number of nodes under the brush influence. */
if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && ss->cache->first_time &&
ss->cache->mirror_symmetry_pass == 0 && !ss->cache->alt_smooth) {
- SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS);
+
+ /* Dyntopo does not support Face Sets data, so it can't store/restore it from undo. */
+ /* TODO (pablodp606): This check should be done in the undo code and not here, but the rest of
+ * the sculpt code is not checking for unsupported undo types that may return a null node. */
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
+ SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS);
+ }
+
if (ss->cache->invert) {
/* When inverting the brush, pick the paint face mask ID from the mesh. */
ss->cache->paint_face_set = SCULPT_active_face_set_get(ss);
@@ -5345,7 +5439,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ 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)) {
@@ -5464,6 +5558,12 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
case SCULPT_TOOL_DRAW_FACE_SETS:
SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode);
break;
+ case SCULPT_TOOL_PAINT:
+ SCULPT_do_paint_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_SMEAR:
+ SCULPT_do_smear_brush(sd, ob, nodes, totnode);
+ break;
}
if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
@@ -5602,7 +5702,7 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings);
}
@@ -5690,7 +5790,7 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, SCULPT_flush_stroke_deform_task_cb, &settings);
if (vertCos) {
@@ -5991,6 +6091,10 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "Cloth Brush";
case SCULPT_TOOL_DRAW_FACE_SETS:
return "Draw Face Sets";
+ case SCULPT_TOOL_PAINT:
+ return "Paint Brush";
+ case SCULPT_TOOL_SMEAR:
+ return "Smear Brush";
}
return "Sculpting";
@@ -6005,6 +6109,7 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->dial);
MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp);
MEM_SAFE_FREE(cache->layer_displacement_factor);
+ MEM_SAFE_FREE(cache->prev_colors);
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
@@ -6119,7 +6224,11 @@ static void sculpt_update_cache_invariants(
cache->saved_mask_brush_tool = brush->mask_tool;
brush->mask_tool = BRUSH_MASK_SMOOTH;
}
- else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SLIDE_RELAX, SCULPT_TOOL_DRAW_FACE_SETS)) {
+ else if (ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_SLIDE_RELAX,
+ SCULPT_TOOL_DRAW_FACE_SETS,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_SMEAR)) {
/* Do nothing, this tool has its own smooth mode. */
}
else {
@@ -6266,6 +6375,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
int tool = brush->sculpt_tool;
if (ELEM(tool,
+ SCULPT_TOOL_PAINT,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_ELASTIC_DEFORM,
SCULPT_TOOL_CLOTH,
@@ -6519,7 +6629,7 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd,
((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) ||
(brush->sculpt_tool == SCULPT_TOOL_POSE) ||
(brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) ||
- (brush->sculpt_tool == SCULPT_TOOL_CLOTH) ||
+ (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) ||
(brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS));
}
@@ -6533,7 +6643,7 @@ void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *b
if (ss->shapekey_active || ss->deform_modifiers_active ||
(!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) {
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false, false);
}
}
@@ -6877,7 +6987,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
SculptSession *ss = CTX_data_active_object(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
int mode = RNA_enum_get(op->ptr, "mode");
- bool is_smooth;
+ bool is_smooth, needs_colors;
bool need_mask = false;
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
@@ -6892,7 +7002,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
sculpt_brush_init_tex(scene, sd, ss);
is_smooth = sculpt_needs_connectivity_info(sd, brush, ss, mode);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask);
+ needs_colors = ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask, needs_colors);
}
static void sculpt_restore_mesh(Sculpt *sd, Object *ob)
@@ -7035,12 +7146,19 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
if (update_flags & SCULPT_UPDATE_COORDS) {
BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateOriginalBB);
+
+ /* Coordinates were modified, so fake neighbors are not longer valid. */
+ SCULPT_fake_neighbors_free(ob);
}
if (update_flags & SCULPT_UPDATE_MASK) {
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
}
+ if (update_flags & SCULPT_UPDATE_COLOR) {
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
+ }
+
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BKE_pbvh_bmesh_after_stroke(ss->pbvh);
}
@@ -7155,6 +7273,9 @@ static void sculpt_stroke_update_step(bContext *C,
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
}
+ else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
+ }
else {
SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
}
@@ -7192,7 +7313,11 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
brush->mask_tool = ss->cache->saved_mask_brush_tool;
}
- else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SLIDE_RELAX, SCULPT_TOOL_DRAW_FACE_SETS)) {
+ else if (ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_SLIDE_RELAX,
+ SCULPT_TOOL_DRAW_FACE_SETS,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_SMEAR)) {
/* Do nothing. */
}
else {
@@ -7208,6 +7333,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
SCULPT_automasking_end(ob);
}
+ BKE_pbvh_node_color_buffer_free(ss->pbvh);
SCULPT_cache_free(ss->cache);
ss->cache = NULL;
@@ -7346,18 +7472,18 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
if (ss) {
SCULPT_vertex_random_access_init(ss);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
- MEM_SAFE_FREE(ss->layer_base);
+ MEM_SAFE_FREE(ss->persistent_base);
const int totvert = SCULPT_vertex_count_get(ss);
- ss->layer_base = MEM_mallocN(sizeof(SculptLayerPersistentBase) * totvert,
- "layer persistent base");
+ ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert,
+ "layer persistent base");
for (int i = 0; i < totvert; i++) {
- copy_v3_v3(ss->layer_base[i].co, SCULPT_vertex_co_get(ss, i));
- SCULPT_vertex_normal_get(ss, i, ss->layer_base[i].no);
- ss->layer_base[i].disp = 0.0f;
+ copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i));
+ SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no);
+ ss->persistent_base[i].disp = 0.0f;
}
}
@@ -7548,7 +7674,7 @@ static void sculpt_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob)
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = OB_MODE_SCULPT;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
/* Here we can detect geometry that was just added to Sculpt Mode as it has the
* SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
@@ -7834,7 +7960,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
return;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
if (!ss->pmap) {
return;
@@ -7886,6 +8012,417 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
ss->preview_vert_index_count = totpoints;
}
+static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ ID *data;
+ data = ob->data;
+ if (data && ID_IS_LINKED(data)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ob->type != OB_MESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Mesh *mesh = ob->data;
+
+ const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
+ if (mloopcol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
+
+ const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
+ if (MPropCol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+
+ MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+ MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+
+ for (int i = 0; i < mesh->totpoly; i++) {
+ MPoly *c_poly = &polys[i];
+ for (int j = 0; j < c_poly->totloop; j++) {
+ int loop_index = c_poly->loopstart + j;
+ MLoop *c_loop = &loops[c_poly->loopstart + j];
+ loopcols[loop_index].r = (char)(vertcols[c_loop->v].color[0] * 255);
+ loopcols[loop_index].g = (char)(vertcols[c_loop->v].color[1] * 255);
+ loopcols[loop_index].b = (char)(vertcols[c_loop->v].color[2] * 255);
+ loopcols[loop_index].a = (char)(vertcols[c_loop->v].color[3] * 255);
+ }
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Sculpt Vertex Color to Vertex Color";
+ ot->description = "Copy the Sculpt Vertex Color to a regular color layer";
+ ot->idname = "SCULPT_OT_vertex_to_loop_colors";
+
+ /* api callbacks */
+ ot->poll = SCULPT_mode_poll;
+ ot->exec = vertex_to_loop_colors_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ ID *data;
+ data = ob->data;
+ if (data && ID_IS_LINKED(data)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ob->type != OB_MESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Mesh *mesh = ob->data;
+
+ const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
+ if (mloopcol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
+
+ const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
+ if (MPropCol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+
+ MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+ MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+
+ for (int i = 0; i < mesh->totpoly; i++) {
+ MPoly *c_poly = &polys[i];
+ for (int j = 0; j < c_poly->totloop; j++) {
+ int loop_index = c_poly->loopstart + j;
+ MLoop *c_loop = &loops[c_poly->loopstart + j];
+ vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
+ vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
+ vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
+ vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f);
+ }
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Vertex Color to Sculpt Vertex Color";
+ ot->description = "Copy the active loop color layer to the vertex color";
+ ot->idname = "SCULPT_OT_loop_to_vertex_colors";
+
+ /* api callbacks */
+ ot->poll = SCULPT_mode_poll;
+ ot->exec = loop_to_vertex_colors_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int sculpt_sample_color_invoke(bContext *C,
+ wmOperator *UNUSED(op),
+ const wmEvent *UNUSED(e))
+{
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+ int active_vertex = SCULPT_active_vertex_get(ss);
+ const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex);
+ if (!active_vertex_color) {
+ return OPERATOR_CANCELLED;
+ }
+
+ float color_srgb[3];
+ copy_v3_v3(color_srgb, active_vertex_color);
+ IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb);
+ BKE_brush_color_set(scene, brush, color_srgb);
+ BKE_brush_alpha_set(scene, brush, active_vertex_color[3]);
+
+ WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_sample_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Sample color";
+ ot->idname = "SCULPT_OT_sample_color";
+ ot->description = "Sample the vertex color of the active vertex";
+
+ /* api callbacks */
+ ot->invoke = sculpt_sample_color_invoke;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER;
+}
+
+/* Fake Neighbors. */
+/* This allows the sculpt tools to work on meshes with multiple connected components as they had
+ * only one connected component. When initialized and enabled, the sculpt API will return extra
+ * connectivity neighbors that are not in the real mesh. These neighbors are calculated for each
+ * vertex using the minimun distance to a vertex that is in a different connected component. */
+
+/* The fake neighbors first need to be ensured to be initialized.
+ * After that tools which needs fake neighbors functionality need to
+ * temporarily enable it:
+ *
+ * void my_awesome_sculpt_tool() {
+ * SCULPT_fake_neighbors_ensure(sd, object, brush->disconnected_distance_max);
+ * SCULPT_fake_neighbors_enable(ob);
+ *
+ * ... Logic of the tool ...
+ * SCULPT_fake_neighbors_disable(ob);
+ * }
+ *
+ * Such approach allows to keep all the connectivity information ready for reuse
+ * (withouy having lag prior to every stroke), but also makes it so the affect
+ * is localized to a specific brushes and tools only. */
+
+enum {
+ SCULPT_TOPOLOGY_ID_NONE,
+ SCULPT_TOPOLOGY_ID_DEFAULT,
+};
+
+static int SCULPT_vertex_get_connected_component(SculptSession *ss, int index)
+{
+ if (ss->vertex_info.connected_component) {
+ return ss->vertex_info.connected_component[index];
+ }
+ return SCULPT_TOPOLOGY_ID_DEFAULT;
+}
+
+static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist)
+{
+ const int totvert = SCULPT_vertex_count_get(ss);
+ ss->fake_neighbors.fake_neighbor_index = MEM_malloc_arrayN(
+ totvert, sizeof(int), "fake neighbor");
+ for (int i = 0; i < totvert; i++) {
+ ss->fake_neighbors.fake_neighbor_index[i] = FAKE_NEIGHBOR_NONE;
+ }
+
+ ss->fake_neighbors.current_max_distance = max_dist;
+}
+
+static void SCULPT_fake_neighbor_add(SculptSession *ss, int v_index_a, int v_index_b)
+{
+ if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) {
+ ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b;
+ ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a;
+ }
+}
+
+static void sculpt_pose_fake_neighbors_free(SculptSession *ss)
+{
+ MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index);
+}
+
+typedef struct NearestVertexFakeNeighborTLSData {
+ int nearest_vertex_index;
+ float nearest_vertex_distance_squared;
+ int current_topology_id;
+} NearestVertexFakeNeighborTLSData;
+
+static void do_fake_neighbor_search_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ NearestVertexFakeNeighborTLSData *nvtd = tls->userdata_chunk;
+ PBVHVertexIter vd;
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index);
+ if (vd_topology_id != nvtd->current_topology_id &&
+ ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) {
+ float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co);
+ if (distance_squared < nvtd->nearest_vertex_distance_squared &&
+ distance_squared < data->max_distance_squared) {
+ nvtd->nearest_vertex_index = vd.index;
+ nvtd->nearest_vertex_distance_squared = distance_squared;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ NearestVertexFakeNeighborTLSData *join = chunk_join;
+ NearestVertexFakeNeighborTLSData *nvtd = chunk;
+ if (join->nearest_vertex_index == -1) {
+ join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
+ }
+ else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
+ join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
+ }
+}
+
+static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, float max_distance)
+{
+ SculptSession *ss = ob->sculpt;
+ PBVHNode **nodes = NULL;
+ int totnode;
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .sd = sd,
+ .radius_squared = max_distance * max_distance,
+ .original = false,
+ .center = SCULPT_vertex_co_get(ss, index),
+ };
+ BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
+
+ if (totnode == 0) {
+ return -1;
+ }
+
+ SculptThreadedTaskData task_data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = nodes,
+ .max_distance_squared = max_distance * max_distance,
+ };
+
+ copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, index));
+
+ NearestVertexFakeNeighborTLSData nvtd;
+ nvtd.nearest_vertex_index = -1;
+ nvtd.nearest_vertex_distance_squared = FLT_MAX;
+ nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, index);
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ settings.func_reduce = fake_neighbor_search_reduce;
+ settings.userdata_chunk = &nvtd;
+ settings.userdata_chunk_size = sizeof(NearestVertexFakeNeighborTLSData);
+ BLI_task_parallel_range(0, totnode, &task_data, do_fake_neighbor_search_task_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+
+ return nvtd.nearest_vertex_index;
+}
+
+typedef struct SculptTopologyIDFloodFillData {
+ int next_id;
+} SculptTopologyIDFloodFillData;
+
+static bool SCULPT_connected_components_floodfill_cb(
+ SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+{
+ SculptTopologyIDFloodFillData *data = userdata;
+ ss->vertex_info.connected_component[from_v] = data->next_id;
+ ss->vertex_info.connected_component[to_v] = data->next_id;
+ return true;
+}
+
+static void sculpt_connected_components_ensure(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+
+ /* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild.
+ */
+ if (ss->vertex_info.connected_component) {
+ return;
+ }
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ ss->vertex_info.connected_component = MEM_malloc_arrayN(totvert, sizeof(int), "topology ID");
+
+ for (int i = 0; i < totvert; i++) {
+ ss->vertex_info.connected_component[i] = SCULPT_TOPOLOGY_ID_NONE;
+ }
+
+ int next_id = 0;
+ for (int i = 0; i < totvert; i++) {
+ if (ss->vertex_info.connected_component[i] == SCULPT_TOPOLOGY_ID_NONE) {
+ SculptFloodFill flood;
+ SCULPT_floodfill_init(ss, &flood);
+ SCULPT_floodfill_add_initial(&flood, i);
+ SculptTopologyIDFloodFillData data;
+ data.next_id = next_id;
+ SCULPT_floodfill_execute(ss, &flood, SCULPT_connected_components_floodfill_cb, &data);
+ SCULPT_floodfill_free(&flood);
+ next_id++;
+ }
+ }
+}
+
+void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
+{
+ SculptSession *ss = ob->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ /* Fake neighbors were already initialized with the same distance, so no need to be recalculated.
+ */
+ if (ss->fake_neighbors.fake_neighbor_index &&
+ ss->fake_neighbors.current_max_distance == max_dist) {
+ return;
+ }
+
+ sculpt_connected_components_ensure(ob);
+ SCULPT_fake_neighbor_init(ss, max_dist);
+
+ for (int i = 0; i < totvert; i++) {
+ const int from_v = i;
+
+ /* This vertex does not have a fake neighbor yet, seach one for it. */
+ if (ss->fake_neighbors.fake_neighbor_index[from_v] == FAKE_NEIGHBOR_NONE) {
+ const int to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist);
+ if (to_v != -1) {
+ /* Add the fake neighbor if available. */
+ SCULPT_fake_neighbor_add(ss, from_v, to_v);
+ }
+ }
+ }
+}
+
+void SCULPT_fake_neighbors_enable(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
+ ss->fake_neighbors.use_fake_neighbors = true;
+}
+
+void SCULPT_fake_neighbors_disable(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
+ ss->fake_neighbors.use_fake_neighbors = false;
+}
+
+void SCULPT_fake_neighbors_free(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ sculpt_pose_fake_neighbors_free(ss);
+}
+
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
@@ -7908,4 +8445,8 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_face_sets_init);
WM_operatortype_append(SCULPT_OT_cloth_filter);
WM_operatortype_append(SCULPT_OT_face_sets_edit);
+ WM_operatortype_append(SCULPT_OT_sample_color);
+ WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
+ WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
+ WM_operatortype_append(SCULPT_OT_color_filter);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 7776af11a77..c821685e8b1 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -25,7 +25,7 @@
#include "BLI_blenlib.h"
#include "BLI_dial_2d.h"
-#include "BLI_ghash.h"
+#include "BLI_edgehash.h"
#include "BLI_gsqueue.h"
#include "BLI_hash.h"
#include "BLI_math.h"
@@ -106,25 +106,11 @@
#define CLOTH_MAX_CONSTRAINTS_PER_VERTEX 1024
#define CLOTH_SIMULATION_TIME_STEP 0.01f
-static void cloth_brush_constraint_key_get(int r_key[2], const int v1, const int v2)
-{
- if (v1 < v2) {
- r_key[0] = v1;
- r_key[1] = v2;
- }
- else {
- r_key[0] = v2;
- r_key[1] = v1;
- }
-}
-
static bool cloth_brush_sim_has_length_constraint(SculptClothSimulation *cloth_sim,
const int v1,
const int v2)
{
- int constraint[2];
- cloth_brush_constraint_key_get(constraint, v1, v2);
- return BLI_gset_haskey(cloth_sim->created_length_constraints, constraint);
+ return BLI_edgeset_haskey(cloth_sim->created_length_constraints, v1, v2);
}
static void cloth_brush_add_length_constraint(SculptSession *ss,
@@ -149,9 +135,7 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
}
/* Add the constraint to the GSet to avoid creating it again. */
- int constraint[2];
- cloth_brush_constraint_key_get(constraint, v1, v2);
- BLI_gset_add(cloth_sim->created_length_constraints, constraint);
+ BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2);
}
static void do_cloth_brush_build_constraints_task_cb_ex(
@@ -472,8 +456,7 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd,
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, false, totnode);
- cloth_sim->created_length_constraints = BLI_gset_new(
- BLI_ghashutil_inthash_v2_p, BLI_ghashutil_inthash_v2_cmp, "created length constraints");
+ cloth_sim->created_length_constraints = BLI_edgeset_new("created length constraints");
SculptThreadedTaskData build_constraints_data = {
.sd = sd,
@@ -487,7 +470,7 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd,
BLI_task_parallel_range(
0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings);
- BLI_gset_free(cloth_sim->created_length_constraints, NULL);
+ BLI_edgeset_free(cloth_sim->created_length_constraints);
}
static void cloth_brush_satisfy_constraints(SculptSession *ss,
@@ -564,7 +547,7 @@ static void cloth_brush_do_simulation_step(
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(
0, totnode, &solve_simulation_data, do_cloth_brush_solve_simulation_task_cb_ex, &settings);
}
@@ -639,7 +622,7 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod
}
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(
0, totnode, &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings);
}
@@ -873,7 +856,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
@@ -889,8 +872,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, cloth_filter_apply_forces_task_cb, &settings);
@@ -922,10 +904,10 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
/* Needs mask data to be available as it is used when solving the constraints. */
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_undo_push_begin("Cloth filter");
- SCULPT_filter_cache_init(ob, sd);
+ SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass");
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index f071deaa219..463233fd6fb 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -181,7 +181,7 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my)
/* Update the active vertex. */
float mouse[2] = {mx, my};
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
/* Average the edge length of the connected edges to the active vertex. */
int active_vertex = SCULPT_active_vertex_get(ss);
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 7bb54366204..031b4f8731d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -203,7 +203,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
if (ss->cache->alt_smooth) {
for (int i = 0; i < 4; i++) {
BLI_task_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings);
@@ -269,7 +269,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
float threshold = 0.5f;
@@ -630,7 +630,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
@@ -787,7 +787,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
const int mode = RNA_enum_get(op->ptr, "mode");
@@ -1083,7 +1083,7 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
return OPERATOR_CANCELLED;
}
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
new file mode 100644
index 00000000000..5f7805af347
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -0,0 +1,323 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_hash.h"
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_task.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+#include "BKE_scene.h"
+
+#include "IMB_colormanagement.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+
+#include "bmesh.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+typedef enum eSculptColorFilterTypes {
+ COLOR_FILTER_FILL,
+ COLOR_FILTER_HUE,
+ COLOR_FILTER_SATURATION,
+ COLOR_FILTER_VALUE,
+ COLOR_FILTER_BRIGHTNESS,
+ COLOR_FILTER_CONTRAST,
+ COLOR_FILTER_RED,
+ COLOR_FILTER_GREEN,
+ COLOR_FILTER_BLUE,
+ COLOR_FILTER_SMOOTH,
+} eSculptColorFilterTypes;
+
+EnumPropertyItem prop_color_filter_types[] = {
+ {COLOR_FILTER_FILL, "FILL", 0, "Fill", "Fill with a specific color"},
+ {COLOR_FILTER_HUE, "HUE", 0, "Hue", "Change hue"},
+ {COLOR_FILTER_SATURATION, "SATURATION", 0, "Saturation", "Change saturation"},
+ {COLOR_FILTER_VALUE, "VALUE", 0, "Value", "Change value"},
+
+ {COLOR_FILTER_BRIGHTNESS, "BRIGTHNESS", 0, "Brightness", "Change brightness"},
+ {COLOR_FILTER_CONTRAST, "CONTRAST", 0, "Contrast", "Change contrast"},
+
+ {COLOR_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth colors"},
+
+ {COLOR_FILTER_RED, "RED", 0, "Red", "Change red channel"},
+ {COLOR_FILTER_GREEN, "GREEN", 0, "Green", "Change green channel"},
+ {COLOR_FILTER_BLUE, "BLUE", 0, "Blue", "Change blue channel"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void color_filter_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ const int mode = data->filter_type;
+
+ SculptOrigVertData orig_data;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ SCULPT_orig_vert_data_update(&orig_data, &vd);
+ float orig_color[3], final_color[4], hsv_color[3];
+ int hue;
+ float brightness, contrast, gain, delta, offset;
+ float fade = vd.mask ? *vd.mask : 0.0f;
+ fade = 1.0f - fade;
+ fade *= data->filter_strength;
+
+ copy_v3_v3(orig_color, orig_data.col);
+
+ switch (mode) {
+ case COLOR_FILTER_FILL: {
+ float fill_color_rgba[4];
+ copy_v3_v3(fill_color_rgba, data->filter_fill_color);
+ fill_color_rgba[3] = 1.0f;
+ CLAMP(fade, 0.0f, 1.0f);
+ mul_v4_fl(fill_color_rgba, fade);
+ blend_color_mix_float(final_color, orig_data.col, fill_color_rgba);
+ break;
+ }
+ case COLOR_FILTER_HUE:
+ rgb_to_hsv_v(orig_color, hsv_color);
+ hue = hsv_color[0] + fade;
+ hsv_color[0] = fabs((hsv_color[0] + fade) - hue);
+ hsv_to_rgb_v(hsv_color, final_color);
+ break;
+ case COLOR_FILTER_SATURATION:
+ rgb_to_hsv_v(orig_color, hsv_color);
+ hsv_color[1] = hsv_color[1] + fade;
+ CLAMP(hsv_color[1], 0.0f, 1.0f);
+ hsv_to_rgb_v(hsv_color, final_color);
+ break;
+ case COLOR_FILTER_VALUE:
+ rgb_to_hsv_v(orig_color, hsv_color);
+ hsv_color[2] = hsv_color[2] + fade;
+ CLAMP(hsv_color[2], 0.0f, 1.0f);
+ hsv_to_rgb_v(hsv_color, final_color);
+ break;
+ case COLOR_FILTER_RED:
+ orig_color[0] = orig_color[0] + fade;
+ CLAMP(orig_color[0], 0.0f, 1.0f);
+ copy_v3_v3(final_color, orig_color);
+ break;
+ case COLOR_FILTER_GREEN:
+ orig_color[1] = orig_color[1] + fade;
+ CLAMP(orig_color[1], 0.0f, 1.0f);
+ copy_v3_v3(final_color, orig_color);
+ break;
+ case COLOR_FILTER_BLUE:
+ orig_color[2] = orig_color[2] + fade;
+ CLAMP(orig_color[2], 0.0f, 1.0f);
+ copy_v3_v3(final_color, orig_color);
+ break;
+ case COLOR_FILTER_BRIGHTNESS:
+ CLAMP(fade, -1.0f, 1.0f);
+ brightness = fade;
+ contrast = 0;
+ delta = contrast / 2.0f;
+ gain = 1.0f - delta * 2.0f;
+ delta *= -1;
+ offset = gain * (brightness + delta);
+ for (int i = 0; i < 3; i++) {
+ final_color[i] = gain * orig_color[i] + offset;
+ CLAMP(final_color[i], 0.0f, 1.0f);
+ }
+ break;
+ case COLOR_FILTER_CONTRAST:
+ CLAMP(fade, -1.0f, 1.0f);
+ brightness = 0;
+ contrast = fade;
+ delta = contrast / 2.0f;
+ gain = 1.0f - delta * 2.0f;
+ if (contrast > 0) {
+ gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
+ offset = gain * (brightness - delta);
+ }
+ else {
+ delta *= -1;
+ offset = gain * (brightness + delta);
+ }
+ for (int i = 0; i < 3; i++) {
+ final_color[i] = gain * orig_color[i] + offset;
+ CLAMP(final_color[i], 0.0f, 1.0f);
+ }
+ break;
+ case COLOR_FILTER_SMOOTH: {
+ CLAMP(fade, -1.0f, 1.0f);
+ float smooth_color[4];
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ blend_color_interpolate_float(final_color, vd.col, smooth_color, fade);
+ break;
+ }
+ }
+
+ copy_v3_v3(vd.col, final_color);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+ BKE_pbvh_node_mark_update_color(data->nodes[n]);
+}
+
+static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ const int mode = RNA_enum_get(op->ptr, "type");
+ float filter_strength = RNA_float_get(op->ptr, "strength");
+
+ if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
+ SCULPT_undo_push_end();
+ SCULPT_filter_cache_free(ss);
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR);
+ return OPERATOR_FINISHED;
+ }
+
+ if (event->type != MOUSEMOVE) {
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ float len = event->prevclickx - event->mval[0];
+ filter_strength = filter_strength * -len * 0.001f;
+
+ float fill_color[3];
+ RNA_float_get_array(op->ptr, "fill_color", fill_color);
+ IMB_colormanagement_srgb_to_scene_linear_v3(fill_color);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = ss->filter_cache->nodes,
+ .filter_type = mode,
+ .filter_strength = filter_strength,
+ .filter_fill_color = fill_color,
+ };
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
+ BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, color_filter_task_cb, &settings);
+
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Object *ob = CTX_data_active_object(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ SculptSession *ss = ob->sculpt;
+ int mode = RNA_enum_get(op->ptr, "type");
+ PBVH *pbvh = ob->sculpt->pbvh;
+
+ /* Disable for multires and dyntopo for now */
+ if (!ss->pbvh) {
+ return OPERATOR_CANCELLED;
+ }
+ if (BKE_pbvh_type(pbvh) != PBVH_FACES) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (!ss->vcol) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_undo_push_begin("color filter");
+
+ bool needs_pmap = mode == COLOR_FILTER_SMOOTH;
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, true);
+
+ if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COLOR);
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void SCULPT_OT_color_filter(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Filter color";
+ ot->idname = "SCULPT_OT_color_filter";
+ ot->description = "Applies a filter to modify the current sculpt vertex colors";
+
+ /* api callbacks */
+ ot->invoke = sculpt_color_filter_invoke;
+ ot->modal = sculpt_color_filter_modal;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* rna */
+ RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", "");
+ RNA_def_float(
+ ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+
+ PropertyRNA *prop = RNA_def_float_color(
+ ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "fill color", 0.0f, 1.0f);
+ RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
index d2a683461a7..83145f5600f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
@@ -202,7 +202,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
int totnode;
int filter_type = RNA_enum_get(op->ptr, "filter_type");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_vertex_random_access_init(ss);
@@ -247,7 +247,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings);
if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
@@ -276,7 +276,7 @@ void SCULPT_mask_filter_smooth_apply(
for (int i = 0; i < smooth_iterations; i++) {
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings);
}
}
@@ -432,7 +432,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op)
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
int totnode;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_vertex_random_access_init(ss);
@@ -459,7 +459,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = dirty_mask_compute_range_reduce;
settings.userdata_chunk = &range;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index fd0f67f040a..494588d0996 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -70,10 +70,10 @@ static void filter_cache_init_task_cb(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
PBVHNode *node = data->nodes[i];
- SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
+ SCULPT_undo_push_node(data->ob, node, data->filter_undo_type);
}
-void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
+void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type)
{
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
@@ -110,11 +110,11 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
.sd = sd,
.ob = ob,
.nodes = ss->filter_cache->nodes,
+ .filter_undo_type = undo_type,
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, filter_cache_init_task_cb, &settings);
}
@@ -475,7 +475,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
SCULPT_vertex_random_access_init(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
SculptThreadedTaskData data = {
.sd = sd,
@@ -486,8 +486,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings);
if (filter_type == MESH_FILTER_SURFACE_SMOOTH) {
@@ -542,7 +541,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
const int totvert = SCULPT_vertex_count_get(ss);
if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
@@ -551,7 +550,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_undo_push_begin("Mesh filter");
- SCULPT_filter_cache_init(ob, sd);
+ SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
if (use_face_sets) {
ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 0e27658e848..c981f89ada7 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -27,6 +27,7 @@
#include "DNA_brush_types.h"
#include "DNA_key_types.h"
#include "DNA_listBase.h"
+#include "DNA_meshdata_types.h"
#include "DNA_vec_types.h"
#include "BLI_bitmap.h"
@@ -56,6 +57,7 @@ typedef enum SculptUpdateType {
SCULPT_UPDATE_COORDS = 1 << 0,
SCULPT_UPDATE_MASK = 1 << 1,
SCULPT_UPDATE_VISIBILITY = 1 << 2,
+ SCULPT_UPDATE_COLOR = 1 << 3,
} SculptUpdateType;
void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags);
@@ -92,6 +94,7 @@ int SCULPT_vertex_count_get(struct SculptSession *ss);
const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index);
void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]);
float SCULPT_vertex_mask_get(struct SculptSession *ss, int index);
+const float *SCULPT_vertex_color_get(SculptSession *ss, int index);
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
typedef struct SculptVertexNeighborIter {
@@ -145,6 +148,15 @@ void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]);
bool SCULPT_vertex_is_boundary(SculptSession *ss, const int index);
+/* Fake Neighbors */
+
+#define FAKE_NEIGHBOR_NONE -1
+
+void SCULPT_fake_neighbors_ensure(struct Sculpt *sd, Object *ob, const float max_dist);
+void SCULPT_fake_neighbors_enable(Object *ob);
+void SCULPT_fake_neighbors_disable(Object *ob);
+void SCULPT_fake_neighbors_free(struct Object *ob);
+
/* Sculpt Visibility API */
void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible);
@@ -179,15 +191,20 @@ typedef struct {
float (*coords)[3];
short (*normals)[3];
const float *vmasks;
+ float (*colors)[4];
/* Original coordinate, normal, and mask. */
const float *co;
const short *no;
float mask;
+ const float *col;
} SculptOrigVertData;
void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data,
+ Object *ob,
+ struct SculptUndoNode *unode);
/* Utils. */
void SCULPT_calc_brush_plane(struct Sculpt *sd,
@@ -305,7 +322,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
float *automask_factor);
/* Filters. */
-void SCULPT_filter_cache_init(Object *ob, Sculpt *sd);
+void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type);
void SCULPT_filter_cache_free(SculptSession *ss);
void SCULPT_mask_filter_smooth_apply(
@@ -374,6 +391,12 @@ void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,
/* Draw Face Sets Brush. */
void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+/* Paint Brush. */
+void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+
+/* Smear Brush. */
+void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+
/* Smooth Brush. */
void SCULPT_neighbor_average(SculptSession *ss, float avg[3], uint vert);
@@ -383,6 +406,7 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct
void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index);
float SCULPT_neighbor_mask_average(SculptSession *ss, int index);
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index);
void SCULPT_smooth(Sculpt *sd,
Object *ob,
@@ -427,6 +451,7 @@ typedef enum {
SCULPT_UNDO_DYNTOPO_SYMMETRIZE,
SCULPT_UNDO_GEOMETRY,
SCULPT_UNDO_FACE_SETS,
+ SCULPT_UNDO_COLOR,
} SculptUndoType;
/* Storage of geometry for the undo node.
@@ -457,6 +482,7 @@ typedef struct SculptUndoNode {
float (*co)[3];
float (*orig_co)[3];
short (*no)[3];
+ float (*col)[4];
float *mask;
int totvert;
@@ -556,6 +582,7 @@ typedef struct SculptThreadedTaskData {
int filter_type;
float filter_strength;
+ float *filter_fill_color;
bool use_area_cos;
bool use_area_nos;
@@ -568,6 +595,8 @@ typedef struct SculptThreadedTaskData {
bool any_vertex_sampled;
+ float *wet_mix_sampled_color;
+
float *prev_mask;
float *pose_factor;
@@ -601,6 +630,7 @@ typedef struct SculptThreadedTaskData {
bool dirty_mask_dirty_only;
int face_set;
+ int filter_undo_type;
ThreadMutex mutex;
@@ -720,6 +750,8 @@ typedef struct StrokeCache {
float bstrength;
float normal_weight; /* from brush (with optional override) */
+ float (*prev_colors)[4];
+
/* The rest is temporary storage that isn't saved as a property */
bool first_time; /* Beginning of stroke may do some things special */
@@ -813,6 +845,9 @@ typedef struct StrokeCache {
float stroke_local_mat[4][4];
float multiplane_scrape_angle;
+ float wet_mix_prev_color[4];
+ float density_seed;
+
rcti previous_r; /* previous redraw rectangle */
rcti current_r; /* current redraw rectangle */
@@ -901,6 +936,9 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot);
/* Cloth Filter. */
void SCULPT_OT_cloth_filter(struct wmOperatorType *ot);
+/* Color Filter. */
+void SCULPT_OT_color_filter(struct wmOperatorType *ot);
+
/* Mask filter and Dirty Mask. */
void SCULPT_OT_mask_filter(struct wmOperatorType *ot);
void SCULPT_OT_dirty_mask(struct wmOperatorType *ot);
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
index cbb198e14a3..60483cc168d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
@@ -206,7 +206,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
(event->type == EVT_PADENTER && event->val == KM_PRESS)) {
/* Smooth iterations. */
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
const int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations");
SCULPT_mask_filter_smooth_apply(
sd, ob, ss->filter_cache->nodes, ss->filter_cache->totnode, smooth_iterations);
@@ -289,8 +289,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"),
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings);
ss->filter_cache->mask_update_current_it = mask_expand_update_it;
}
@@ -365,7 +364,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
int vertex_count = SCULPT_vertex_count_get(ss);
@@ -459,8 +458,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"),
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings);
const char *status_str = TIP_(
diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
index f3327706102..503f9429a50 100644
--- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
+++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
@@ -304,7 +304,7 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes,
MultiplaneScrapeSampleData mssd = {{{0}}};
TaskParallelSettings sample_settings;
- BKE_pbvh_parallel_range_settings(&sample_settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode);
sample_settings.func_reduce = calc_multiplane_scrape_surface_reduce;
sample_settings.userdata_chunk = &mssd;
sample_settings.userdata_chunk_size = sizeof(MultiplaneScrapeSampleData);
@@ -395,7 +395,7 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes,
plane_from_point_normal_v3(data.multiplane_scrape_planes[0], area_co, plane_no);
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_multiplane_scrape_brush_task_cb_ex, &settings);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
new file mode 100644
index 00000000000..9b38def6b7b
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -0,0 +1,477 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_hash.h"
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_task.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+
+#include "IMB_colormanagement.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+
+#include "IMB_imbuf.h"
+
+#include "bmesh.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+static void do_color_smooth_task_cb_exec(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = ss->cache->bstrength;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float smooth_color[4];
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_paint_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = fabsf(ss->cache->bstrength);
+
+ PBVHVertexIter vd;
+ PBVHColorBufferNode *color_buffer;
+
+ SculptOrigVertData orig_data;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ copy_v3_v3(brush_color, BKE_brush_color_get(ss->scene, brush));
+ IMB_colormanagement_srgb_to_scene_linear_v3(brush_color);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ SCULPT_orig_vert_data_update(&orig_data, &vd);
+
+ bool affect_vertex = false;
+ float distance_to_stroke_location = 0.0f;
+ if (brush->tip_roundness < 1.0f) {
+ affect_vertex = SCULPT_brush_test_cube(&test, vd.co, data->mat, brush->tip_roundness);
+ distance_to_stroke_location = ss->cache->radius * test.dist;
+ }
+ else {
+ affect_vertex = sculpt_brush_test_sq_fn(&test, vd.co);
+ distance_to_stroke_location = sqrtf(test.dist);
+ }
+
+ if (affect_vertex) {
+ float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ distance_to_stroke_location,
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ /* Density. */
+ float noise = 1.0f;
+ const float density = brush->density;
+ if (density < 1.0f) {
+ const float hash_noise = BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index);
+ if (hash_noise > density) {
+ noise = density * hash_noise;
+ fade = fade * noise;
+ }
+ }
+
+ /* Brush paint color, brush test falloff and flow. */
+ float paint_color[4];
+ float wet_mix_color[4];
+ float buffer_color[4];
+
+ mul_v4_v4fl(paint_color, brush_color, fade * brush->flow);
+ mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * brush->flow);
+
+ /* Interpolate with the wet_mix color for wet paint mixing. */
+ blend_color_interpolate_float(paint_color, paint_color, wet_mix_color, brush->wet_mix);
+ blend_color_mix_float(color_buffer->color[vd.i], color_buffer->color[vd.i], paint_color);
+
+ /* Final mix over the original color using brush alpha. */
+ mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha);
+
+ IMB_blend_color_float(vd.col, orig_data.col, buffer_color, brush->blend);
+ }
+ CLAMP4(vd.col, 0.0f, 1.0f);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+typedef struct SampleWetPaintTLSData {
+ int tot_samples;
+ float color[4];
+} SampleWetPaintTLSData;
+
+static void do_sample_wet_paint_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ SampleWetPaintTLSData *swptd = tls->userdata_chunk;
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ add_v4_v4(swptd->color, vd.col);
+ swptd->tot_samples++;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void sample_wet_paint_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ SampleWetPaintTLSData *join = chunk_join;
+ SampleWetPaintTLSData *swptd = chunk;
+
+ join->tot_samples += swptd->tot_samples;
+ add_v4_v4(join->color, swptd->color);
+}
+
+void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss->vcol) {
+ return;
+ }
+
+ if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
+ ss->cache->density_seed = BLI_hash_int_01(ss->cache->location[0] * 1000);
+ return;
+ }
+
+ BKE_curvemapping_initialize(brush->curve);
+
+ float area_no[3];
+ float mat[4][4];
+ float scale[4][4];
+ float tmat[4][4];
+
+ /* If the brush is round the tip does not need to be aligned to the surface, so this saves a
+ * whole iteration over the affected nodes. */
+ if (brush->tip_roundness < 1.0f) {
+ SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no);
+
+ cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
+ mat[0][3] = 0;
+ cross_v3_v3v3(mat[1], area_no, mat[0]);
+ mat[1][3] = 0;
+ copy_v3_v3(mat[2], area_no);
+ mat[2][3] = 0;
+ copy_v3_v3(mat[3], ss->cache->location);
+ mat[3][3] = 1;
+ normalize_m4(mat);
+
+ scale_m4_fl(scale, ss->cache->radius);
+ mul_m4_m4m4(tmat, mat, scale);
+ mul_v3_fl(tmat[1], brush->tip_scale_x);
+ invert_m4_m4(mat, tmat);
+ if (is_zero_m4(mat)) {
+ return;
+ }
+ }
+
+ /* Smooth colors mode. */
+ if (ss->cache->alt_smooth) {
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .mat = mat,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_color_smooth_task_cb_exec, &settings);
+ return;
+ }
+
+ /* Regular Paint mode. */
+
+ /* Wet paint color sampling. */
+ float wet_color[4] = {0.0f};
+ if (brush->wet_mix > 0.0f) {
+ SculptThreadedTaskData task_data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = nodes,
+ .brush = brush,
+ };
+
+ SampleWetPaintTLSData swptd;
+ swptd.tot_samples = 0;
+ zero_v4(swptd.color);
+
+ TaskParallelSettings settings_sample;
+ BKE_pbvh_parallel_range_settings(&settings_sample, true, totnode);
+ settings_sample.func_reduce = sample_wet_paint_reduce;
+ settings_sample.userdata_chunk = &swptd;
+ settings_sample.userdata_chunk_size = sizeof(SampleWetPaintTLSData);
+ BLI_task_parallel_range(0, totnode, &task_data, do_sample_wet_paint_task_cb, &settings_sample);
+
+ if (swptd.tot_samples > 0 && is_finite_v4(swptd.color)) {
+ copy_v4_v4(wet_color, swptd.color);
+ mul_v4_fl(wet_color, 1.0f / (float)swptd.tot_samples);
+ CLAMP4(wet_color, 0.0f, 1.0f);
+
+ if (ss->cache->first_time) {
+ copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
+ }
+ blend_color_interpolate_float(
+ wet_color, wet_color, ss->cache->wet_mix_prev_color, brush->wet_persistence);
+ copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
+ CLAMP4(ss->cache->wet_mix_prev_color, 0.0f, 1.0f);
+ }
+ }
+
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .wet_mix_sampled_color = wet_color,
+ .mat = mat,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_paint_brush_task_cb_ex, &settings);
+}
+
+static void do_smear_brush_task_cb_exec(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = ss->cache->bstrength;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float current_disp[3];
+ float current_disp_norm[3];
+ float interp_color[4];
+ copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]);
+
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ normalize_v3_v3(current_disp_norm, current_disp);
+ mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ float vertex_disp[3];
+ float vertex_disp_norm[3];
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ const float *neighbor_color = ss->cache->prev_colors[ni.index];
+ normalize_v3_v3(vertex_disp_norm, vertex_disp);
+ if (dot_v3v3(current_disp_norm, vertex_disp_norm) < 0.0f) {
+ const float color_interp = clamp_f(
+ -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f);
+ float color_mix[4];
+ copy_v4_v4(color_mix, neighbor_color);
+ mul_v4_fl(color_mix, color_interp * fade);
+ blend_color_mix_float(interp_color, interp_color, color_mix);
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ blend_color_interpolate_float(vd.col, ss->cache->prev_colors[vd.index], interp_color, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.index));
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss->vcol) {
+ return;
+ }
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
+ if (!ss->cache->prev_colors) {
+ ss->cache->prev_colors = MEM_callocN(sizeof(float) * 4 * totvert, "prev colors");
+ for (int i = 0; i < totvert; i++) {
+ copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, i));
+ }
+ }
+ }
+
+ BKE_curvemapping_initialize(brush->curve);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+
+ /* Smooth colors mode. */
+ if (ss->cache->alt_smooth) {
+ BLI_task_parallel_range(0, totnode, &data, do_color_smooth_task_cb_exec, &settings);
+ }
+ else {
+ /* Smear mode. */
+ BLI_task_parallel_range(0, totnode, &data, do_smear_store_prev_colors_task_cb_exec, &settings);
+ BLI_task_parallel_range(0, totnode, &data, do_smear_brush_task_cb_exec, &settings);
+ }
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index a4a87051e03..c9e2b7318d6 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -293,7 +293,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd,
PoseGrowFactorTLSData gftd;
gftd.pos_count = 0;
zero_v3(gftd.pos_avg);
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
settings.func_reduce = pose_brush_grow_factor_reduce;
settings.userdata_chunk = &gftd;
settings.userdata_chunk_size = sizeof(PoseGrowFactorTLSData);
@@ -937,18 +937,32 @@ SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
const float initial_location[3],
const float radius)
{
+ SculptPoseIKChain *ik_chain = NULL;
+
+ const bool use_fake_neighbors = !(br->flag2 & BRUSH_USE_CONNECTED_ONLY);
+
+ if (use_fake_neighbors) {
+ SCULPT_fake_neighbors_ensure(sd, ob, br->disconnected_distance_max);
+ SCULPT_fake_neighbors_enable(ob);
+ }
+
switch (br->pose_origin_type) {
case BRUSH_POSE_ORIGIN_TOPOLOGY:
- return pose_ik_chain_init_topology(sd, ob, ss, br, initial_location, radius);
+ ik_chain = pose_ik_chain_init_topology(sd, ob, ss, br, initial_location, radius);
break;
case BRUSH_POSE_ORIGIN_FACE_SETS:
- return pose_ik_chain_init_face_sets(sd, ob, ss, br, radius);
+ ik_chain = pose_ik_chain_init_face_sets(sd, ob, ss, br, radius);
break;
case BRUSH_POSE_ORIGIN_FACE_SETS_FK:
return pose_ik_chain_init_face_sets_fk(sd, ob, ss, radius, initial_location);
break;
}
- return NULL;
+
+ if (use_fake_neighbors) {
+ SCULPT_fake_neighbors_disable(ob);
+ }
+
+ return ik_chain;
}
void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br)
@@ -975,7 +989,7 @@ void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br
data.pose_factor = ss->cache->pose_ik_chain->segments[ik].weights;
for (int i = 0; i < br->pose_smooth_iterations; i++) {
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings);
}
}
@@ -1198,7 +1212,7 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 17451cb40ae..aa82ae1d7ad 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -226,6 +226,26 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
}
}
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
+{
+ float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ int total = 0;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index));
+ total++;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (total > 0) {
+ mul_v4_v4fl(result, avg, 1.0f / (float)total);
+ }
+ else {
+ copy_v4_v4(result, SCULPT_vertex_color_get(ss, index));
+ }
+}
+
static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -433,7 +453,7 @@ void SCULPT_smooth(Sculpt *sd,
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
switch (type) {
case PBVH_GRIDS:
@@ -525,10 +545,15 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
{
SCULPT_orig_vert_data_update(&orig_data, &vd);
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- const float fade =
- bstrength *
- SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
float disp[3];
SCULPT_surface_smooth_laplacian_step(ss,
@@ -566,10 +591,15 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- const float fade =
- bstrength *
- SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
SCULPT_surface_smooth_displace_step(
ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade);
}
@@ -598,7 +628,7 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
};
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
for (int i = 0; i < brush->surface_smooth_iterations; i++) {
BLI_task_parallel_range(
0, totnode, &data, SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex, &settings);
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index 59a4695ce18..f616817c330 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -71,12 +71,12 @@ void ED_sculpt_init_transform(struct bContext *C)
copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
SCULPT_undo_push_begin("Transform");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
ss->pivot_rot[3] = 1.0f;
SCULPT_vertex_random_access_init(ss);
- SCULPT_filter_cache_init(ob, sd);
+ SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
}
static void sculpt_transform_task_cb(void *__restrict userdata,
@@ -127,7 +127,7 @@ void ED_sculpt_update_modal_transform(struct bContext *C)
const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
SCULPT_vertex_random_access_init(ss);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
SculptThreadedTaskData data = {
.sd = sd,
@@ -178,8 +178,7 @@ void ED_sculpt_update_modal_transform(struct bContext *C)
}
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(
- &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings);
@@ -253,7 +252,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
int mode = RNA_enum_get(op->ptr, "mode");
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
/* Pivot to center. */
if (mode == SCULPT_PIVOT_POSITION_ORIGIN) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index d21552efafe..6ede631eb11 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -205,7 +205,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
if (kb) {
ob->shapenr = BLI_findindex(&key->block, kb) + 1;
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
WM_event_add_notifier(C, NC_OBJECT | ND_DATA, ob);
}
else {
@@ -326,6 +326,29 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode)
return true;
}
+static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
+{
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Object *ob = OBACT(view_layer);
+ SculptSession *ss = ob->sculpt;
+ MVert *mvert;
+ MPropCol *vcol;
+ int *index, i;
+
+ if (unode->maxvert) {
+ /* regular mesh restore */
+ index = unode->index;
+ mvert = ss->mvert;
+ vcol = ss->vcol;
+
+ for (i = 0; i < unode->totvert; i++) {
+ copy_v4_v4(vcol[index[i]].color, unode->col[i]);
+ mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ return true;
+}
+
static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -394,10 +417,7 @@ static void sculpt_undo_bmesh_restore_generic_task_cb(
BKE_pbvh_node_mark_redraw(nodes[n]);
}
-static void sculpt_undo_bmesh_restore_generic(bContext *C,
- SculptUndoNode *unode,
- Object *ob,
- SculptSession *ss)
+static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, SculptSession *ss)
{
if (unode->applied) {
BM_log_undo(ss->bm, ss->bm_log);
@@ -411,12 +431,11 @@ static void sculpt_undo_bmesh_restore_generic(bContext *C,
if (unode->type == SCULPT_UNDO_MASK) {
int totnode;
PBVHNode **nodes;
- Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(
0, totnode, nodes, sculpt_undo_bmesh_restore_generic_task_cb, &settings);
@@ -588,7 +607,7 @@ static int sculpt_undo_bmesh_restore(bContext *C,
return true;
default:
if (ss->bm_log) {
- sculpt_undo_bmesh_restore_generic(C, unode, ob, ss);
+ sculpt_undo_bmesh_restore_generic(unode, ob, ss);
return true;
}
break;
@@ -633,7 +652,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
rebuild = true;
BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask, false);
SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
@@ -659,7 +678,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
* ensure object is updated after the node is handled. */
const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first;
if (first_unode->type != SCULPT_UNDO_GEOMETRY) {
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false);
}
if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) {
@@ -712,10 +731,15 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
break;
case SCULPT_UNDO_FACE_SETS:
break;
+ case SCULPT_UNDO_COLOR:
+ if (sculpt_undo_restore_color(C, unode)) {
+ update = true;
+ }
+ break;
case SCULPT_UNDO_GEOMETRY:
sculpt_undo_geometry_restore(unode, ob);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false);
break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
@@ -998,6 +1022,12 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert;
break;
+ case SCULPT_UNDO_COLOR:
+ unode->col = MEM_callocN(sizeof(MPropCol) * allvert, "SculptUndoNode.col");
+
+ usculpt->undo_size += (sizeof(MPropCol) * sizeof(int)) * allvert;
+
+ break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
@@ -1083,6 +1113,18 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode)
BKE_pbvh_vertex_iter_end;
}
+static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode)
+{
+ SculptSession *ss = ob->sculpt;
+ PBVHVertexIter vd;
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL)
+ {
+ copy_v4_v4(unode->col[vd.i], vd.col);
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
static SculptUndoNodeGeometry *sculpt_undo_geometry_get(SculptUndoNode *unode)
{
if (!unode->geometry_original.is_initialized) {
@@ -1203,6 +1245,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
case SCULPT_UNDO_GEOMETRY:
case SCULPT_UNDO_FACE_SETS:
+ case SCULPT_UNDO_COLOR:
break;
}
}
@@ -1272,6 +1315,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
case SCULPT_UNDO_MASK:
sculpt_undo_store_mask(ob, unode);
break;
+ case SCULPT_UNDO_COLOR:
+ sculpt_undo_store_color(ob, unode);
+ break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: