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