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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c857
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h26
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c81
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;
}