Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / sculpt_paint / paint_vertex_weight_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_weight_ops.c
22  *  \ingroup edsculpt
23  */
24
25 #include "MEM_guardedalloc.h"
26
27 #include "BLI_blenlib.h"
28 #include "BLI_math.h"
29 #include "BLI_array_utils.h"
30 #include "BLI_bitmap.h"
31 #include "BLI_task.h"
32 #include "BLI_string_utils.h"
33
34 #include "IMB_imbuf.h"
35 #include "IMB_imbuf_types.h"
36 #include "IMB_colormanagement.h"
37
38 #include "DNA_mesh_types.h"
39 #include "DNA_meshdata_types.h"
40 #include "DNA_particle_types.h"
41 #include "DNA_brush_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_scene_types.h"
44
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47 #include "RNA_enum_types.h"
48
49 #include "BKE_brush.h"
50 #include "BKE_context.h"
51 #include "BKE_deform.h"
52 #include "BKE_mesh.h"
53 #include "BKE_mesh_iterators.h"
54 #include "BKE_mesh_mapping.h"
55 #include "BKE_mesh_runtime.h"
56 #include "BKE_modifier.h"
57 #include "BKE_object_deform.h"
58 #include "BKE_paint.h"
59 #include "BKE_report.h"
60 #include "BKE_colortools.h"
61
62 #include "DEG_depsgraph.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "ED_armature.h"
68 #include "ED_mesh.h"
69 #include "ED_screen.h"
70 #include "ED_view3d.h"
71
72 #include "paint_intern.h"  /* own include */
73
74 /* -------------------------------------------------------------------- */
75 /** \name Store Previous Weights
76  *
77  * Use to avoid feedback loop w/ mirrored edits.
78  * \{ */
79
80 struct WPaintPrev {
81         /* previous vertex weights */
82         struct MDeformVert *wpaint_prev;
83         /* allocation size of prev buffers */
84         int tot;
85 };
86
87
88 static void wpaint_prev_init(struct WPaintPrev *wpp)
89 {
90         wpp->wpaint_prev = NULL;
91         wpp->tot = 0;
92 }
93
94 static void wpaint_prev_create(struct WPaintPrev *wpp, MDeformVert *dverts, int dcount)
95 {
96         wpaint_prev_init(wpp);
97
98         if (dverts && dcount) {
99                 wpp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev");
100                 wpp->tot = dcount;
101                 BKE_defvert_array_copy(wpp->wpaint_prev, dverts, dcount);
102         }
103 }
104
105 static void wpaint_prev_destroy(struct WPaintPrev *wpp)
106 {
107         if (wpp->wpaint_prev) {
108                 BKE_defvert_array_free(wpp->wpaint_prev, wpp->tot);
109         }
110         wpp->wpaint_prev = NULL;
111         wpp->tot = 0;
112 }
113
114 /** \} */
115
116 /* -------------------------------------------------------------------- */
117 /** \name Weight from Bones Operator
118  * \{ */
119
120 static bool weight_from_bones_poll(bContext *C)
121 {
122         Object *ob = CTX_data_active_object(C);
123
124         return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob));
125 }
126
127 static int weight_from_bones_exec(bContext *C, wmOperator *op)
128 {
129         Depsgraph *depsgraph = CTX_data_depsgraph(C);
130         Scene *scene = CTX_data_scene(C);
131         Object *ob = CTX_data_active_object(C);
132         Object *armob = modifiers_isDeformedByArmature(ob);
133         Mesh *me = ob->data;
134         int type = RNA_enum_get(op->ptr, "type");
135
136         ED_object_vgroup_calc_from_armature(op->reports, depsgraph, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X));
137
138         DEG_id_tag_update(&me->id, 0);
139         WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
140
141         return OPERATOR_FINISHED;
142 }
143
144 void PAINT_OT_weight_from_bones(wmOperatorType *ot)
145 {
146         static const EnumPropertyItem type_items[] = {
147                 {ARM_GROUPS_AUTO, "AUTOMATIC", 0, "Automatic", "Automatic weights from bones"},
148                 {ARM_GROUPS_ENVELOPE, "ENVELOPES", 0, "From Envelopes", "Weights from envelopes with user defined radius"},
149                 {0, NULL, 0, NULL, NULL}};
150
151         /* identifiers */
152         ot->name = "Weight from Bones";
153         ot->idname = "PAINT_OT_weight_from_bones";
154         ot->description = (
155                 "Set the weights of the groups matching the attached armature's selected bones, "
156                 "using the distance between the vertices and the bones");
157
158         /* api callbacks */
159         ot->exec = weight_from_bones_exec;
160         ot->invoke = WM_menu_invoke;
161         ot->poll = weight_from_bones_poll;
162
163         /* flags */
164         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
165
166         /* properties */
167         ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights");
168 }
169
170 /** \} */
171
172 /* -------------------------------------------------------------------- */
173 /** \name Sample Weight Operator
174  * \{ */
175
176 /* sets wp->weight to the closest weight value to vertex */
177 /* note: we cant sample frontbuf, weight colors are interpolated too unpredictable */
178 static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
179 {
180         ViewContext vc;
181         Mesh *me;
182         bool changed = false;
183
184         ED_view3d_viewcontext_init(C, &vc);
185         me = BKE_mesh_from_object(vc.obact);
186
187         if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) {
188                 const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
189                 int v_idx_best = -1;
190                 uint index;
191
192                 view3d_operator_needs_opengl(C);
193                 ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
194
195                 if (use_vert_sel) {
196                         if (ED_mesh_pick_vert(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_VERT_SIZE, true)) {
197                                 v_idx_best = index;
198                         }
199                 }
200                 else {
201                         if (ED_mesh_pick_face_vert(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
202                                 v_idx_best = index;
203                         }
204                         else if (ED_mesh_pick_face(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
205                                 /* this relies on knowning the internal worksings of ED_mesh_pick_face_vert() */
206                                 BKE_report(op->reports, RPT_WARNING, "The modifier used does not support deformed locations");
207                         }
208                 }
209
210                 if (v_idx_best != -1) { /* should always be valid */
211                         ToolSettings *ts = vc.scene->toolsettings;
212                         Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
213                         const int vgroup_active = vc.obact->actdef - 1;
214                         float vgroup_weight = defvert_find_weight(&me->dvert[v_idx_best], vgroup_active);
215
216                         /* use combined weight in multipaint mode, since that's what is displayed to the user in the colors */
217                         if (ts->multipaint) {
218                                 int defbase_tot_sel;
219                                 const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
220                                 bool *defbase_sel = BKE_object_defgroup_selected_get(vc.obact, defbase_tot, &defbase_tot_sel);
221
222                                 if (defbase_tot_sel > 1) {
223                                         if (me->editflag & ME_EDIT_MIRROR_X) {
224                                                 BKE_object_defgroup_mirror_selection(
225                                                         vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
226                                         }
227
228                                         vgroup_weight = BKE_defvert_multipaint_collective_weight(
229                                                 &me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, ts->auto_normalize);
230
231                                         /* if autonormalize is enabled, but weights are not normalized, the value can exceed 1 */
232                                         CLAMP(vgroup_weight, 0.0f, 1.0f);
233                                 }
234
235                                 MEM_freeN(defbase_sel);
236                         }
237
238                         BKE_brush_weight_set(vc.scene, brush, vgroup_weight);
239                         changed = true;
240                 }
241         }
242
243         if (changed) {
244                 /* not really correct since the brush didnt change, but redraws the toolbar */
245                 WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL); /* ts->wpaint->paint.brush */
246
247                 return OPERATOR_FINISHED;
248         }
249         else {
250                 return OPERATOR_CANCELLED;
251         }
252 }
253
254 void PAINT_OT_weight_sample(wmOperatorType *ot)
255 {
256         /* identifiers */
257         ot->name = "Weight Paint Sample Weight";
258         ot->idname = "PAINT_OT_weight_sample";
259         ot->description = "Use the mouse to sample a weight in the 3D view";
260
261         /* api callbacks */
262         ot->invoke = weight_sample_invoke;
263         ot->poll = weight_paint_mode_poll;
264
265         /* flags */
266         ot->flag = OPTYPE_UNDO;
267 }
268
269 /** \} */
270
271 /* -------------------------------------------------------------------- */
272 /** \name Weight Paint Sample Group Operator
273  * \{ */
274
275 /* samples cursor location, and gives menu with vertex groups to activate */
276 static bool weight_paint_sample_enum_itemf__helper(const MDeformVert *dvert, const int defbase_tot, int *groups)
277 {
278         /* this func fills in used vgroup's */
279         bool found = false;
280         int i = dvert->totweight;
281         MDeformWeight *dw;
282         for (dw = dvert->dw; i > 0; dw++, i--) {
283                 if (dw->def_nr < defbase_tot) {
284                         groups[dw->def_nr] = true;
285                         found = true;
286                 }
287         }
288         return found;
289 }
290 static const EnumPropertyItem *weight_paint_sample_enum_itemf(
291         bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
292 {
293         if (C) {
294                 wmWindow *win = CTX_wm_window(C);
295                 if (win && win->eventstate) {
296                         ViewContext vc;
297                         Mesh *me;
298
299                         ED_view3d_viewcontext_init(C, &vc);
300                         me = BKE_mesh_from_object(vc.obact);
301
302                         if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) {
303                                 const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
304                                 const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
305                                 int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups");
306                                 bool found = false;
307                                 uint index;
308
309                                 const int mval[2] = {
310                                     win->eventstate->x - vc.ar->winrct.xmin,
311                                     win->eventstate->y - vc.ar->winrct.ymin,
312                                 };
313
314                                 view3d_operator_needs_opengl(C);
315                                 ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
316
317                                 if (use_vert_sel) {
318                                         if (ED_mesh_pick_vert(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_VERT_SIZE, true)) {
319                                                 MDeformVert *dvert = &me->dvert[index];
320                                                 found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
321                                         }
322                                 }
323                                 else {
324                                         if (ED_mesh_pick_face(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
325                                                 const MPoly *mp = &me->mpoly[index];
326                                                 uint fidx = mp->totloop - 1;
327
328                                                 do {
329                                                         MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v];
330                                                         found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
331                                                 } while (fidx--);
332                                         }
333                                 }
334
335                                 if (found == false) {
336                                         MEM_freeN(groups);
337                                 }
338                                 else {
339                                         EnumPropertyItem *item = NULL, item_tmp = {0};
340                                         int totitem = 0;
341                                         int i = 0;
342                                         bDeformGroup *dg;
343                                         for (dg = vc.obact->defbase.first; dg && i < defbase_tot; i++, dg = dg->next) {
344                                                 if (groups[i]) {
345                                                         item_tmp.identifier = item_tmp.name = dg->name;
346                                                         item_tmp.value = i;
347                                                         RNA_enum_item_add(&item, &totitem, &item_tmp);
348                                                 }
349                                         }
350
351                                         RNA_enum_item_end(&item, &totitem);
352                                         *r_free = true;
353
354                                         MEM_freeN(groups);
355                                         return item;
356                                 }
357                         }
358                 }
359         }
360
361         return DummyRNA_NULL_items;
362 }
363
364 static int weight_sample_group_exec(bContext *C, wmOperator *op)
365 {
366         int type = RNA_enum_get(op->ptr, "group");
367         ViewContext vc;
368         ED_view3d_viewcontext_init(C, &vc);
369
370         BLI_assert(type + 1 >= 0);
371         vc.obact->actdef = type + 1;
372
373         DEG_id_tag_update(&vc.obact->id, OB_RECALC_DATA);
374         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, vc.obact);
375         return OPERATOR_FINISHED;
376 }
377
378 /* TODO, we could make this a menu into OBJECT_OT_vertex_group_set_active rather than its own operator */
379 void PAINT_OT_weight_sample_group(wmOperatorType *ot)
380 {
381         PropertyRNA *prop = NULL;
382
383         /* identifiers */
384         ot->name = "Weight Paint Sample Group";
385         ot->idname = "PAINT_OT_weight_sample_group";
386         ot->description = "Select one of the vertex groups available under current mouse position";
387
388         /* api callbacks */
389         ot->exec = weight_sample_group_exec;
390         ot->invoke = WM_menu_invoke;
391         ot->poll = weight_paint_mode_poll;
392
393         /* flags */
394         ot->flag = OPTYPE_UNDO;
395
396         /* keyingset to use (dynamic enum) */
397         prop = RNA_def_enum(ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
398         RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf);
399         RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
400         ot->prop = prop;
401 }
402
403 /** \} */
404
405 /* -------------------------------------------------------------------- */
406 /** \name Weight Set Operator
407  * \{ */
408
409 /* fills in the selected faces with the current weight and vertex group */
410 static bool weight_paint_set(Object *ob, float paintweight)
411 {
412         Mesh *me = ob->data;
413         const MPoly *mp;
414         MDeformWeight *dw, *dw_prev;
415         int vgroup_active, vgroup_mirror = -1;
416         uint index;
417         const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
418
419         /* mutually exclusive, could be made into a */
420         const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me);
421
422         if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) {
423                 return false;
424         }
425
426         vgroup_active = ob->actdef - 1;
427
428         /* if mirror painting, find the other group */
429         if (me->editflag & ME_EDIT_MIRROR_X) {
430                 vgroup_mirror = ED_wpaint_mirror_vgroup_ensure(ob, vgroup_active);
431         }
432
433         struct WPaintPrev wpp;
434         wpaint_prev_create(&wpp, me->dvert, me->totvert);
435
436         for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) {
437                 uint fidx = mp->totloop - 1;
438
439                 if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) {
440                         continue;
441                 }
442
443                 do {
444                         uint vidx = me->mloop[mp->loopstart + fidx].v;
445
446                         if (!me->dvert[vidx].flag) {
447                                 if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) {
448                                         continue;
449                                 }
450
451                                 dw = defvert_verify_index(&me->dvert[vidx], vgroup_active);
452                                 if (dw) {
453                                         dw_prev = defvert_verify_index(wpp.wpaint_prev + vidx, vgroup_active);
454                                         dw_prev->weight = dw->weight; /* set the undo weight */
455                                         dw->weight = paintweight;
456
457                                         if (me->editflag & ME_EDIT_MIRROR_X) {  /* x mirror painting */
458                                                 int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology);
459                                                 if (j >= 0) {
460                                                         /* copy, not paint again */
461                                                         if (vgroup_mirror != -1) {
462                                                                 dw = defvert_verify_index(me->dvert + j, vgroup_mirror);
463                                                                 dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_mirror);
464                                                         }
465                                                         else {
466                                                                 dw = defvert_verify_index(me->dvert + j, vgroup_active);
467                                                                 dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_active);
468                                                         }
469                                                         dw_prev->weight = dw->weight; /* set the undo weight */
470                                                         dw->weight = paintweight;
471                                                 }
472                                         }
473                                 }
474                                 me->dvert[vidx].flag = 1;
475                         }
476
477                 } while (fidx--);
478         }
479
480         {
481                 MDeformVert *dv = me->dvert;
482                 for (index = me->totvert; index != 0; index--, dv++) {
483                         dv->flag = 0;
484                 }
485         }
486
487         wpaint_prev_destroy(&wpp);
488
489         DEG_id_tag_update(&me->id, 0);
490
491         return true;
492 }
493
494
495 static int weight_paint_set_exec(bContext *C, wmOperator *op)
496 {
497         struct Scene *scene = CTX_data_scene(C);
498         Object *obact = CTX_data_active_object(C);
499         ToolSettings *ts = CTX_data_tool_settings(C);
500         Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
501         float vgroup_weight = BKE_brush_weight_get(scene, brush);
502
503         if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, NULL) == false) {
504                 return OPERATOR_CANCELLED;
505         }
506
507         if (weight_paint_set(obact, vgroup_weight)) {
508                 ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */
509                 return OPERATOR_FINISHED;
510         }
511         else {
512                 return OPERATOR_CANCELLED;
513         }
514 }
515
516 void PAINT_OT_weight_set(wmOperatorType *ot)
517 {
518         /* identifiers */
519         ot->name = "Set Weight";
520         ot->idname = "PAINT_OT_weight_set";
521         ot->description = "Fill the active vertex group with the current paint weight";
522
523         /* api callbacks */
524         ot->exec = weight_paint_set_exec;
525         ot->poll = mask_paint_poll;
526
527         /* flags */
528         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
529 }
530
531 /** \} */
532
533 /* -------------------------------------------------------------------- */
534 /** \name Interactive Weight Gradient Operator
535  * \{ */
536
537 /* *** VGroups Gradient *** */
538 typedef struct WPGradient_vertStore {
539         float sco[2];
540         float weight_orig;
541         enum {
542                 VGRAD_STORE_NOP      = 0,
543                 VGRAD_STORE_DW_EXIST = (1 << 0)
544         } flag;
545 } WPGradient_vertStore;
546
547 typedef struct WPGradient_vertStoreBase {
548         struct WPaintPrev wpp;
549         WPGradient_vertStore elem[0];
550 } WPGradient_vertStoreBase;
551
552 typedef struct WPGradient_userData {
553         struct ARegion *ar;
554         Scene *scene;
555         Mesh *me;
556         Brush *brush;
557         const float *sco_start;     /* [2] */
558         const float *sco_end;       /* [2] */
559         float        sco_line_div;  /* store (1.0f / len_v2v2(sco_start, sco_end)) */
560         int def_nr;
561         bool is_init;
562         WPGradient_vertStoreBase *vert_cache;
563         /* only for init */
564         BLI_bitmap *vert_visit;
565
566         /* options */
567         short use_select;
568         short type;
569         float weightpaint;
570 } WPGradient_userData;
571
572 static void gradientVert_update(WPGradient_userData *grad_data, int index)
573 {
574         Mesh *me = grad_data->me;
575         WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
576         float alpha;
577
578         if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) {
579                 alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end);
580         }
581         else {
582                 BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL);
583                 alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div;
584         }
585         /* no need to clamp 'alpha' yet */
586
587         /* adjust weight */
588         alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f);
589
590         if (alpha != 0.0f) {
591                 MDeformVert *dv = &me->dvert[index];
592                 MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr);
593                 // dw->weight = alpha; // testing
594                 int tool = grad_data->brush->vertexpaint_tool;
595                 float testw;
596
597                 /* init if we just added */
598                 testw = ED_wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha);
599                 CLAMP(testw, 0.0f, 1.0f);
600                 dw->weight = testw;
601         }
602         else {
603                 MDeformVert *dv = &me->dvert[index];
604                 if (vs->flag & VGRAD_STORE_DW_EXIST) {
605                         /* normally we NULL check, but in this case we know it exists */
606                         MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
607                         dw->weight = vs->weight_orig;
608                 }
609                 else {
610                         /* wasn't originally existing, remove */
611                         MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
612                         if (dw) {
613                                 defvert_remove_group(dv, dw);
614                         }
615                 }
616         }
617 }
618
619 static void gradientVertUpdate__mapFunc(
620         void *userData, int index, const float UNUSED(co[3]),
621         const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
622 {
623         WPGradient_userData *grad_data = userData;
624         Mesh *me = grad_data->me;
625         if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
626                 WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
627                 if (vs->sco[0] != FLT_MAX) {
628                         gradientVert_update(grad_data, index);
629                 }
630         }
631 }
632
633 static void gradientVertInit__mapFunc(
634         void *userData, int index, const float co[3],
635         const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
636 {
637         WPGradient_userData *grad_data = userData;
638         Mesh *me = grad_data->me;
639
640         if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
641                 /* run first pass only,
642                  * the screen coords of the verts need to be cached because
643                  * updating the mesh may move them about (entering feedback loop) */
644
645                 if (BLI_BITMAP_TEST(grad_data->vert_visit, index) == 0) {
646                         WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
647                         if (ED_view3d_project_float_object(
648                                     grad_data->ar,
649                                     co, vs->sco,
650                                     V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK)
651                         {
652                                 /* ok */
653                                 MDeformVert *dv = &me->dvert[index];
654                                 const MDeformWeight *dw;
655                                 dw = defvert_find_index(dv, grad_data->def_nr);
656                                 if (dw) {
657                                         vs->weight_orig = dw->weight;
658                                         vs->flag = VGRAD_STORE_DW_EXIST;
659                                 }
660                                 else {
661                                         vs->weight_orig = 0.0f;
662                                         vs->flag = VGRAD_STORE_NOP;
663                                 }
664
665                                 BLI_BITMAP_ENABLE(grad_data->vert_visit, index);
666
667                                 gradientVert_update(grad_data, index);
668                         }
669                         else {
670                                 /* no go */
671                                 copy_v2_fl(vs->sco, FLT_MAX);
672                         }
673                 }
674         }
675 }
676
677 static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
678 {
679         wmGesture *gesture = op->customdata;
680         WPGradient_vertStoreBase *vert_cache = gesture->userdata;
681         int ret = WM_gesture_straightline_modal(C, op, event);
682
683         if (ret & OPERATOR_RUNNING_MODAL) {
684                 if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {  /* XXX, hardcoded */
685                         /* generally crap! redo! */
686                         WM_gesture_straightline_cancel(C, op);
687                         ret &= ~OPERATOR_RUNNING_MODAL;
688                         ret |= OPERATOR_FINISHED;
689                 }
690         }
691
692         if (ret & OPERATOR_CANCELLED) {
693                 Object *ob = CTX_data_active_object(C);
694                 Mesh *me = ob->data;
695                 if (vert_cache->wpp.wpaint_prev) {
696                         BKE_defvert_array_free_elems(me->dvert, me->totvert);
697                         BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert);
698                         wpaint_prev_destroy(&vert_cache->wpp);
699                 }
700                 MEM_freeN(vert_cache);
701
702                 DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
703                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
704         }
705         else if (ret & OPERATOR_FINISHED) {
706                 wpaint_prev_destroy(&vert_cache->wpp);
707                 MEM_freeN(vert_cache);
708         }
709
710         return ret;
711 }
712
713 static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
714 {
715         wmGesture *gesture = op->customdata;
716         WPGradient_vertStoreBase *vert_cache;
717         struct ARegion *ar = CTX_wm_region(C);
718         Scene *scene = CTX_data_scene(C);
719         Object *ob = CTX_data_active_object(C);
720         Mesh *me = ob->data;
721         int x_start = RNA_int_get(op->ptr, "xstart");
722         int y_start = RNA_int_get(op->ptr, "ystart");
723         int x_end = RNA_int_get(op->ptr, "xend");
724         int y_end = RNA_int_get(op->ptr, "yend");
725         float sco_start[2] = {x_start, y_start};
726         float sco_end[2] = {x_end, y_end};
727         const bool is_interactive = (gesture != NULL);
728
729         Depsgraph *depsgraph = CTX_data_depsgraph(C);
730
731         WPGradient_userData data = {NULL};
732
733         if (is_interactive) {
734                 if (gesture->userdata == NULL) {
735                         gesture->userdata = MEM_mallocN(
736                                 sizeof(WPGradient_vertStoreBase) +
737                                 (sizeof(WPGradient_vertStore) * me->totvert),
738                                 __func__);
739                         gesture->userdata_free = false;
740                         data.is_init = true;
741
742                         wpaint_prev_create(&((WPGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert);
743
744                         /* on init only, convert face -> vert sel  */
745                         if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
746                                 BKE_mesh_flush_select_from_polys(me);
747                         }
748                 }
749
750                 vert_cache = gesture->userdata;
751         }
752         else {
753                 if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) {
754                         return OPERATOR_CANCELLED;
755                 }
756
757                 data.is_init = true;
758                 vert_cache = MEM_mallocN(
759                         sizeof(WPGradient_vertStoreBase) +
760                         (sizeof(WPGradient_vertStore) * me->totvert),
761                         __func__);
762         }
763
764         data.ar = ar;
765         data.scene = scene;
766         data.me = ob->data;
767         data.sco_start = sco_start;
768         data.sco_end   = sco_end;
769         data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end);
770         data.def_nr = ob->actdef - 1;
771         data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL));
772         data.vert_cache = vert_cache;
773         data.vert_visit = NULL;
774         data.type = RNA_enum_get(op->ptr, "type");
775
776         {
777                 ToolSettings *ts = CTX_data_tool_settings(C);
778                 VPaint *wp = ts->wpaint;
779                 struct Brush *brush = BKE_paint_brush(&wp->paint);
780
781                 curvemapping_initialize(brush->curve);
782
783                 data.brush = brush;
784                 data.weightpaint = BKE_brush_weight_get(scene, brush);
785         }
786
787         ED_view3d_init_mats_rv3d(ob, ar->regiondata);
788
789         Mesh *me_eval = mesh_get_eval_final(depsgraph, scene, ob, scene->customdata_mask | CD_MASK_ORIGINDEX);
790         if (data.is_init) {
791                 data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__);
792
793                 BKE_mesh_foreach_mapped_vert(me_eval, gradientVertInit__mapFunc, &data, MESH_FOREACH_NOP);
794
795                 MEM_freeN(data.vert_visit);
796                 data.vert_visit = NULL;
797         }
798         else {
799                 BKE_mesh_foreach_mapped_vert(me_eval, gradientVertUpdate__mapFunc, &data, MESH_FOREACH_NOP);
800         }
801
802         DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
803         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
804
805         if (is_interactive == false) {
806                 MEM_freeN(vert_cache);
807         }
808
809         return OPERATOR_FINISHED;
810 }
811
812 static int paint_weight_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event)
813 {
814         int ret;
815
816         if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) {
817                 return OPERATOR_CANCELLED;
818         }
819
820         ret = WM_gesture_straightline_invoke(C, op, event);
821         if (ret & OPERATOR_RUNNING_MODAL) {
822                 struct ARegion *ar = CTX_wm_region(C);
823                 if (ar->regiontype == RGN_TYPE_WINDOW) {
824                         /* TODO, hardcoded, extend WM_gesture_straightline_ */
825                         if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
826                                 wmGesture *gesture = op->customdata;
827                                 gesture->is_active = true;
828                         }
829                 }
830         }
831         return ret;
832 }
833
834 void PAINT_OT_weight_gradient(wmOperatorType *ot)
835 {
836         /* defined in DNA_space_types.h */
837         static const EnumPropertyItem gradient_types[] = {
838                 {WPAINT_GRADIENT_TYPE_LINEAR, "LINEAR", 0, "Linear", ""},
839                 {WPAINT_GRADIENT_TYPE_RADIAL, "RADIAL", 0, "Radial", ""},
840                 {0, NULL, 0, NULL, NULL}
841         };
842
843         PropertyRNA *prop;
844
845         /* identifiers */
846         ot->name = "Weight Gradient";
847         ot->idname = "PAINT_OT_weight_gradient";
848         ot->description = "Draw a line to apply a weight gradient to selected vertices";
849
850         /* api callbacks */
851         ot->invoke = paint_weight_gradient_invoke;
852         ot->modal = paint_weight_gradient_modal;
853         ot->exec = paint_weight_gradient_exec;
854         ot->poll = weight_paint_poll_ignore_tool;
855         ot->cancel = WM_gesture_straightline_cancel;
856
857         /* flags */
858         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
859
860         prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", "");
861         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
862
863         WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
864 }
865
866 /** \} */