diff options
author | Julian Eisel <julian@blender.org> | 2020-07-01 18:13:57 +0300 |
---|---|---|
committer | Julian Eisel <julian@blender.org> | 2020-07-01 18:13:57 +0300 |
commit | 0829cebeb024095c268f190c34daa8ae9a5a224c (patch) | |
tree | 12ee5a4a1c2a32e12eff47c8eb9bb0ed217791c1 /source/blender/editors/sculpt_paint | |
parent | cfde6ebf450594faa57c4bfeaecff10fe512c91b (diff) | |
parent | 42be3964eb201180f6b0fa1ff6ce43b8c3845bc2 (diff) |
Merge branch 'master' into asset-uuid--archivedasset-uuid--archived
Diffstat (limited to 'source/blender/editors/sculpt_paint')
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 = ⦥ 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: |