diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 857 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 26 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_undo.c | 81 |
3 files changed, 930 insertions, 34 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 390b69df7ff..b87a050a7c2 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -35,6 +35,8 @@ #include "BLT_translation.h" +#include "PIL_time.h" + #include "DNA_customdata_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -218,6 +220,249 @@ static void sculpt_active_vertex_normal_get(SculptSession *ss, float normal[3]) sculpt_vertex_normal_get(ss, sculpt_active_vertex_get(ss), normal); } +/* Sculpt Face Sets and Visibility*/ + +static void sculpt_vertex_visible_set(SculptSession *ss, int index, bool visible) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + SET_FLAG_FROM_TEST(ss->mvert[index].flag, !visible, ME_HIDE); + ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE; + break; + case PBVH_BMESH: + BM_elem_flag_set(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN, !visible); + break; + case PBVH_GRIDS: + break; + } +} + +static bool sculpt_vertex_visible_get(SculptSession *ss, int index) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return !(ss->mvert[index].flag & ME_HIDE); + case PBVH_BMESH: + return !BM_elem_flag_test(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN); + case PBVH_GRIDS: + return true; + } + return true; +} + +static void sculpt_face_set_visibility_set(SculptSession *ss, int face_set, bool visible) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + for (int i = 0; i < ss->totpoly; i++) { + if (abs(ss->face_sets[i]) == face_set) { + if (visible) { + ss->face_sets[i] = abs(ss->face_sets[i]); + } + else { + ss->face_sets[i] = -abs(ss->face_sets[i]); + } + } + } + break; + case PBVH_BMESH: + break; + case PBVH_GRIDS: + break; + } +} + +static void sculpt_face_sets_visibility_invert(SculptSession *ss) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + for (int i = 0; i < ss->totpoly; i++) { + ss->face_sets[i] *= -1; + } + break; + case PBVH_BMESH: + break; + case PBVH_GRIDS: + break; + } +} + +static void sculpt_face_sets_visibility_all_set(SculptSession *ss, bool visible) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + for (int i = 0; i < ss->totpoly; i++) { + if (visible) { + ss->face_sets[i] = abs(ss->face_sets[i]); + } + else { + ss->face_sets[i] = -abs(ss->face_sets[i]); + } + } + break; + case PBVH_BMESH: + break; + case PBVH_GRIDS: + break; + } +} + +static bool sculpt_vertex_visibility_from_face_sets_get(SculptSession *ss, int index) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + MeshElemMap *vert_map = &ss->pmap[index]; + for (int j = 0; j < ss->pmap[index].count; j++) { + if (ss->face_sets[vert_map->indices[j]] > 0) { + return true; + } + } + return false; + } + case PBVH_BMESH: + return true; + case PBVH_GRIDS: + return true; + } + return true; +} + +static void sculpt_vertex_face_set_set(SculptSession *ss, int index, int face_set) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + MeshElemMap *vert_map = &ss->pmap[index]; + for (int j = 0; j < ss->pmap[index].count; j++) { + if (ss->face_sets[vert_map->indices[j]] > 0) { + ss->face_sets[vert_map->indices[j]] = abs(face_set); + } + else { + ss->face_sets[vert_map->indices[j]] = -abs(face_set); + } + } + } break; + case PBVH_BMESH: + break; + case PBVH_GRIDS: + break; + } +} + +static int sculpt_vertex_face_set_get(SculptSession *ss, int index) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + MeshElemMap *vert_map = &ss->pmap[index]; + int face_set = 0; + for (int i = 0; i < ss->pmap[index].count; i++) { + if (ss->face_sets[vert_map->indices[i]] > face_set) { + face_set = abs(ss->face_sets[vert_map->indices[i]]); + } + } + return face_set; + } + case PBVH_BMESH: + return 0; + case PBVH_GRIDS: + return 0; + } + return 0; +} + +static bool sculpt_vertex_has_face_set(SculptSession *ss, int index, int face_set) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + MeshElemMap *vert_map = &ss->pmap[index]; + for (int i = 0; i < ss->pmap[index].count; i++) { + if (ss->face_sets[vert_map->indices[i]] == face_set) { + return true; + } + } + return false; + } + case PBVH_BMESH: + return true; + case PBVH_GRIDS: + return true; + } + return true; +} + +static void sculpt_visibility_sync_face_sets_to_vertex(SculptSession *ss, int index) +{ + sculpt_vertex_visible_set(ss, index, sculpt_vertex_visibility_from_face_sets_get(ss, index)); +} + +void sculpt_visibility_sync_all_face_sets_to_vertices(SculptSession *ss) +{ + for (int i = 0; i < ss->totvert; i++) { + sculpt_visibility_sync_face_sets_to_vertex(ss, i); + } +} + +static void sculpt_visibility_sync_vertex_to_face_sets(SculptSession *ss, int index) +{ + MeshElemMap *vert_map = &ss->pmap[index]; + const bool visible = sculpt_vertex_visible_get(ss, index); + for (int i = 0; i < ss->pmap[index].count; i++) { + if (visible) { + ss->face_sets[vert_map->indices[i]] = abs(ss->face_sets[vert_map->indices[i]]); + } + else { + ss->face_sets[vert_map->indices[i]] = -abs(ss->face_sets[vert_map->indices[i]]); + } + } + ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE; +} + +void sculpt_visibility_sync_all_vertex_to_face_sets(SculptSession *ss) +{ + for (int i = 0; i < ss->totvert; i++) { + sculpt_visibility_sync_vertex_to_face_sets(ss, i); + } +} + +static bool UNUSED_FUNCTION(sculpt_vertex_has_unique_face_set)(SculptSession *ss, int index) +{ + MeshElemMap *vert_map = &ss->pmap[index]; + int face_set = -1; + for (int i = 0; i < ss->pmap[index].count; i++) { + if (face_set == -1) { + face_set = ss->face_sets[vert_map->indices[i]]; + } + else { + if (ss->face_sets[vert_map->indices[i]] != face_set) { + return false; + } + } + } + return true; +} + +static int sculpt_face_set_next_available_get(SculptSession *ss) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + int next_face_set = 0; + for (int i = 0; i < ss->totpoly; i++) { + if (abs(ss->face_sets[i]) > next_face_set) { + next_face_set = abs(ss->face_sets[i]); + } + } + next_face_set++; + return next_face_set; + } + case PBVH_BMESH: + return 0; + case PBVH_GRIDS: + return 0; + } + return 0; +} + +/* Sculpt Neighbor Iterators */ + #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index) @@ -609,8 +854,12 @@ static bool sculpt_tool_needs_original(const char sculpt_tool) static bool sculpt_tool_is_proxy_used(const char sculpt_tool) { - return ELEM( - sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER, SCULPT_TOOL_POSE, SCULPT_TOOL_CLOTH); + return ELEM(sculpt_tool, + SCULPT_TOOL_SMOOTH, + SCULPT_TOOL_LAYER, + SCULPT_TOOL_POSE, + SCULPT_TOOL_CLOTH, + SCULPT_TOOL_DRAW_FACE_SETS); } static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush *brush) @@ -1219,6 +1468,9 @@ static bool sculpt_automasking_enabled(SculptSession *ss, const Brush *br) if (br->automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) { return true; } + if (br->automasking_flags & BRUSH_AUTOMASKING_FACE_SETS) { + return true; + } return false; } @@ -1305,6 +1557,34 @@ static float *sculpt_topology_automasking_init(Sculpt *sd, Object *ob, float *au return automask_factor; } +static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (!sculpt_automasking_enabled(ss, brush)) { + return NULL; + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { + BLI_assert(!"Face Sets automasking: pmap missing"); + return NULL; + } + + int tot_vert = sculpt_vertex_count_get(ss); + int active_face_set = sculpt_vertex_face_set_get(ss, sculpt_active_vertex_get(ss)); + for (int i = 0; i < tot_vert; i++) { + if (sculpt_vertex_has_face_set(ss, i, active_face_set)) { + automask_factor[i] = 1; + } + else { + automask_factor[i] = 0; + } + } + + return automask_factor; +} + static void sculpt_automasking_init(Sculpt *sd, Object *ob) { SculptSession *ss = ob->sculpt; @@ -1317,6 +1597,10 @@ static void sculpt_automasking_init(Sculpt *sd, Object *ob) sculpt_vertex_random_access_init(ss); sculpt_topology_automasking_init(sd, ob, ss->cache->automask); } + if (brush->automasking_flags & BRUSH_AUTOMASKING_FACE_SETS) { + sculpt_vertex_random_access_init(ss); + sculpt_face_sets_automasking_init(sd, ob, ss->cache->automask); + } } /* ===== Sculpting ===== @@ -1796,6 +2080,8 @@ static float brush_strength(const Sculpt *sd, * brush and object. */ return 10.0f * alpha * flip * pressure * overlap * feather; } + case SCULPT_TOOL_DRAW_FACE_SETS: + return alpha * pressure * overlap * feather; case SCULPT_TOOL_SLIDE_RELAX: return alpha * pressure * overlap * feather * 2.0f; case SCULPT_TOOL_CLAY_STRIPS: @@ -2969,6 +3255,74 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BKE_pbvh_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); } +static void do_draw_face_sets_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 = 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); + + 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 * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + tls->thread_id); + + if (fade > 0.05f) { + sculpt_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0 && + ss->cache->radial_symmetry_pass == 0) { + if (ss->cache->invert) { + /* When inverting the brush, pick the paint face mask ID from the mesh. */ + ss->cache->paint_face_set = sculpt_vertex_face_set_get(ss, sculpt_active_vertex_get(ss)); + } + else { + /* By default create a new Face Sets. */ + ss->cache->paint_face_set = sculpt_face_set_next_available_get(ss); + } + } + + BKE_curvemapping_initialize(brush->curve); + + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + PBVHParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); + BKE_pbvh_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings); +} + static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -5684,10 +6038,13 @@ static void do_brush_action_task_cb(void *__restrict userdata, { SculptThreadedTaskData *data = userdata; - sculpt_undo_push_node(data->ob, - data->nodes[n], - data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : - SCULPT_UNDO_COORDS); + /* Face Sets modifications do a single undo push */ + if (data->brush->sculpt_tool != SCULPT_TOOL_DRAW_FACE_SETS) { + 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) { BKE_pbvh_node_mark_update_mask(data->nodes[n]); } @@ -5751,6 +6108,11 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BKE_pbvh_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && ss->cache->first_time && + ss->cache->mirror_symmetry_pass == 0) { + sculpt_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + } + if (sculpt_brush_needs_normal(ss, brush)) { update_sculpt_normal(sd, ob, nodes, totnode); } @@ -5859,6 +6221,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe case SCULPT_TOOL_CLOTH: SCULPT_do_cloth_brush(sd, ob, nodes, totnode); break; + case SCULPT_TOOL_DRAW_FACE_SETS: + do_draw_face_sets_brush(sd, ob, nodes, totnode); + break; } if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && @@ -6383,6 +6748,8 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Slide/Relax Brush"; case SCULPT_TOOL_CLOTH: return "Cloth Brush"; + case SCULPT_TOOL_DRAW_FACE_SETS: + return "Draw Face Sets"; } return "Sculpting"; @@ -6923,7 +7290,8 @@ static bool sculpt_needs_connectivity_info(const Brush *brush, SculptSession *ss ((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_DRAW_FACE_SETS)); } static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) @@ -8931,6 +9299,9 @@ static void sculpt_filter_cache_free(SculptSession *ss) if (ss->filter_cache->normal_factor) { MEM_freeN(ss->filter_cache->normal_factor); } + if (ss->filter_cache->prev_face_set) { + MEM_freeN(ss->filter_cache->prev_face_set); + } MEM_freeN(ss->filter_cache); ss->filter_cache = NULL; } @@ -8998,12 +9369,19 @@ static void mesh_filter_task_cb(void *__restrict userdata, continue; } + if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) { + if (!sculpt_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) { + continue; + } + } + if (filter_type == MESH_FILTER_RELAX) { copy_v3_v3(orig_co, vd.co); } else { copy_v3_v3(orig_co, orig_data.co); } + switch (filter_type) { case MESH_FILTER_SMOOTH: CLAMP(fade, -1.0f, 1.0f); @@ -9144,7 +9522,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_RUNNING_MODAL; } -static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); @@ -9158,6 +9536,15 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } + if (RNA_boolean_get(op->ptr, "use_face_sets")) { + /* Update the active vertex */ + float mouse[2]; + SculptCursorGeometryInfo sgi; + mouse[0] = event->mval[0]; + mouse[1] = event->mval[1]; + sculpt_cursor_geometry_info_update(C, &sgi, mouse, false); + } + sculpt_vertex_random_access_init(ss); bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type); @@ -9171,6 +9558,14 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent sculpt_filter_cache_init(ob, sd); + if (RNA_boolean_get(op->ptr, "use_face_sets")) { + ss->filter_cache->active_face_set = sculpt_vertex_face_set_get(ss, + sculpt_active_vertex_get(ss)); + } + else { + ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE; + } + ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X; ss->filter_cache->enabled_axis[1] = deform_axis & MESH_FILTER_DEFORM_Y; ss->filter_cache->enabled_axis[2] = deform_axis & MESH_FILTER_DEFORM_Z; @@ -9208,6 +9603,11 @@ static void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z, "Deform axis", "Apply the deformation in the selected axis"); + ot->prop = RNA_def_boolean(ot->srna, + "use_face_sets", + false, + "Use Face Sets", + "Apply the filter only to the Face Mask under the cursor"); } typedef enum eSculptMaskFilterTypes { @@ -9617,7 +10017,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); - BKE_pbvh_update_vertex_data(pbvh, SCULPT_UPDATE_MASK); + BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); sculpt_undo_push_end(); @@ -9705,20 +10105,29 @@ static void sculpt_expand_task_cb(void *__restrict userdata, } } - if (data->mask_expand_keep_prev_mask) { - final_mask = MAX2(ss->filter_cache->prev_mask[vd.index], final_mask); + if (data->mask_expand_create_face_set) { + if (final_mask == 1.0f) { + sculpt_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set); + } + BKE_pbvh_node_mark_redraw(node); } + else { - if (data->mask_expand_invert_mask) { - final_mask = 1.0f - final_mask; - } + if (data->mask_expand_keep_prev_mask) { + final_mask = MAX2(ss->filter_cache->prev_mask[vd.index], final_mask); + } - if (*vd.mask != final_mask) { - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + if (data->mask_expand_invert_mask) { + final_mask = 1.0f - final_mask; + } + + if (*vd.mask != final_mask) { + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + *vd.mask = final_mask; + BKE_pbvh_node_mark_update_mask(node); } - *vd.mask = final_mask; - BKE_pbvh_node_mark_update_mask(node); } } BKE_pbvh_vertex_iter_end; @@ -9730,6 +10139,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ARegion *ar = CTX_wm_region(C); float prevclick_f[2]; copy_v2_v2(prevclick_f, op->customdata); int prevclick[2] = {(int)prevclick_f[0], (int)prevclick_f[1]}; @@ -9739,6 +10149,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * int mask_expand_update_it = len / mask_speed; mask_expand_update_it = mask_expand_update_it + 1; + const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set"); + if (RNA_boolean_get(op->ptr, "use_cursor")) { SculptCursorGeometryInfo sgi; float mouse[2]; @@ -9823,15 +10235,28 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_FINISHED; } - if (event->type != MOUSEMOVE) { + /* When pressing Ctrl, expand directly to the max number of iterations. This allows to flood fill + * mask and face sets by connectivity directly. */ + if (event->ctrl) { + mask_expand_update_it = ss->filter_cache->mask_update_last_it - 1; + } + + if (!ELEM(event->type, MOUSEMOVE, LEFTCTRLKEY, RIGHTCTRLKEY)) { return OPERATOR_RUNNING_MODAL; } if (mask_expand_update_it == ss->filter_cache->mask_update_current_it) { + ED_region_tag_redraw(ar); return OPERATOR_RUNNING_MODAL; } if (mask_expand_update_it < ss->filter_cache->mask_update_last_it) { + + if (create_face_set) { + for (int i = 0; i < ss->totpoly; i++) { + ss->face_sets[i] = ss->filter_cache->prev_face_set[i]; + } + } SculptThreadedTaskData data = { .sd = sd, .ob = ob, @@ -9840,6 +10265,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * .mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals"), .mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"), .mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"), + .mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"), }; PBVHParallelSettings settings; BKE_pbvh_parallel_range_settings( @@ -9903,7 +10329,8 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent Sculpt *sd = CTX_data_tool_settings(C)->sculpt; PBVH *pbvh = ob->sculpt->pbvh; - bool use_normals = RNA_boolean_get(op->ptr, "use_normals"); + const bool use_normals = RNA_boolean_get(op->ptr, "use_normals"); + const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set"); SculptCursorGeometryInfo sgi; float mouse[2]; @@ -9927,9 +10354,17 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent sculpt_undo_push_begin("Mask Expand"); - for (int i = 0; i < ss->filter_cache->totnode; i++) { - sculpt_undo_push_node(ob, ss->filter_cache->nodes[i], SCULPT_UNDO_MASK); - BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]); + if (create_face_set) { + sculpt_undo_push_node(ob, ss->filter_cache->nodes[0], SCULPT_UNDO_FACE_SETS); + for (int i = 0; i < ss->filter_cache->totnode; i++) { + BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]); + } + } + else { + for (int i = 0; i < ss->filter_cache->totnode; i++) { + sculpt_undo_push_node(ob, ss->filter_cache->nodes[i], SCULPT_UNDO_MASK); + BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]); + } } ss->filter_cache->mask_update_it = MEM_callocN(sizeof(int) * vertex_count, @@ -9944,9 +10379,18 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent } } - ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask"); - for (int i = 0; i < vertex_count; i++) { - ss->filter_cache->prev_mask[i] = sculpt_vertex_mask_get(ss, i); + if (create_face_set) { + ss->filter_cache->prev_face_set = MEM_callocN(sizeof(float) * ss->totpoly, "prev face mask"); + for (int i = 0; i < ss->totpoly; i++) { + ss->filter_cache->prev_face_set[i] = ss->face_sets[i]; + } + ss->filter_cache->new_face_set = sculpt_face_set_next_available_get(ss); + } + else { + ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask"); + for (int i = 0; i < vertex_count; i++) { + ss->filter_cache->prev_mask[i] = sculpt_vertex_mask_get(ss, i); + } } ss->filter_cache->mask_update_last_it = 1; @@ -9992,6 +10436,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent .mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals"), .mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"), .mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"), + .mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"), }; PBVHParallelSettings settings; BKE_pbvh_parallel_range_settings( @@ -10053,6 +10498,11 @@ static void SCULPT_OT_mask_expand(wmOperatorType *ot) "using normals to generate the mask", 0, 2000); + ot->prop = RNA_def_boolean(ot->srna, + "create_face_set", + false, + "Expand Face Mask", + "Expand a new Face Mask instead of the sculpt mask"); } void sculpt_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius) @@ -10437,6 +10887,356 @@ static void SCULPT_OT_set_pivot_position(wmOperatorType *ot) 10000.0f); } +typedef enum eSculptFaceGroupsCreateModes { + SCULPT_FACE_SET_MASKED = 0, + SCULPT_FACE_SET_VISIBLE = 1, + SCULPT_FACE_SET_ALL = 2, +} eSculptFaceGroupsCreateModes; + +static EnumPropertyItem prop_sculpt_face_set_create_types[] = { + { + SCULPT_FACE_SET_MASKED, + "MASKED", + 0, + "Face Mask From Masked", + "Create a new Face Mask from the masked faces", + }, + { + SCULPT_FACE_SET_VISIBLE, + "VISIBLE", + 0, + "Face Mask From Visible", + "Create a new Face Mask from the visible vertices", + }, + { + SCULPT_FACE_SET_ALL, + "ALL", + 0, + "Face Mask Full Mesh", + "Create an unique Face Mask with all faces in the sculpt", + }, + {0, NULL, 0, NULL, NULL}, +}; + +static int sculpt_face_set_create_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + ARegion *ar = CTX_wm_region(C); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + + const int mode = RNA_enum_get(op->ptr, "mode"); + + /* Dyntopo and Multires not supported for now. */ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return OPERATOR_CANCELLED; + } + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED); + + const int tot_vert = sculpt_vertex_count_get(ss); + float threshold = 0.5f; + + PBVH *pbvh = ob->sculpt->pbvh; + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + + if (!nodes) { + return OPERATOR_CANCELLED; + } + + sculpt_undo_push_begin("face mask change"); + sculpt_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + + const int next_face_set = sculpt_face_set_next_available_get(ss); + + if (mode == SCULPT_FACE_SET_MASKED) { + for (int i = 0; i < tot_vert; i++) { + if (sculpt_vertex_mask_get(ss, i) >= threshold) { + sculpt_vertex_face_set_set(ss, i, next_face_set); + } + } + } + + if (mode == SCULPT_FACE_SET_VISIBLE) { + for (int i = 0; i < tot_vert; i++) { + if (sculpt_vertex_visible_get(ss, i)) { + sculpt_vertex_face_set_set(ss, i, next_face_set); + } + } + } + + if (mode == SCULPT_FACE_SET_ALL) { + for (int i = 0; i < tot_vert; i++) { + sculpt_vertex_face_set_set(ss, i, next_face_set); + } + } + + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_redraw(nodes[i]); + } + + MEM_SAFE_FREE(nodes); + + sculpt_undo_push_end(); + + ED_region_tag_redraw(ar); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_face_sets_create(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Create Face Group"; + ot->idname = "SCULPT_OT_face_sets_create"; + ot->description = "Create a new Face Group"; + + /* api callbacks */ + ot->invoke = sculpt_face_set_create_invoke; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum( + ot->srna, "mode", prop_sculpt_face_set_create_types, SCULPT_FACE_SET_MASKED, "Mode", ""); +} + +typedef enum eSculptFaceGroupVisibilityModes { + SCULPT_FACE_SET_VISIBILITY_TOGGLE = 0, + SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE = 1, + SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE = 2, + SCULPT_FACE_SET_VISIBILITY_INVERT = 3, + SCULPT_FACE_SET_VISIBILITY_SHOW_ALL = 4, +} eSculptFaceGroupVisibilityModes; + +static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = { + { + SCULPT_FACE_SET_VISIBILITY_TOGGLE, + "TOGGLE", + 0, + "Toggle Visibility", + "Hide all Face Sets except for the active one", + }, + { + SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE, + "SHOW_ACTIVE", + 0, + "Show Active Face Mask", + "Show Active Face Mask", + }, + { + SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE, + "HIDE_ACTIVE", + 0, + "Hide Active Face Sets", + "Hide Active Face Sets", + }, + { + SCULPT_FACE_SET_VISIBILITY_INVERT, + "INVERT", + 0, + "Invert Face Mask Visibility", + "Invert Face Mask Visibility", + }, + { + SCULPT_FACE_SET_VISIBILITY_SHOW_ALL, + "SHOW_ALL", + 0, + "Show All Face Sets", + "Show All Face Sets", + }, + {0, NULL, 0, NULL, NULL}, +}; + +static int sculpt_face_sets_change_visibility_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + ARegion *ar = CTX_wm_region(C); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + + /* Dyntopo and Multires not supported for now. */ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return OPERATOR_CANCELLED; + } + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true); + + const int tot_vert = sculpt_vertex_count_get(ss); + const int mode = RNA_enum_get(op->ptr, "mode"); + int active_vertex_index = sculpt_active_vertex_get(ss); + int active_face_set = sculpt_vertex_face_set_get(ss, active_vertex_index); + + sculpt_undo_push_begin("Hide area"); + + PBVH *pbvh = ob->sculpt->pbvh; + PBVHNode **nodes; + int totnode; + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + + if (totnode == 0) { + MEM_SAFE_FREE(nodes); + return OPERATOR_CANCELLED; + } + + sculpt_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + + if (mode == SCULPT_FACE_SET_VISIBILITY_TOGGLE) { + bool hidden_vertex = false; + for (int i = 0; i < tot_vert; i++) { + if (!sculpt_vertex_visible_get(ss, i)) { + hidden_vertex = true; + break; + } + } + + for (int i = 0; i < ss->totpoly; i++) { + if (ss->face_sets[i] < 0) { + hidden_vertex = true; + break; + } + } + if (hidden_vertex) { + sculpt_face_sets_visibility_all_set(ss, true); + } + else { + sculpt_face_sets_visibility_all_set(ss, false); + sculpt_face_set_visibility_set(ss, active_face_set, true); + } + } + + if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) { + sculpt_face_sets_visibility_all_set(ss, true); + } + + if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) { + sculpt_face_sets_visibility_all_set(ss, false); + sculpt_face_set_visibility_set(ss, active_face_set, true); + for (int i = 0; i < tot_vert; i++) { + sculpt_vertex_visible_set(ss, + i, + sculpt_vertex_visible_get(ss, i) && + sculpt_vertex_has_face_set(ss, i, active_face_set)); + } + } + + if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) { + sculpt_face_set_visibility_set(ss, active_face_set, false); + } + + if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) { + sculpt_face_sets_visibility_invert(ss); + } + + /* Sync face mask visibility and vertex visibility. */ + sculpt_visibility_sync_all_face_sets_to_vertices(ss); + + sculpt_undo_push_end(); + + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_update_visibility(nodes[i]); + } + + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); + + MEM_SAFE_FREE(nodes); + + if (BKE_pbvh_type(pbvh) == PBVH_FACES) { + BKE_mesh_flush_hidden_from_verts(ob->data); + } + + ED_region_tag_redraw(ar); + + View3D *v3d = CTX_wm_view3d(C); + if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) { + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Face Mask Visibility"; + ot->idname = "SCULPT_OT_face_set_change_visibility"; + ot->description = "Change the visibility of the Face Sets of the sculpt"; + + /* Api callbacks. */ + ot->invoke = sculpt_face_sets_change_visibility_invoke; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, + "mode", + prop_sculpt_face_sets_change_visibility_types, + SCULPT_FACE_SET_VISIBILITY_TOGGLE, + "Mode", + ""); +} + +static int sculpt_face_sets_randomize_colors_invoke(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(event)) +{ + + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + ARegion *ar = CTX_wm_region(C); + + /* Dyntopo and Multires not supported for now. */ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return OPERATOR_CANCELLED; + } + + PBVH *pbvh = ob->sculpt->pbvh; + PBVHNode **nodes; + int totnode; + Mesh *mesh = ob->data; + + int new_seed = BLI_hash_int(PIL_check_seconds_timer_i() & UINT_MAX); + mesh->face_sets_color_seed = new_seed; + BKE_pbvh_face_sets_color_seed_set(pbvh, new_seed); + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_redraw(nodes[i]); + } + + MEM_SAFE_FREE(nodes); + + View3D *v3d = CTX_wm_view3d(C); + if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) { + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + } + + ED_region_tag_redraw(ar); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Randomize Face Sets Colors"; + ot->idname = "SCULPT_OT_face_sets_randomize_colors"; + ot->description = "Generates a new set of random colors to render the Face Sets in the viewport"; + + /* Api callbacks. */ + ot->invoke = sculpt_face_sets_randomize_colors_invoke; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + void ED_operatortypes_sculpt(void) { WM_operatortype_append(SCULPT_OT_brush_stroke); @@ -10453,4 +11253,7 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_dirty_mask); WM_operatortype_append(SCULPT_OT_mask_expand); WM_operatortype_append(SCULPT_OT_set_pivot_position); + WM_operatortype_append(SCULPT_OT_face_sets_create); + WM_operatortype_append(SCULPT_OT_face_sets_change_visibility); + WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 91656cb2ad5..a935e32cc08 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -54,6 +54,7 @@ bool sculpt_poll_view3d(struct bContext *C); typedef enum SculptUpdateType { SCULPT_UPDATE_COORDS = 1 << 0, SCULPT_UPDATE_MASK = 1 << 1, + SCULPT_UPDATE_VISIBILITY = 1 << 2, } SculptUpdateType; /* Stroke */ @@ -236,9 +237,17 @@ struct SculptPoseIKChain *SCULPT_pose_ik_chain_init(struct Sculpt *sd, struct Brush *br, const float initial_location[3], const float radius); - void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain); +/* Sculpt Visibility API */ +void sculpt_visibility_sync_all_face_sets_to_vertices(struct SculptSession *ss); +void sculpt_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); + +/* Dynamic topology */ +void sculpt_pbvh_clear(Object *ob); +void sculpt_dyntopo_node_layers_add(struct SculptSession *ss); +void sculpt_dynamic_topology_disable(bContext *C, struct SculptUndoNode *unode); + /* Undo */ typedef enum { @@ -249,6 +258,7 @@ typedef enum { SCULPT_UNDO_DYNTOPO_END, SCULPT_UNDO_DYNTOPO_SYMMETRIZE, SCULPT_UNDO_GEOMETRY, + SCULPT_UNDO_FACE_SETS, } SculptUndoType; typedef struct SculptUndoNode { @@ -298,6 +308,9 @@ typedef struct SculptUndoNode { float pivot_pos[3]; float pivot_rot[4]; + /* Sculpt Face Sets */ + int *face_sets; + size_t undo_size; } SculptUndoNode; @@ -386,6 +399,7 @@ typedef struct SculptThreadedTaskData { bool mask_expand_invert_mask; bool mask_expand_use_normals; bool mask_expand_keep_prev_mask; + bool mask_expand_create_face_set; float transform_mats[8][4][4]; @@ -395,6 +409,8 @@ typedef struct SculptThreadedTaskData { float dirty_mask_max; bool dirty_mask_dirty_only; + int face_set; + ThreadMutex mutex; } SculptThreadedTaskData; @@ -528,6 +544,9 @@ typedef struct StrokeCache { bool is_rake_rotation_valid; struct SculptRakeData rake_data; + /* Face Sets */ + int paint_face_set; + /* Symmetry index between 0 and 7 bit combo 0 is Brush only; * 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ int symmetry; @@ -612,6 +631,11 @@ typedef struct FilterCache { float *edge_factor; float *prev_mask; float mask_expand_initial_co[3]; + + int new_face_set; + int *prev_face_set; + + int active_face_set; } FilterCache; void sculpt_cache_calc_brushdata_symm(StrokeCache *cache, diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 044d5b3c0b0..d1896f47c52 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -79,8 +79,7 @@ static void update_cb(PBVHNode *node, void *rebuild) BKE_pbvh_node_mark_update(node); BKE_pbvh_node_mark_update_mask(node); if (*((bool *)rebuild)) { - BKE_pbvh_node_mark_rebuild_draw(node); - BKE_pbvh_node_mark_visibility_update(node); + BKE_pbvh_node_mark_update_visibility(node); } BKE_pbvh_node_fully_hidden_set(node, 0); } @@ -332,6 +331,15 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) return true; } +static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + SculptSession *ss = ob->sculpt; + memcpy(ss->face_sets, unode->face_sets, ss->totpoly * sizeof(int)); + return false; +} + static void sculpt_undo_bmesh_restore_generic_task_cb( void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) { @@ -533,6 +541,30 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask); return; } + else if (unode->type == SCULPT_UNDO_FACE_SETS) { + + sculpt_undo_restore_face_sets(C, unode); + + rebuild = true; + BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask); + + sculpt_visibility_sync_all_face_sets_to_vertices(ss); + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); + + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + BKE_mesh_flush_hidden_from_verts(ob->data); + } + + if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) { + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + + unode->applied = true; + return; + } } BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask); @@ -584,6 +616,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase update_mask = true; } break; + case SCULPT_UNDO_FACE_SETS: + break; case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: @@ -619,10 +653,16 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase }; BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); + if (update_mask) { BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); } + if (update_visibility) { + sculpt_visibility_sync_all_vertex_to_face_sets(ss); + BKE_pbvh_update_visibility(ss->pbvh); + } + if (BKE_sculpt_multires_active(scene, ob)) { if (rebuild) { multires_mark_as_modified(depsgraph, ob, MULTIRES_HIDDEN_MODIFIED); @@ -642,10 +682,6 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase BKE_sculptsession_free_deformMats(ss); } - if (update_visibility) { - BKE_pbvh_update_visibility(ss->pbvh); - } - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && update_visibility) { Mesh *mesh = ob->data; BKE_mesh_flush_hidden_from_verts(mesh); @@ -714,6 +750,10 @@ static void sculpt_undo_free_list(ListBase *lb) CustomData_free(&unode->geom_pdata, unode->geom_totpoly); } + if (unode->face_sets) { + MEM_freeN(unode->face_sets); + } + MEM_freeN(unode); unode = unode_next; @@ -828,6 +868,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: BLI_assert(!"Dynamic topology should've already been handled"); case SCULPT_UNDO_GEOMETRY: + case SCULPT_UNDO_FACE_SETS: break; } @@ -939,6 +980,27 @@ static SculptUndoNode *sculpt_undo_geometry_push(Object *ob, SculptUndoType type return unode; } +static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType type) +{ + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + SculptSession *ss = ob->sculpt; + + SculptUndoNode *unode = usculpt->nodes.first; + + unode = MEM_callocN(sizeof(*unode), __func__); + + BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); + unode->type = type; + unode->applied = true; + + unode->face_sets = MEM_callocN(ss->totpoly * sizeof(int), "sculpt face sets"); + memcpy(unode->face_sets, ss->face_sets, ss->totpoly * sizeof(int)); + + BLI_addtail(&usculpt->nodes, unode); + + return unode; +} + static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); @@ -1022,6 +1084,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: case SCULPT_UNDO_GEOMETRY: + case SCULPT_UNDO_FACE_SETS: break; } } @@ -1051,6 +1114,11 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType BLI_thread_unlock(LOCK_CUSTOM1); return unode; } + else if (type == SCULPT_UNDO_FACE_SETS) { + unode = sculpt_undo_face_sets_push(ob, type); + BLI_thread_unlock(LOCK_CUSTOM1); + return unode; + } else if ((unode = sculpt_undo_get_node(node))) { BLI_thread_unlock(LOCK_CUSTOM1); return unode; @@ -1091,6 +1159,7 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: BLI_assert(!"Dynamic topology should've already been handled"); case SCULPT_UNDO_GEOMETRY: + case SCULPT_UNDO_FACE_SETS: break; } |