diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
20 files changed, 473 insertions, 134 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index 57d4bd67438..944b3f953a0 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -376,7 +376,7 @@ static int hide_show_exec(bContext *C, wmOperator *op) } /* End undo. */ - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); /* Ensure that edges and faces get hidden as well (not used by * sculpt but it looks wrong when entering editmode otherwise). */ diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 5e89a4823db..5929aa75b45 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -173,7 +173,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); if (nodes) { MEM_freeN(nodes); @@ -707,7 +707,8 @@ static void sculpt_gesture_apply(bContext *C, SculptGestureContext *sgcontext) operation->sculpt_gesture_end(C, sgcontext); - SCULPT_undo_push_end(); + Object *ob = CTX_data_active_object(C); + SCULPT_undo_push_end(ob); SCULPT_tag_update_overlays(C); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 3f0f97dffd2..33b92c22d3f 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -27,6 +27,7 @@ #include "RNA_access.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -190,7 +191,14 @@ bool vertex_paint_mode_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly; + if (!(ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly)) { + return false; + } + + CustomDataLayer *layer = BKE_id_attributes_active_color_get((ID *)ob->data); + AttributeDomain domain = BKE_id_attribute_domain((ID *)ob->data, layer); + + return layer && layer->type == CD_MLOOPCOL && domain == ATTR_DOMAIN_CORNER; } static bool vertex_paint_poll_ex(bContext *C, bool check_tool) @@ -3559,7 +3567,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) /* identifiers */ ot->name = "Vertex Paint"; ot->idname = "PAINT_OT_vertex_paint"; - ot->description = "Paint a stroke in the active vertex color layer"; + ot->description = "Paint a stroke in the active color attribute layer"; /* api callbacks */ ot->invoke = vpaint_invoke; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index e82ac058281..e03f12025c8 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -31,6 +31,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_ccg.h" #include "BKE_colortools.h" @@ -143,19 +144,27 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index) return NULL; } -const float *SCULPT_vertex_color_get(SculptSession *ss, int index) +bool SCULPT_has_loop_colors(const Object *ob) { - 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; + Mesh *me = BKE_object_get_original_mesh(ob); + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + + return layer && BKE_id_attribute_domain(&me->id, layer) == ATTR_DOMAIN_CORNER; +} + +bool SCULPT_has_colors(const SculptSession *ss) +{ + return ss->vcol || ss->mcol; +} + +void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]) +{ + BKE_pbvh_vertex_color_get(ss->pbvh, index, r_color); +} + +void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]) +{ + BKE_pbvh_vertex_color_set(ss->pbvh, index, color); } void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) @@ -1045,6 +1054,7 @@ void SCULPT_tag_update_overlays(bContext *C) WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + View3D *v3d = CTX_wm_view3d(C); if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -1378,7 +1388,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, *vd.mask = orig_data.mask; } else if (orig_data.unode->type == SCULPT_UNDO_COLOR) { - copy_v4_v4(vd.col, orig_data.col); + SCULPT_vertex_color_set(ss, vd.index, orig_data.col); } if (vd.mvert) { @@ -3149,7 +3159,7 @@ static void do_brush_action_task_cb(void *__restrict userdata, 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)) { + else if (SCULPT_TOOL_NEEDS_COLOR(data->brush->sculpt_tool)) { SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR); BKE_pbvh_node_mark_update_color(data->nodes[n]); } @@ -3167,12 +3177,13 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe /* 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; + if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) { + if (type != PBVH_FACES) { + return; + } + + BKE_pbvh_ensure_node_loops(ss->pbvh); } /* Build a list of all nodes that are potentially within the brush's area of influence */ @@ -3188,6 +3199,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; float radius_scale = 1.0f; + /* With these options enabled not all required nodes are inside the original brush radius, so * the brush can produce artifacts in some situations. */ if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) { @@ -3849,10 +3861,12 @@ bool SCULPT_mode_poll(bContext *C) bool SCULPT_vertex_colors_poll(bContext *C) { - if (!U.experimental.use_sculpt_vertex_colors) { + if (!SCULPT_mode_poll(C)) { return false; } - return SCULPT_mode_poll(C); + + Object *ob = CTX_data_active_object(C); + return ob->sculpt && SCULPT_has_colors(ob->sculpt); } bool SCULPT_mode_poll_view3d(bContext *C) @@ -4591,6 +4605,7 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd, (brush->sculpt_tool == SCULPT_TOOL_POSE) || (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) || (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) || + SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) || (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) || (brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR)); @@ -4950,7 +4965,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, needs_colors; + bool need_pmap, needs_colors; bool need_mask = false; if (brush->sculpt_tool == SCULPT_TOOL_MASK) { @@ -4965,8 +4980,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) view3d_operator_needs_opengl(C); sculpt_brush_init_tex(scene, sd, ss); - is_smooth = sculpt_needs_connectivity_info(sd, brush, ss, mode); - needs_colors = ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR); + need_pmap = sculpt_needs_connectivity_info(sd, brush, ss, mode); + needs_colors = SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool); if (needs_colors) { BKE_sculpt_color_layer_create_if_needed(ob); @@ -4975,7 +4990,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of * earlier steps modifying the data. */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask, needs_colors); + BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, need_mask, needs_colors); } static void sculpt_restore_mesh(Sculpt *sd, Object *ob) @@ -5174,6 +5189,14 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) { + View3D *v3d = CTX_wm_view3d(C); + if (v3d) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } + } ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); @@ -5302,7 +5325,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str SCULPT_cache_free(ss->cache); ss->cache = NULL; - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index a4cfb611138..dcf90f9e819 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -1493,7 +1493,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index dd8921d575f..fe69cf6b84f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -117,7 +117,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) } MEM_SAFE_FREE(nodes); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); /* Force rebuild of PBVH for better BB placement. */ SCULPT_pbvh_clear(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 58da5adc5e3..4f884420401 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -274,7 +274,7 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, } SCULPT_dynamic_topology_disable_ex(bmain, depsgraph, scene, ob, NULL); if (use_undo) { - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); } } } @@ -294,7 +294,7 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); if (use_undo) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 2ef35d540b9..46940b619e6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -1128,7 +1128,7 @@ static void sculpt_expand_restore_color_data(SculptSession *ss, ExpandCache *exp PBVHNode *node = nodes[n]; PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - copy_v4_v4(vd.col, expand_cache->original_colors[vd.index]); + SCULPT_vertex_color_set(ss, vd.index, expand_cache->original_colors[vd.index]); } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_redraw(node); @@ -1192,7 +1192,7 @@ static void sculpt_expand_cancel(bContext *C, wmOperator *UNUSED(op)) sculpt_expand_restore_original_state(C, ob, ss->expand_cache); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); sculpt_expand_cache_free(ss); } @@ -1287,7 +1287,7 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { float initial_color[4]; - copy_v4_v4(initial_color, vd.col); + SCULPT_vertex_color_get(ss, vd.index, initial_color); const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); float fade; @@ -1314,7 +1314,8 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, continue; } - copy_v4_v4(vd.col, final_color); + SCULPT_vertex_color_set(ss, vd.index, final_color); + any_changed = true; if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -1370,7 +1371,7 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) { expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors"); for (int i = 0; i < totvert; i++) { - copy_v4_v4(expand_cache->original_colors[i], SCULPT_vertex_color_get(ss, i)); + SCULPT_vertex_color_get(ss, i, expand_cache->original_colors[i]); } } } @@ -1526,7 +1527,7 @@ static void sculpt_expand_finish(bContext *C) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); /* Tag all nodes to redraw to avoid artifacts after the fast partial updates. */ PBVHNode **nodes; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 23bc9fbb54d..7171c241534 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -398,7 +398,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_tag_update_overlays(C); @@ -737,7 +737,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) break; } - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ SCULPT_visibility_sync_all_face_sets_to_vertices(ob); @@ -927,7 +927,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) /* Sync face sets visibility and vertex visibility. */ SCULPT_visibility_sync_all_face_sets_to_vertices(ob); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); @@ -1354,7 +1354,7 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, SCULPT_undo_push_begin(ob, "face set edit"); SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); face_set_edit_do_post_visibility_updates(ob, nodes, totnode); MEM_freeN(nodes); } @@ -1382,7 +1382,7 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C, } SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); MEM_freeN(nodes); } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 377f1e0ed32..cbb9180a209 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -181,12 +181,16 @@ static void color_filter_task_cb(void *__restrict userdata, fade = clamp_f(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); + + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + + blend_color_interpolate_float(final_color, col, smooth_color, fade); break; } } - copy_v3_v3(vd.col, final_color); + SCULPT_vertex_color_set(ss, vd.index, final_color); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -205,7 +209,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent float filter_strength = RNA_float_get(op->ptr, "strength"); if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_filter_cache_free(ss); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR); return OPERATOR_FINISHED; @@ -247,7 +251,6 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = CTX_data_active_object(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; const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL); @@ -269,7 +272,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - if (!ss->vcol) { + if (!SCULPT_has_colors(ss)) { return OPERATOR_CANCELLED; } @@ -280,10 +283,9 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of * earlier steps modifying the data. */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - const bool needs_topology_info = mode == COLOR_FILTER_SMOOTH || use_automasking; - BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, true); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true); - if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) { + if (BKE_pbvh_type(pbvh) == PBVH_FACES && !ob->sculpt->pmap) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index e88265714b8..c8137301de5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -239,7 +239,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_tag_update_overlays(C); @@ -447,7 +447,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); ED_region_tag_redraw(region); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index b73e182fcab..4b832256dae 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -110,6 +110,10 @@ void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int und ss->filter_cache->random_seed = rand(); + if (undo_type == SCULPT_UNDO_COLOR) { + BKE_pbvh_ensure_node_loops(ss->pbvh); + } + const float center[3] = {0.0f}; SculptSearchSphereData search_data = { .original = true, @@ -597,7 +601,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 8cab0349356..73fc5bd68f3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -20,6 +20,10 @@ #include "BLI_gsqueue.h" #include "BLI_threads.h" +#ifdef __cplusplus +extern "C" { +#endif + struct AutomaskingCache; struct KeyBlock; struct Object; @@ -139,9 +143,16 @@ typedef struct SculptUndoNode { float *mask; int totvert; + float (*loop_col)[4]; + float (*orig_loop_col)[4]; + int totloop; + /* non-multires */ int maxvert; /* to verify if totvert it still the same */ - int *index; /* to restore into right location */ + int *index; /* Unique vertex indices, to restore into right location */ + int maxloop; + int *loop_index; + BLI_bitmap *vert_hidden; /* multires */ @@ -857,7 +868,14 @@ 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); +void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]); +void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]); + +/** Returns true if a color attribute exists in the current sculpt session. */ +bool SCULPT_has_colors(const SculptSession *ss); + +/** Returns true if the active color attribute is on loop (ATTR_DOMAIN_CORNER) domain. */ +bool SCULPT_has_loop_colors(const struct Object *ob); const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index); void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); @@ -1416,8 +1434,8 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node); SculptUndoNode *SCULPT_undo_get_first_node(void); void SCULPT_undo_push_begin(struct Object *ob, const char *name); -void SCULPT_undo_push_end(void); -void SCULPT_undo_push_end_ex(bool use_nested_undo); +void SCULPT_undo_push_end(struct Object *ob); +void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo); /** \} */ @@ -1720,3 +1738,9 @@ void SCULPT_bmesh_topology_rake( void SCULPT_OT_brush_stroke(struct wmOperatorType *ot); /* end sculpt_ops.c */ + +#define SCULPT_TOOL_NEEDS_COLOR(tool) ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 8fc10061f83..201e02b8235 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -82,7 +82,7 @@ static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op) SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); } SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); ED_workspace_status_text(C, NULL); } @@ -237,7 +237,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c index 42101988b11..025f34ab2d7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c @@ -150,7 +150,7 @@ static int sculpt_mask_init_exec(bContext *C, wmOperator *op) multires_stitch_grids(ob); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); MEM_SAFE_FREE(nodes); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index cd174681ccb..f84852d1d0e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -234,7 +234,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) /* Finish undo. */ BM_log_all_added(ss->bm, ss->bm_log); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); break; case PBVH_FACES: @@ -396,7 +396,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain, SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); if (has_undo) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); } } else { @@ -508,6 +508,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); if (wm->op_undo_depth <= 1) { SCULPT_undo_push_begin(ob, op->type->name); + SCULPT_undo_push_end(ob); } } } @@ -749,11 +750,14 @@ static int sculpt_sample_color_invoke(bContext *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) { + float active_vertex_color[4]; + + if (!SCULPT_has_colors(ss)) { return OPERATOR_CANCELLED; } + SCULPT_vertex_color_get(ss, active_vertex, active_vertex_color); + float color_srgb[3]; copy_v3_v3(color_srgb, active_vertex_color); IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb); @@ -862,7 +866,7 @@ static void do_mask_by_color_contiguous_update_nodes_cb( } BKE_pbvh_vertex_iter_end; if (update_node) { - BKE_pbvh_node_mark_redraw(data->nodes[n]); + BKE_pbvh_node_mark_update_mask(data->nodes[n]); } } @@ -870,7 +874,10 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb( SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) { MaskByColorContiguousFloodFillData *data = userdata; - const float *current_color = SCULPT_vertex_color_get(ss, to_v); + float current_color[4]; + + SCULPT_vertex_color_get(ss, to_v, current_color); + float new_vertex_mask = sculpt_mask_by_color_delta_get( current_color, data->initial_color, data->threshold, data->invert); data->new_mask[to_v] = new_vertex_mask; @@ -909,7 +916,11 @@ static void sculpt_mask_by_color_contiguous(Object *object, ffd.threshold = threshold; ffd.invert = invert; ffd.new_mask = new_mask; - copy_v3_v3(ffd.initial_color, SCULPT_vertex_color_get(ss, vertex)); + + float color[4]; + SCULPT_vertex_color_get(ss, vertex, color); + + copy_v3_v3(ffd.initial_color, color); SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd); SCULPT_floodfill_free(&flood); @@ -951,12 +962,17 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, const float threshold = data->mask_by_color_threshold; const bool invert = data->mask_by_color_invert; const bool preserve_mask = data->mask_by_color_preserve_mask; - const float *active_color = SCULPT_vertex_color_get(ss, data->mask_by_color_vertex); + float active_color[4]; + + SCULPT_vertex_color_get(ss, data->mask_by_color_vertex, active_color); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + const float current_mask = *vd.mask; - const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert); + const float new_mask = sculpt_mask_by_color_delta_get(active_color, col, threshold, invert); *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); if (current_mask == *vd.mask) { @@ -1014,10 +1030,14 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven return OPERATOR_CANCELLED; } - if (!ss->vcol) { + if (!SCULPT_has_colors(ss)) { return OPERATOR_CANCELLED; } + if (SCULPT_has_loop_colors(ob)) { + BKE_pbvh_ensure_node_loops(ss->pbvh); + } + SCULPT_vertex_random_access_ensure(ss); /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, @@ -1043,9 +1063,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven } BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 5d248cb520a..cc4392c6a8a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -85,7 +85,11 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, float smooth_color[4]; SCULPT_neighbor_color_average(ss, smooth_color, vd.index); - blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade); + float col[4]; + + SCULPT_vertex_color_get(ss, vd.index, col); + blend_color_interpolate_float(col, col, smooth_color, fade); + SCULPT_vertex_color_set(ss, vd.index, col); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -154,7 +158,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, float noise = 1.0f; const float density = ss->cache->paint_brush.density; if (density < 1.0f) { - const float hash_noise = BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index); + const float hash_noise = (float) BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index); if (hash_noise > density) { noise = density * hash_noise; fade = fade * noise; @@ -177,9 +181,11 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, /* 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); + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + IMB_blend_color_float(col, orig_data.col, buffer_color, brush->blend); + CLAMP4(col, 0.0f, 1.0f); + SCULPT_vertex_color_set(ss, vd.index, col); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -214,7 +220,10 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata, continue; } - add_v4_v4(swptd->color, vd.col); + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + + add_v4_v4(swptd->color, col); swptd->tot_samples++; } BKE_pbvh_vertex_iter_end; @@ -236,13 +245,13 @@ 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) { + if (!SCULPT_has_colors(ss)) { return; } if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - ss->cache->density_seed = BLI_hash_int_01(ss->cache->location[0] * 1000); + ss->cache->density_seed = (float) BLI_hash_int_01(ss->cache->location[0] * 1000); } return; } @@ -384,6 +393,9 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, float interp_color[4]; copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]); + float no[3]; + SCULPT_vertex_normal_get(ss, vd.index, no); + switch (brush->smear_deform_type) { case BRUSH_SMEAR_DEFORM_DRAG: sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); @@ -395,29 +407,89 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, sub_v3_v3v3(current_disp, vd.co, ss->cache->location); break; } + + /* Project into vertex plane. */ + madd_v3_v3fl(current_disp, no, -dot_v3v3(current_disp, no)); + 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) { - continue; + float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float totw = 0.0f; + + /* + * NOTE: we have to do a nested iteration here to avoid + * blocky artifacts on quad topologies. The runtime cost + * is not as bad as it seems due to neighbor iteration + * in the sculpt code being cache bound; once the data is in + * the cache iterating over it a few more times is not terribly + * costly. + */ + + SculptVertexNeighborIter ni2; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni2) { + const float *nco = SCULPT_vertex_co_get(ss, ni2.index); + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.index, ni) { + if (ni.index == vd.index) { + continue; + } + + float vertex_disp[3]; + float vertex_disp_norm[3]; + + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + + /* Weight by how close we are to our target distance from vd.co. */ + float w = (1.0f + fabsf(len_v3(vertex_disp) / ss->cache->bstrength - 1.0f)); + + /* TODO: use cotangents (or at least face areas) here. */ + float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.index), nco); + if (len > 0.0f) { + len = ss->cache->bstrength / len; + } + else { /* Coincident point. */ + len = 1.0f; + } + + /* Multiply weight with edge lengths (in the future this will be + cotangent weights or face areas). */ + w *= len; + + /* Build directional weight. */ + + /* Project into vertex plane. */ + madd_v3_v3fl(vertex_disp, no, -dot_v3v3(no, vertex_disp)); + normalize_v3_v3(vertex_disp_norm, vertex_disp); + + if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) { + continue; + } + + const float *neighbor_color = ss->cache->prev_colors[ni.index]; + float color_interp = -dot_v3v3(current_disp_norm, vertex_disp_norm); + + /* Square directional weight to get a somewhat sharper result. */ + w *= color_interp * color_interp; + + madd_v4_v4fl(accum, neighbor_color, w); + totw += w; } - 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); } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni2); + + if (totw != 0.0f) { + mul_v4_fl(accum, 1.0f / totw); + } + + blend_color_mix_float(interp_color, interp_color, accum); - blend_color_interpolate_float(vd.col, ss->cache->prev_colors[vd.index], interp_color, fade); + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + blend_color_interpolate_float(col, ss->cache->prev_colors[vd.index], interp_color, fade); + SCULPT_vertex_color_set(ss, vd.index, col); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -435,7 +507,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, 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)); + SCULPT_vertex_color_get(ss, vd.index, ss->cache->prev_colors[vd.index]); } BKE_pbvh_vertex_iter_end; } @@ -445,7 +517,7 @@ 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) { + if (!SCULPT_has_colors(ss)) { return; } @@ -455,7 +527,7 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode 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)); + SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 482bdf97d78..53babc3d36d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -179,7 +179,11 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index)); + float tmp[4] = {0}; + + SCULPT_vertex_color_get(ss, ni.index, tmp); + + add_v4_v4(avg, tmp); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -188,7 +192,7 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index mul_v4_v4fl(result, avg, 1.0f / total); } else { - copy_v4_v4(result, SCULPT_vertex_color_get(ss, index)); + SCULPT_vertex_color_get(ss, index, result); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 5f55546c8a0..b3616254b26 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -203,7 +203,7 @@ void ED_sculpt_end_transform(struct bContext *C, Object *ob) * undo system works separate from regular undo and this is require to properly * finish an undo step also when canceling. */ const bool use_nested_undo = true; - SCULPT_undo_push_end_ex(use_nested_undo); + SCULPT_undo_push_end_ex(ob, use_nested_undo); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 2ec553c63c7..1354277fbdd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -24,6 +24,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "BKE_attribute.h" #include "BKE_ccg.h" #include "BKE_context.h" #include "BKE_customdata.h" @@ -31,6 +32,7 @@ #include "BKE_key.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" #include "BKE_multires.h" #include "BKE_object.h" @@ -94,13 +96,38 @@ * End of dynamic topology and symmetrize in this mode are handled in a special * manner as well. */ +#define NO_ACTIVE_LAYER ATTR_DOMAIN_AUTO + typedef struct UndoSculpt { ListBase nodes; size_t undo_size; } UndoSculpt; +typedef struct SculptAttrRef { + AttributeDomain domain; + int type; + char name[MAX_CUSTOMDATA_LAYER_NAME]; + bool was_set; +} SculptAttrRef; + +typedef struct SculptUndoStep { + UndoStep step; + /* NOTE: will split out into list for multi-object-sculpt-mode. */ + UndoSculpt data; + + /* Active color attribute at the start of this undo step. */ + SculptAttrRef active_color_start; + + /* Active color attribute at the end of this undo step. */ + SculptAttrRef active_color_end; + + bContext *C; +} SculptUndoStep; + static UndoSculpt *sculpt_undo_get_nodes(void); +static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b); +static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr); static void update_cb(PBVHNode *node, void *rebuild) { @@ -313,17 +340,30 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode) Object *ob = OBACT(view_layer); SculptSession *ss = ob->sculpt; - if (unode->maxvert) { - /* regular mesh restore */ - int *index = unode->index; - MPropCol *vcol = ss->vcol; + bool modified = false; + /* NOTE: even with loop colors we still store derived + * vertex colors for original data lookup.*/ + if (unode->col && !unode->loop_col) { + BKE_pbvh_swap_colors(ss->pbvh, unode->index, unode->totvert, unode->col); + modified = true; + } + + Mesh *me = BKE_object_get_original_mesh(ob); + + if (unode->loop_col && unode->maxloop == me->totloop) { + BKE_pbvh_swap_colors(ss->pbvh, unode->loop_index, unode->totloop, unode->loop_col); + + modified = true; + } + + if (modified) { for (int i = 0; i < unode->totvert; i++) { - copy_v4_v4(vcol[index[i]].color, unode->col[i]); - BKE_pbvh_vert_mark_update(ss->pbvh, index[i]); + BKE_pbvh_vert_mark_update(ss->pbvh, unode->index[i]); } } - return true; + + return modified; } static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) @@ -739,8 +779,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase if (sculpt_undo_restore_color(C, unode)) { update = true; } - break; + break; case SCULPT_UNDO_GEOMETRY: need_refine_subdiv = true; sculpt_undo_geometry_restore(unode, ob); @@ -843,12 +883,21 @@ static void sculpt_undo_free_list(ListBase *lb) if (unode->co) { MEM_freeN(unode->co); } + if (unode->col) { + MEM_freeN(unode->col); + } + if (unode->loop_col) { + MEM_freeN(unode->loop_col); + } if (unode->no) { MEM_freeN(unode->no); } if (unode->index) { MEM_freeN(unode->index); } + if (unode->loop_index) { + MEM_freeN(unode->loop_index); + } if (unode->grids) { MEM_freeN(unode->grids); } @@ -1010,6 +1059,21 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt unode->totvert = totvert; } + bool need_loops = type == SCULPT_UNDO_COLOR; + + if (need_loops) { + int totloop; + + BKE_pbvh_node_num_loops(ss->pbvh, node, &totloop); + + unode->loop_index = MEM_calloc_arrayN(totloop, sizeof(int), __func__); + unode->maxloop = 0; + unode->totloop = totloop; + + size_t alloc_size = sizeof(int) * (size_t)totloop; + usculpt->undo_size += alloc_size; + } + switch (type) { case SCULPT_UNDO_COORDS: { size_t alloc_size = sizeof(*unode->co) * (size_t)allvert; @@ -1041,9 +1105,20 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt break; } case SCULPT_UNDO_COLOR: { + /* Allocate vertex colors, even for loop colors we still + * need this for original data lookup. */ const size_t alloc_size = sizeof(*unode->col) * (size_t)allvert; unode->col = MEM_callocN(alloc_size, "SculptUndoNode.col"); usculpt->undo_size += alloc_size; + + /* Allocate loop colors separately too. */ + if (ss->vcol_domain == ATTR_DOMAIN_CORNER) { + size_t alloc_size_loop = sizeof(float) * 4 * (size_t)unode->totloop; + + unode->loop_col = MEM_calloc_arrayN( + unode->totloop, sizeof(float) * 4, "SculptUndoNode.loop_col"); + usculpt->undo_size += alloc_size_loop; + } break; } case SCULPT_UNDO_DYNTOPO_BEGIN: @@ -1139,12 +1214,19 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) 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); + BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES); + + int allvert; + BKE_pbvh_node_num_verts(ss->pbvh, unode->node, NULL, &allvert); + + /* NOTE: even with loop colors we still store (derived) + * vertex colors for original data lookup. */ + BKE_pbvh_store_colors_vertex(ss->pbvh, unode->index, allvert, unode->col); + + if (unode->loop_col && unode->totloop) { + BKE_pbvh_store_colors(ss->pbvh, unode->loop_index, unode->totloop, unode->loop_col); } - BKE_pbvh_vertex_iter_end; } static SculptUndoNodeGeometry *sculpt_undo_geometry_get(SculptUndoNode *unode) @@ -1316,11 +1398,23 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType memcpy(unode->grids, grids, sizeof(int) * totgrid); } else { - const int *vert_indices; - int allvert; - BKE_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert); + const int *vert_indices, *loop_indices; + int allvert, allloop; + + BKE_pbvh_node_num_verts(ss->pbvh, unode->node, NULL, &allvert); BKE_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL); - memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert); + memcpy(unode->index, vert_indices, sizeof(int) * allvert); + + if (unode->loop_index) { + BKE_pbvh_node_num_loops(ss->pbvh, unode->node, &allloop); + BKE_pbvh_node_get_loops(ss->pbvh, unode->node, &loop_indices, NULL); + + if (allloop) { + memcpy(unode->loop_index, loop_indices, sizeof(int) * allloop); + + unode->maxloop = BKE_object_get_original_mesh(ob)->totloop; + } + } } switch (type) { @@ -1362,6 +1456,29 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType return unode; } +static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b) +{ + return a->domain == b->domain && a->type == b->type && STREQ(a->name, b->name); +} + +static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr) +{ + Mesh *me = BKE_object_get_original_mesh(ob); + CustomDataLayer *layer; + + if (ob && me && (layer = BKE_id_attributes_active_color_get((ID *)me))) { + attr->domain = BKE_id_attribute_domain((ID *)me, layer); + BLI_strncpy(attr->name, layer->name, sizeof(attr->name)); + attr->type = layer->type; + } + else { + attr->domain = NO_ACTIVE_LAYER; + attr->name[0] = 0; + } + + attr->was_set = true; +} + void SCULPT_undo_push_begin(Object *ob, const char *name) { UndoStack *ustack = ED_undo_stack_get(); @@ -1376,15 +1493,28 @@ void SCULPT_undo_push_begin(Object *ob, const char *name) /* Special case, we never read from this. */ bContext *C = NULL; - BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); + SculptUndoStep *us = (SculptUndoStep *)BKE_undosys_step_push_init_with_type( + ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); + + if (!us->active_color_start.was_set) { + sculpt_save_active_attribute(ob, &us->active_color_start); + } + + /* Set end attribute in case SCULPT_undo_push_end is not called, + * so we don't end up with corrupted state. + */ + if (!us->active_color_end.was_set) { + sculpt_save_active_attribute(ob, &us->active_color_end); + us->active_color_end.was_set = false; + } } -void SCULPT_undo_push_end(void) +void SCULPT_undo_push_end(Object *ob) { - SCULPT_undo_push_end_ex(false); + SCULPT_undo_push_end_ex(ob, false); } -void SCULPT_undo_push_end_ex(const bool use_nested_undo) +void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptUndoNode *unode; @@ -1408,17 +1538,54 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo) } WM_file_tag_modified(); } + + UndoStack *ustack = ED_undo_stack_get(); + SculptUndoStep *us = (SculptUndoStep *)BKE_undosys_stack_init_or_active_with_type( + ustack, BKE_UNDOSYS_TYPE_SCULPT); + + sculpt_save_active_attribute(ob, &us->active_color_end); } /* -------------------------------------------------------------------- */ /** \name Implements ED Undo System * \{ */ -typedef struct SculptUndoStep { - UndoStep step; - /* NOTE: will split out into list for multi-object-sculpt-mode. */ - UndoSculpt data; -} SculptUndoStep; +static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr) +{ + if (attr->domain == ATTR_DOMAIN_AUTO) { + return; + } + + Object *ob = CTX_data_active_object(C); + Mesh *me = BKE_object_get_original_mesh(ob); + + SculptAttrRef existing; + sculpt_save_active_attribute(ob, &existing); + + CustomDataLayer *layer; + layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); + + if (!layer) { + /* Memfile undo killed the layer; re-create it. */ + CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &me->vdata : &me->ldata; + int totelem = attr->domain == ATTR_DOMAIN_POINT ? me->totvert : me->totloop; + + CustomData_add_layer_named(cdata, attr->type, CD_DEFAULT, NULL, totelem, attr->name); + layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); + } + + if (layer) { + BKE_id_attributes_active_color_set(&me->id, layer); + + if (ob->sculpt && ob->sculpt->pbvh) { + BKE_pbvh_update_active_vcol(ob->sculpt->pbvh, me); + + if (!sculpt_attribute_ref_equals(&existing, attr)) { + BKE_pbvh_update_vertex_data(ob->sculpt->pbvh, PBVH_UpdateColor); + } + } + } +} static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) { @@ -1454,6 +1621,7 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == true); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = false; } @@ -1463,6 +1631,7 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == false); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = true; } @@ -1484,10 +1653,17 @@ static void sculpt_undosys_step_decode_undo(struct bContext *C, while ((us_iter != us) || (!is_final && us_iter == us)) { BLI_assert(us_iter->step.type == us->step.type); /* Previous loop ensures this. */ + + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start); sculpt_undosys_step_decode_undo_impl(C, depsgraph, us_iter); + if (us_iter == us) { + if (us_iter->step.prev && us_iter->step.prev->type == BKE_UNDOSYS_TYPE_SCULPT) { + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter->step.prev)->active_color_end); + } break; } + us_iter = (SculptUndoStep *)us_iter->step.prev; } } @@ -1504,8 +1680,11 @@ static void sculpt_undosys_step_decode_redo(struct bContext *C, us_iter = (SculptUndoStep *)us_iter->step.prev; } while (us_iter && (us_iter->step.is_applied == false)) { + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start); sculpt_undosys_step_decode_redo_impl(C, depsgraph, us_iter); + if (us_iter == us) { + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_end); break; } us_iter = (SculptUndoStep *)us_iter->step.next; @@ -1526,7 +1705,7 @@ static void sculpt_undosys_step_decode( ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = OBACT(view_layer); if (ob && (ob->type == OB_MESH)) { - if (ob->mode & OB_MODE_SCULPT) { + if (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT)) { /* Pass. */ } else { @@ -1579,7 +1758,7 @@ void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name) void ED_sculpt_undo_geometry_end(struct Object *ob) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); } void ED_sculpt_undosys_type(UndoType *ut) @@ -1705,7 +1884,7 @@ void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str) SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY); geometry_unode->geometry_clear_pbvh = false; - SCULPT_undo_push_end(); + SCULPT_undo_push_end(object); } /** \} */ |