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