Fix i18n messages extraction script, and a few more UI messages...
[blender.git] / source / blender / editors / sculpt_paint / paint_vertex_color_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/sculpt_paint/paint_vertex_color_ops.c
22  *  \ingroup edsculpt
23  */
24
25 #include "MEM_guardedalloc.h"
26
27 #include "DNA_mesh_types.h"
28 #include "DNA_meshdata_types.h"
29 #include "DNA_object_types.h"
30 #include "DNA_scene_types.h"
31
32 #include "BLI_math_base.h"
33 #include "BLI_math_color.h"
34
35 #include "BKE_context.h"
36 #include "BKE_depsgraph.h"
37 #include "BKE_mesh.h"
38 #include "BKE_deform.h"
39
40 #include "RNA_access.h"
41 #include "RNA_define.h"
42
43 #include "WM_api.h"
44 #include "WM_types.h"
45
46 #include "ED_mesh.h"
47
48 #include "paint_intern.h"  /* own include */
49
50
51 static int vertex_weight_paint_mode_poll(bContext *C)
52 {
53         Object *ob = CTX_data_active_object(C);
54         Mesh *me = BKE_mesh_from_object(ob);
55         return (ob && (ob->mode == OB_MODE_VERTEX_PAINT || ob->mode == OB_MODE_WEIGHT_PAINT)) &&
56                (me && me->totpoly && me->dvert);
57 }
58
59 /* -------------------------------------------------------------------- */
60 /** \name Set Vertex Colors Operator
61  * \{ */
62
63 static bool vertex_color_set(Object *ob, uint paintcol)
64 {
65         Mesh *me;
66         const MPoly *mp;
67         int i, j;
68
69         if (((me = BKE_mesh_from_object(ob)) == NULL) ||
70             (ED_mesh_color_ensure(me, NULL) == false))
71         {
72                 return false;
73         }
74
75         const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
76         const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
77
78         mp = me->mpoly;
79         for (i = 0; i < me->totpoly; i++, mp++) {
80                 MLoopCol *lcol = me->mloopcol + mp->loopstart;
81
82                 if (use_face_sel && !(mp->flag & ME_FACE_SEL))
83                         continue;
84
85                 j = 0;
86                 do {
87                         uint vidx = me->mloop[mp->loopstart + j].v;
88                         if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) {
89                                 *(int *)lcol = paintcol;
90                         }
91                         lcol++;
92                         j++;
93                 } while (j < mp->totloop);
94
95         }
96
97         /* remove stale me->mcol, will be added later */
98         BKE_mesh_tessface_clear(me);
99
100         DAG_id_tag_update(&me->id, 0);
101
102         return true;
103 }
104
105 static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op))
106 {
107         Scene *scene = CTX_data_scene(C);
108         Object *obact = CTX_data_active_object(C);
109         unsigned int paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint);
110
111         if (vertex_color_set(obact, paintcol)) {
112                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
113                 return OPERATOR_FINISHED;
114         }
115         else {
116                 return OPERATOR_CANCELLED;
117         }
118 }
119
120 void PAINT_OT_vertex_color_set(wmOperatorType *ot)
121 {
122         /* identifiers */
123         ot->name = "Set Vertex Colors";
124         ot->idname = "PAINT_OT_vertex_color_set";
125         ot->description = "Fill the active vertex color layer with the current paint color";
126
127         /* api callbacks */
128         ot->exec = vertex_color_set_exec;
129         ot->poll = vertex_paint_mode_poll;
130
131         /* flags */
132         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
133 }
134
135 /** \} */
136
137 /* -------------------------------------------------------------------- */
138 /** \name Vertex Color from Weight Operator
139  * \{ */
140
141 static bool vertex_paint_from_weight(Object *ob)
142 {
143         Mesh *me;
144         const MPoly *mp;
145         int vgroup_active;
146
147         if (((me = BKE_mesh_from_object(ob)) == NULL ||
148             (ED_mesh_color_ensure(me, NULL)) == false))
149         {
150                 return false;
151         }
152
153         /* TODO: respect selection. */
154         mp = me->mpoly;
155         vgroup_active = ob->actdef - 1;
156         for (int i = 0; i < me->totpoly; i++, mp++) {
157                 MLoopCol *lcol = &me->mloopcol[mp->loopstart];
158                 uint j = 0;
159                 do {
160                         uint vidx = me->mloop[mp->loopstart + j].v;
161                         const float weight = defvert_find_weight(&me->dvert[vidx], vgroup_active);
162                         const uchar grayscale = weight * 255;
163                         lcol->r = grayscale;
164                         lcol->b = grayscale;
165                         lcol->g = grayscale;
166                         lcol++;
167                         j++;
168                 } while (j < mp->totloop);
169         }
170
171         DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
172
173         return true;
174 }
175
176 static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op))
177 {
178         Object *obact = CTX_data_active_object(C);
179         if (vertex_paint_from_weight(obact)) {
180                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
181                 return OPERATOR_FINISHED;
182         }
183         else {
184                 return OPERATOR_CANCELLED;
185         }
186 }
187
188 void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot)
189 {
190         /* identifiers */
191         ot->name = "Vertex Color from Weight";
192         ot->idname = "PAINT_OT_vertex_color_from_weight";
193         ot->description = "Convert active weight into grey scale vertex colors";
194
195         /* api callback */
196         ot->exec = vertex_paint_from_weight_exec;
197         ot->poll = vertex_weight_paint_mode_poll;
198
199         /* flags */
200         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
201
202         /* TODO: invert, alpha */
203 }
204
205 /** \} */
206
207 /* -------------------------------------------------------------------- */
208 /** \name Smooth Vertex Colors Operator
209  * \{ */
210
211 static void vertex_color_smooth_looptag(Mesh *me, bool *mlooptag)
212 {
213         const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
214         const MPoly *mp;
215         int (*scol)[4];
216         int i, j;
217         bool has_shared = false;
218
219         /* if no mloopcol: do not do */
220         /* if mtexpoly: only the involved faces, otherwise all */
221
222         if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) return;
223
224         scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol");
225
226         for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
227                 if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
228                         const MLoop *ml = me->mloop + mp->loopstart;
229                         MLoopCol *lcol = me->mloopcol + mp->loopstart;
230                         for (j = 0; j < mp->totloop; j++, ml++, lcol++) {
231                                 scol[ml->v][0] += lcol->r;
232                                 scol[ml->v][1] += lcol->g;
233                                 scol[ml->v][2] += lcol->b;
234                                 scol[ml->v][3] += 1;
235                                 has_shared = 1;
236                         }
237                 }
238         }
239
240         if (has_shared) {
241                 for (i = 0; i < me->totvert; i++) {
242                         if (scol[i][3] != 0) {
243                                 scol[i][0] = divide_round_i(scol[i][0], scol[i][3]);
244                                 scol[i][1] = divide_round_i(scol[i][1], scol[i][3]);
245                                 scol[i][2] = divide_round_i(scol[i][2], scol[i][3]);
246                         }
247                 }
248
249                 for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
250                         if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
251                                 const MLoop *ml = me->mloop + mp->loopstart;
252                                 MLoopCol *lcol = me->mloopcol + mp->loopstart;
253                                 for (j = 0; j < mp->totloop; j++, ml++, lcol++) {
254                                         if (mlooptag[mp->loopstart + j]) {
255                                                 lcol->r = scol[ml->v][0];
256                                                 lcol->g = scol[ml->v][1];
257                                                 lcol->b = scol[ml->v][2];
258                                         }
259                                 }
260                         }
261                 }
262         }
263
264         MEM_freeN(scol);
265 }
266
267 static bool vertex_color_smooth(Object *ob)
268 {
269         Mesh *me;
270         const MPoly *mp;
271
272         int i, j;
273
274         bool *mlooptag;
275
276         if (((me = BKE_mesh_from_object(ob)) == NULL) ||
277             (ED_mesh_color_ensure(me, NULL) == false))
278         {
279                 return false;
280         }
281
282         const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
283
284         mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
285
286         /* simply tag loops of selected faces */
287         mp = me->mpoly;
288         for (i = 0; i < me->totpoly; i++, mp++) {
289                 const MLoop *ml = me->mloop + mp->loopstart;
290                 int ml_index = mp->loopstart;
291
292                 if (use_face_sel && !(mp->flag & ME_FACE_SEL))
293                         continue;
294
295                 for (j = 0; j < mp->totloop; j++, ml_index++, ml++) {
296                         mlooptag[ml_index] = true;
297                 }
298         }
299
300         /* remove stale me->mcol, will be added later */
301         BKE_mesh_tessface_clear(me);
302
303         vertex_color_smooth_looptag(me, mlooptag);
304
305         MEM_freeN(mlooptag);
306
307         DAG_id_tag_update(&me->id, 0);
308
309         return true;
310 }
311
312
313 static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op))
314 {
315         Object *obact = CTX_data_active_object(C);
316         if (vertex_color_smooth(obact)) {
317                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
318                 return OPERATOR_FINISHED;
319         }
320         else {
321                 return OPERATOR_CANCELLED;
322         }
323 }
324
325 void PAINT_OT_vertex_color_smooth(wmOperatorType *ot)
326 {
327         /* identifiers */
328         ot->name = "Smooth Vertex Colors";
329         ot->idname = "PAINT_OT_vertex_color_smooth";
330         ot->description = "Smooth colors across vertices";
331
332         /* api callbacks */
333         ot->exec = vertex_color_smooth_exec;
334         ot->poll = vertex_paint_mode_poll;
335
336         /* flags */
337         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
338 }
339
340 /** \} */
341
342 /* -------------------------------------------------------------------- */
343 /** \name Vertex Color Transformation Operators
344  * \{ */
345
346 struct VPaintTx_BrightContrastData {
347         /* pre-calculated */
348         float gain;
349         float offset;
350 };
351
352 static void vpaint_tx_brightness_contrast(const float col[3], const void *user_data, float r_col[3])
353 {
354         const struct VPaintTx_BrightContrastData *data = user_data;
355
356         for (int i = 0; i < 3; i++) {
357                 r_col[i] = data->gain * col[i] + data->offset;
358         }
359 }
360
361 static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
362 {
363         Object *obact = CTX_data_active_object(C);
364
365         float gain, offset;
366         {
367                 float brightness = RNA_float_get(op->ptr, "brightness");
368                 float contrast = RNA_float_get(op->ptr, "contrast");
369                 brightness /= 100.0f;
370                 float delta = contrast / 200.0f;
371                 gain = 1.0f - delta * 2.0f;
372                 /*
373                  * The algorithm is by Werner D. Streidt
374                  * (http://visca.com/ffactory/archives/5-99/msg00021.html)
375                  * Extracted of OpenCV demhist.c
376                  */
377                 if (contrast > 0) {
378                         gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
379                         offset = gain * (brightness - delta);
380                 }
381                 else {
382                         delta *= -1;
383                         offset = gain * (brightness + delta);
384                 }
385         }
386
387         const struct VPaintTx_BrightContrastData user_data = {
388                 .gain = gain,
389                 .offset = offset,
390         };
391
392         if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) {
393                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
394                 return OPERATOR_FINISHED;
395         }
396         else {
397                 return OPERATOR_CANCELLED;
398         }
399 }
400
401 void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
402 {
403         PropertyRNA *prop;
404
405         /* identifiers */
406         ot->name = "Vertex Paint Bright/Contrast";
407         ot->idname = "PAINT_OT_vertex_color_brightness_contrast";
408         ot->description = "Adjust vertex color brightness/contrast";
409
410         /* api callbacks */
411         ot->exec = vertex_color_brightness_contrast_exec;
412         ot->poll = vertex_paint_mode_poll;
413
414         /* flags */
415         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
416
417         /* params */
418         const float min = -100, max = +100;
419         prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
420         prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
421         RNA_def_property_ui_range(prop, min, max, 1, 1);
422 }
423
424 struct VPaintTx_HueSatData {
425         float hue;
426         float sat;
427         float val;
428 };
429
430 static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3])
431 {
432         const struct VPaintTx_HueSatData *data = user_data;
433         float hsv[3];
434         rgb_to_hsv_v(col, hsv);
435
436         hsv[0] += (data->hue - 0.5f);
437         if (hsv[0] > 1.0f) {
438                 hsv[0] -= 1.0f;
439         }
440         else if (hsv[0] < 0.0f) {
441                 hsv[0] += 1.0f;
442         }
443         hsv[1] *= data->sat;
444         hsv[2] *= data->val;
445
446         hsv_to_rgb_v(hsv, r_col);
447 }
448
449 static int vertex_color_hsv_exec(bContext *C, wmOperator *op)
450 {
451         Object *obact = CTX_data_active_object(C);
452
453         const struct VPaintTx_HueSatData user_data = {
454                 .hue = RNA_float_get(op->ptr, "h"),
455                 .sat = RNA_float_get(op->ptr, "s"),
456                 .val = RNA_float_get(op->ptr, "v"),
457         };
458
459         if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) {
460                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
461                 return OPERATOR_FINISHED;
462         }
463         else {
464                 return OPERATOR_CANCELLED;
465         }
466 }
467
468 void PAINT_OT_vertex_color_hsv(wmOperatorType *ot)
469 {
470         /* identifiers */
471         ot->name = "Vertex Paint Hue Saturation Value";
472         ot->idname = "PAINT_OT_vertex_color_hsv";
473         ot->description = "Adjust vertex color HSV values";
474
475         /* api callbacks */
476         ot->exec = vertex_color_hsv_exec;
477         ot->poll = vertex_paint_mode_poll;
478
479         /* flags */
480         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
481
482         /* params */
483         RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
484         RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
485         RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
486 }
487
488 static void vpaint_tx_invert(const float col[3], const void *UNUSED(user_data), float r_col[3])
489 {
490         for (int i = 0; i < 3; i++) {
491                 r_col[i] = 1.0f - col[i];
492         }
493 }
494
495 static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op))
496 {
497         Object *obact = CTX_data_active_object(C);
498
499         if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) {
500                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
501                 return OPERATOR_FINISHED;
502         }
503         else {
504                 return OPERATOR_CANCELLED;
505         }
506 }
507
508 void PAINT_OT_vertex_color_invert(wmOperatorType *ot)
509 {
510         /* identifiers */
511         ot->name = "Vertex Paint Invert";
512         ot->idname = "PAINT_OT_vertex_color_invert";
513         ot->description = "Invert RGB values";
514
515         /* api callbacks */
516         ot->exec = vertex_color_invert_exec;
517         ot->poll = vertex_paint_mode_poll;
518
519         /* flags */
520         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
521 }
522
523
524 struct VPaintTx_LevelsData {
525         float gain;
526         float offset;
527 };
528
529 static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3])
530 {
531         const struct VPaintTx_LevelsData *data = user_data;
532         for (int i = 0; i < 3; i++) {
533                 r_col[i] = data->gain * (col[i] + data->offset);
534         }
535 }
536
537 static int vertex_color_levels_exec(bContext *C, wmOperator *op)
538 {
539         Object *obact = CTX_data_active_object(C);
540
541         const struct VPaintTx_LevelsData user_data = {
542                 .gain = RNA_float_get(op->ptr, "gain"),
543                 .offset = RNA_float_get(op->ptr, "offset"),
544         };
545
546         if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) {
547                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
548                 return OPERATOR_FINISHED;
549         }
550         else {
551                 return OPERATOR_CANCELLED;
552         }
553 }
554
555 void PAINT_OT_vertex_color_levels(wmOperatorType *ot)
556 {
557         /* identifiers */
558         ot->name = "Vertex Paint Levels";
559         ot->idname = "PAINT_OT_vertex_color_levels";
560         ot->description = "Adjust levels of vertex colors";
561
562         /* api callbacks */
563         ot->exec = vertex_color_levels_exec;
564         ot->poll = vertex_paint_mode_poll;
565
566         /* flags */
567         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
568
569         /* params */
570         RNA_def_float(ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
571         RNA_def_float(ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
572 }
573
574 /** \} */