1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2020 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edsculpt
22 */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BLI_blenlib.h"
27 #include "BLI_hash.h"
28 #include "BLI_math.h"
29 #include "BLI_math_color_blend.h"
30 #include "BLI_task.h"
31
32 #include "DNA_mesh_types.h"
33 #include "DNA_meshdata_types.h"
34
35 #include "BKE_brush.h"
36 #include "BKE_colortools.h"
37 #include "BKE_context.h"
38 #include "BKE_mesh.h"
39 #include "BKE_mesh_mapping.h"
40 #include "BKE_object.h"
41 #include "BKE_paint.h"
42 #include "BKE_pbvh.h"
43 #include "BKE_scene.h"
44
45 #include "IMB_colormanagement.h"
46
47 #include "DEG_depsgraph.h"
48
49 #include "WM_api.h"
50 #include "WM_message.h"
51 #include "WM_toolsystem.h"
52 #include "WM_types.h"
53
54 #include "ED_object.h"
55 #include "ED_screen.h"
56 #include "ED_sculpt.h"
57 #include "paint_intern.h"
58 #include "sculpt_intern.h"
59
60 #include "RNA_access.h"
61 #include "RNA_define.h"
62
63 #include "UI_interface.h"
64
65 #include "bmesh.h"
66
67 #include <math.h>
68 #include <stdlib.h>
69
70 typedef enum eSculptColorFilterTypes {
71 COLOR_FILTER_FILL,
72 COLOR_FILTER_HUE,
73 COLOR_FILTER_SATURATION,
74 COLOR_FILTER_VALUE,
75 COLOR_FILTER_BRIGHTNESS,
76 COLOR_FILTER_CONTRAST,
77 COLOR_FILTER_RED,
78 COLOR_FILTER_GREEN,
79 COLOR_FILTER_BLUE,
80 COLOR_FILTER_SMOOTH,
81 } eSculptColorFilterTypes;
82
83 static EnumPropertyItem prop_color_filter_types[] = {
84 {COLOR_FILTER_FILL, "FILL", 0, "Fill", "Fill with a specific color"},
85 {COLOR_FILTER_HUE, "HUE", 0, "Hue", "Change hue"},
86 {COLOR_FILTER_SATURATION, "SATURATION", 0, "Saturation", "Change saturation"},
87 {COLOR_FILTER_VALUE, "VALUE", 0, "Value", "Change value"},
88
89 {COLOR_FILTER_BRIGHTNESS, "BRIGTHNESS", 0, "Brightness", "Change brightness"},
90 {COLOR_FILTER_CONTRAST, "CONTRAST", 0, "Contrast", "Change contrast"},
91
92 {COLOR_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth colors"},
93
94 {COLOR_FILTER_RED, "RED", 0, "Red", "Change red channel"},
95 {COLOR_FILTER_GREEN, "GREEN", 0, "Green", "Change green channel"},
96 {COLOR_FILTER_BLUE, "BLUE", 0, "Blue", "Change blue channel"},
97 {0, NULL, 0, NULL, NULL},
98 };
99
color_filter_task_cb(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict UNUSED (tls))100 static void color_filter_task_cb(void *__restrict userdata,
101 const int n,
102 const TaskParallelTLS *__restrict UNUSED(tls))
103 {
104 SculptThreadedTaskData *data = userdata;
105 SculptSession *ss = data->ob->sculpt;
106
107 const int mode = data->filter_type;
108
109 SculptOrigVertData orig_data;
110 SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
111
112 PBVHVertexIter vd;
113 BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
114 {
115 SCULPT_orig_vert_data_update(&orig_data, &vd);
116 float orig_color[3], final_color[4], hsv_color[3];
117 int hue;
118 float brightness, contrast, gain, delta, offset;
119 float fade = vd.mask ? *vd.mask : 0.0f;
120 fade = 1.0f - fade;
121 fade *= data->filter_strength;
122 fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
123 if (fade == 0.0f) {
124 continue;
125 }
126
127 copy_v3_v3(orig_color, orig_data.col);
128
129 switch (mode) {
130 case COLOR_FILTER_FILL: {
131 float fill_color_rgba[4];
132 copy_v3_v3(fill_color_rgba, data->filter_fill_color);
133 fill_color_rgba[3] = 1.0f;
134 fade = clamp_f(fade, 0.0f, 1.0f);
135 mul_v4_fl(fill_color_rgba, fade);
136 blend_color_mix_float(final_color, orig_data.col, fill_color_rgba);
137 break;
138 }
139 case COLOR_FILTER_HUE:
140 rgb_to_hsv_v(orig_color, hsv_color);
141 hue = hsv_color[0] + fade;
142 hsv_color[0] = fabs((hsv_color[0] + fade) - hue);
143 hsv_to_rgb_v(hsv_color, final_color);
144 break;
145 case COLOR_FILTER_SATURATION:
146 rgb_to_hsv_v(orig_color, hsv_color);
147 hsv_color[1] = clamp_f(hsv_color[1] + fade, 0.0f, 1.0f);
148 hsv_to_rgb_v(hsv_color, final_color);
149 break;
150 case COLOR_FILTER_VALUE:
151 rgb_to_hsv_v(orig_color, hsv_color);
152 hsv_color[2] = clamp_f(hsv_color[2] + fade, 0.0f, 1.0f);
153 hsv_to_rgb_v(hsv_color, final_color);
154 break;
155 case COLOR_FILTER_RED:
156 orig_color[0] = clamp_f(orig_color[0] + fade, 0.0f, 1.0f);
157 copy_v3_v3(final_color, orig_color);
158 break;
159 case COLOR_FILTER_GREEN:
160 orig_color[1] = clamp_f(orig_color[1] + fade, 0.0f, 1.0f);
161 copy_v3_v3(final_color, orig_color);
162 break;
163 case COLOR_FILTER_BLUE:
164 orig_color[2] = clamp_f(orig_color[2] + fade, 0.0f, 1.0f);
165 copy_v3_v3(final_color, orig_color);
166 break;
167 case COLOR_FILTER_BRIGHTNESS:
168 fade = clamp_f(fade, -1.0f, 1.0f);
169 brightness = fade;
170 contrast = 0;
171 delta = contrast / 2.0f;
172 gain = 1.0f - delta * 2.0f;
173 delta *= -1;
174 offset = gain * (brightness + delta);
175 for (int i = 0; i < 3; i++) {
176 final_color[i] = clamp_f(gain * orig_color[i] + offset, 0.0f, 1.0f);
177 }
178 break;
179 case COLOR_FILTER_CONTRAST:
180 fade = clamp_f(fade, -1.0f, 1.0f);
181 brightness = 0;
182 contrast = fade;
183 delta = contrast / 2.0f;
184 gain = 1.0f - delta * 2.0f;
185 if (contrast > 0) {
186 gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
187 offset = gain * (brightness - delta);
188 }
189 else {
190 delta *= -1;
191 offset = gain * (brightness + delta);
192 }
193 for (int i = 0; i < 3; i++) {
194 final_color[i] = clamp_f(gain * orig_color[i] + offset, 0.0f, 1.0f);
195 }
196 break;
197 case COLOR_FILTER_SMOOTH: {
198 fade = clamp_f(fade, -1.0f, 1.0f);
199 float smooth_color[4];
200 SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
201 blend_color_interpolate_float(final_color, vd.col, smooth_color, fade);
202 break;
203 }
204 }
205
206 copy_v3_v3(vd.col, final_color);
207
208 if (vd.mvert) {
209 vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
210 }
211 }
212 BKE_pbvh_vertex_iter_end;
213 BKE_pbvh_node_mark_update_color(data->nodes[n]);
214 }
215
sculpt_color_filter_modal(bContext * C,wmOperator * op,const wmEvent * event)216 static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
217 {
218 Object *ob = CTX_data_active_object(C);
219 SculptSession *ss = ob->sculpt;
220 Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
221 const int mode = RNA_enum_get(op->ptr, "type");
222 float filter_strength = RNA_float_get(op->ptr, "strength");
223
224 if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
225 SCULPT_undo_push_end();
226 SCULPT_filter_cache_free(ss);
227 SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR);
228 return OPERATOR_FINISHED;
229 }
230
231 if (event->type != MOUSEMOVE) {
232 return OPERATOR_RUNNING_MODAL;
233 }
234
235 const float len = event->prevclickx - event->x;
236 filter_strength = filter_strength * -len * 0.001f;
237
238 float fill_color[3];
239 RNA_float_get_array(op->ptr, "fill_color", fill_color);
240 IMB_colormanagement_srgb_to_scene_linear_v3(fill_color);
241
242 SculptThreadedTaskData data = {
243 .sd = sd,
244 .ob = ob,
245 .nodes = ss->filter_cache->nodes,
246 .filter_type = mode,
247 .filter_strength = filter_strength,
248 .filter_fill_color = fill_color,
249 };
250
251 TaskParallelSettings settings;
252 BLI_parallel_range_settings_defaults(&settings);
253
254 BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
255 BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, color_filter_task_cb, &settings);
256
257 SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
258
259 return OPERATOR_RUNNING_MODAL;
260 }
261
sculpt_color_filter_invoke(bContext * C,wmOperator * op,const wmEvent * event)262 static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event)
263 {
264 Object *ob = CTX_data_active_object(C);
265 Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
266 SculptSession *ss = ob->sculpt;
267 int mode = RNA_enum_get(op->ptr, "type");
268 PBVH *pbvh = ob->sculpt->pbvh;
269
270 const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL);
271 if (use_automasking) {
272 /* Update the active face set manually as the paint cursor is not enabled when using the Mesh
273 * Filter Tool. */
274 float mouse[2];
275 SculptCursorGeometryInfo sgi;
276 mouse[0] = event->mval[0];
277 mouse[1] = event->mval[1];
278 SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
279 }
280
281 /* Disable for multires and dyntopo for now */
282 if (!ss->pbvh) {
283 return OPERATOR_CANCELLED;
284 }
285 if (BKE_pbvh_type(pbvh) != PBVH_FACES) {
286 return OPERATOR_CANCELLED;
287 }
288
289 if (!ss->vcol) {
290 return OPERATOR_CANCELLED;
291 }
292
293 SCULPT_undo_push_begin("color filter");
294
295 BKE_sculpt_color_layer_create_if_needed(ob);
296
297 /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
298 * earlier steps modifying the data. */
299 Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
300 const bool needs_topology_info = mode == COLOR_FILTER_SMOOTH || use_automasking;
301 BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, true);
302
303 if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) {
304 return OPERATOR_CANCELLED;
305 }
306
307 SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COLOR);
308 FilterCache *filter_cache = ss->filter_cache;
309 filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
310 filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
311
312 WM_event_add_modal_handler(C, op);
313 return OPERATOR_RUNNING_MODAL;
314 }
315
SCULPT_OT_color_filter(struct wmOperatorType * ot)316 void SCULPT_OT_color_filter(struct wmOperatorType *ot)
317 {
318 /* identifiers */
319 ot->name = "Filter Color";
320 ot->idname = "SCULPT_OT_color_filter";
321 ot->description = "Applies a filter to modify the current sculpt vertex colors";
322
323 /* api callbacks */
324 ot->invoke = sculpt_color_filter_invoke;
325 ot->modal = sculpt_color_filter_modal;
326 ot->poll = SCULPT_vertex_colors_poll;
327
328 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
329
330 /* rna */
331 RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", "");
332 RNA_def_float(
333 ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
334
335 PropertyRNA *prop = RNA_def_float_color(
336 ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "", 0.0f, 1.0f);
337 RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
338 }
339