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:
authorPablo Dobarro <pablodp606@gmail.com>2020-07-01 20:19:30 +0300
committerPablo Dobarro <pablodp606@gmail.com>2020-07-02 19:20:47 +0300
commitc5ec8d91bd125f8ed1d0a964e36e601da993f5d2 (patch)
tree2177303ecaf3e347f5849f7a850fa92ef60ee0c6 /source/blender/editors/sculpt_paint/sculpt.c
parent85980743b058e287f1d6400a64dcc60f87fad000 (diff)
Sculpt: Mask By Color
This tool generates masks based on the sculpt vertex colors by clicking on the model, similar to automatic selection tools in image editing software. Reviewed By: sergey Differential Revision: https://developer.blender.org/D8157
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt.c')
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 929f466f808..2873f2003cf 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -8466,6 +8466,306 @@ void SCULPT_fake_neighbors_free(Object *ob)
sculpt_pose_fake_neighbors_free(ss);
}
+/* sculpt_mask_by_color_delta_get returns values in the (0,1) range that are used to generate the
+ * mask based on the diference between two colors (the active color and the color of any other
+ * vertex). Ideally, a threshold of 0 should mask only the colors that are equal to the active
+ * color and threshold of 1 should mask all colors. In order to avoid artifacts and produce softer
+ * falloffs in the mask, the MASK_BY_COLOR_SLOPE defines the size of the transition values between
+ * masked and unmasked vertices. The smaller this value is, the sharper the generated mask is going
+ * to be. */
+#define MASK_BY_COLOR_SLOPE 0.25f
+
+static float sculpt_mask_by_color_delta_get(const float *color_a,
+ const float *color_b,
+ const float threshold,
+ const bool invert)
+{
+ float len = len_v3v3(color_a, color_b);
+ /* Normalize len to the (0, 1) range. */
+ len = len / M_SQRT3;
+
+ if (len < threshold - MASK_BY_COLOR_SLOPE) {
+ len = 1.0f;
+ }
+ else if (len >= threshold) {
+ len = 0.0f;
+ }
+ else {
+ len = (-len + threshold) / MASK_BY_COLOR_SLOPE;
+ }
+
+ if (invert) {
+ return 1.0f - len;
+ }
+ return len;
+}
+
+static float sculpt_mask_by_color_final_mask_get(const float current_mask,
+ const float new_mask,
+ const bool invert,
+ const bool preserve_mask)
+{
+ if (preserve_mask) {
+ if (invert) {
+ return min_ff(current_mask, new_mask);
+ }
+ return max_ff(current_mask, new_mask);
+ }
+ return new_mask;
+}
+
+typedef struct MaskByColorContiguousFloodFillData {
+ float threshold;
+ bool invert;
+ float *new_mask;
+ float initial_color[3];
+} MaskByColorContiguousFloodFillData;
+
+static void do_mask_by_color_contiguous_update_nodes_cb(
+ void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
+ bool update_node = false;
+
+ const bool invert = data->mask_by_color_invert;
+ const bool preserve_mask = data->mask_by_color_preserve_mask;
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ const float current_mask = *vd.mask;
+ const float new_mask = data->mask_by_color_floodfill[vd.index];
+ *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
+ if (current_mask != *vd.mask) {
+ update_node = true;
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+ if (update_node) {
+ BKE_pbvh_node_mark_redraw(data->nodes[n]);
+ }
+}
+
+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 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;
+
+ if (is_duplicate) {
+ data->new_mask[to_v] = data->new_mask[from_v];
+ }
+
+ float len = len_v3v3(current_color, data->initial_color);
+ len = len / M_SQRT3;
+ return len <= data->threshold;
+}
+
+static void sculpt_mask_by_color_contiguous(Object *object,
+ const int vertex,
+ const float threshold,
+ const bool invert,
+ const bool preserve_mask)
+{
+ SculptSession *ss = object->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ float *new_mask = MEM_calloc_arrayN(totvert, sizeof(float), "new mask");
+
+ if (invert) {
+ for (int i = 0; i < totvert; i++) {
+ new_mask[i] = 1.0f;
+ }
+ }
+
+ SculptFloodFill flood;
+ SCULPT_floodfill_init(ss, &flood);
+ SCULPT_floodfill_add_initial(&flood, vertex);
+
+ MaskByColorContiguousFloodFillData ffd;
+ ffd.threshold = threshold;
+ ffd.invert = invert;
+ ffd.new_mask = new_mask;
+ copy_v3_v3(ffd.initial_color, SCULPT_vertex_color_get(ss, vertex));
+
+ SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd);
+ SCULPT_floodfill_free(&flood);
+
+ int totnode;
+ PBVHNode **nodes;
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ SculptThreadedTaskData data = {
+ .ob = object,
+ .nodes = nodes,
+ .mask_by_color_floodfill = new_mask,
+ .mask_by_color_vertex = vertex,
+ .mask_by_color_threshold = threshold,
+ .mask_by_color_invert = invert,
+ .mask_by_color_preserve_mask = preserve_mask,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_mask_by_color_contiguous_update_nodes_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+
+ MEM_freeN(new_mask);
+}
+
+static void do_mask_by_color_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
+ bool update_node = false;
+
+ 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);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ const float current_mask = *vd.mask;
+ const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert);
+ *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
+
+ if (current_mask != *vd.mask) {
+ update_node = true;
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+ if (update_node) {
+ BKE_pbvh_node_mark_redraw(data->nodes[n]);
+ }
+}
+
+static void sculpt_mask_by_color_full_mesh(Object *object,
+ const int vertex,
+ const float threshold,
+ const bool invert,
+ const bool preserve_mask)
+{
+ SculptSession *ss = object->sculpt;
+
+ int totnode;
+ PBVHNode **nodes;
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ SculptThreadedTaskData data = {
+ .ob = object,
+ .nodes = nodes,
+ .mask_by_color_vertex = vertex,
+ .mask_by_color_threshold = threshold,
+ .mask_by_color_invert = invert,
+ .mask_by_color_preserve_mask = preserve_mask,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_mask_by_color_task_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+}
+
+static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
+
+ if (!ss->vcol) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_vertex_random_access_init(ss);
+
+ /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
+ * so it needs to be updated here. */
+ SculptCursorGeometryInfo sgi;
+ float mouse[2];
+ mouse[0] = event->mval[0];
+ mouse[1] = event->mval[1];
+ SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+
+ SCULPT_undo_push_begin("Mask by color");
+
+ const int active_vertex = SCULPT_active_vertex_get(ss);
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const bool invert = RNA_boolean_get(op->ptr, "invert");
+ const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
+
+ if (RNA_boolean_get(op->ptr, "contiguous")) {
+ sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask);
+ }
+ else {
+ sculpt_mask_by_color_full_mesh(ob, active_vertex, threshold, invert, preserve_mask);
+ }
+
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
+ SCULPT_undo_push_end();
+
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Mask By Color";
+ ot->idname = "SCULPT_OT_mask_by_color";
+ ot->description = "Creates a mask based on the sculpt vertex colors";
+
+ /* api callbacks */
+ ot->invoke = sculpt_mask_by_color_invoke;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER;
+
+ ot->prop = RNA_def_boolean(
+ ot->srna, "contiguous", false, "Contiguous", "Mask only contiguos color areas");
+
+ ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask");
+ ot->prop = RNA_def_boolean(
+ ot->srna,
+ "preserve_previous_mask",
+ false,
+ "Preserve Previous Mask",
+ "Preserve the previous mask and add or substract the new one generated by the colors");
+
+ RNA_def_float(ot->srna,
+ "threshold",
+ 0.35f,
+ 0.0f,
+ 1.0f,
+ "Threshold",
+ "How much changes in color affect the mask generation",
+ 0.0f,
+ 1.0f);
+}
+
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
@@ -8492,4 +8792,5 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
WM_operatortype_append(SCULPT_OT_color_filter);
+ WM_operatortype_append(SCULPT_OT_mask_by_color);
}