Merge branch 'blender2.7'
[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_bitmap.h"
30
31 #include "IMB_imbuf.h"
32 #include "IMB_imbuf_types.h"
33 #include "IMB_colormanagement.h"
34
35 #include "DNA_mesh_types.h"
36 #include "DNA_meshdata_types.h"
37 #include "DNA_particle_types.h"
38 #include "DNA_brush_types.h"
39 #include "DNA_object_types.h"
40 #include "DNA_scene_types.h"
41
42 #include "RNA_access.h"
43 #include "RNA_define.h"
44 #include "RNA_enum_types.h"
45
46 #include "BKE_brush.h"
47 #include "BKE_context.h"
48 #include "BKE_deform.h"
49 #include "BKE_mesh.h"
50 #include "BKE_mesh_iterators.h"
51 #include "BKE_mesh_mapping.h"
52 #include "BKE_mesh_runtime.h"
53 #include "BKE_modifier.h"
54 #include "BKE_object_deform.h"
55 #include "BKE_paint.h"
56 #include "BKE_report.h"
57 #include "BKE_colortools.h"
58
59 #include "DEG_depsgraph.h"
60 #include "DEG_depsgraph_query.h"
61
62 #include "WM_api.h"
63 #include "WM_types.h"
64
65 #include "ED_armature.h"
66 #include "ED_mesh.h"
67 #include "ED_screen.h"
68 #include "ED_view3d.h"
69
70 #include "paint_intern.h"  /* own include */
71
72 /* -------------------------------------------------------------------- */
73 /** \name Store Previous Weights
74  *
75  * Use to avoid feedback loop w/ mirrored edits.
76  * \{ */
77
78 struct WPaintPrev {
79         /* previous vertex weights */
80         struct MDeformVert *wpaint_prev;
81         /* allocation size of prev buffers */
82         int tot;
83 };
84
85
86 static void wpaint_prev_init(struct WPaintPrev *wpp)
87 {
88         wpp->wpaint_prev = NULL;
89         wpp->tot = 0;
90 }
91
92 static void wpaint_prev_create(struct WPaintPrev *wpp, MDeformVert *dverts, int dcount)
93 {
94         wpaint_prev_init(wpp);
95
96         if (dverts && dcount) {
97                 wpp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev");
98                 wpp->tot = dcount;
99                 BKE_defvert_array_copy(wpp->wpaint_prev, dverts, dcount);
100         }
101 }
102
103 static void wpaint_prev_destroy(struct WPaintPrev *wpp)
104 {
105         if (wpp->wpaint_prev) {
106                 BKE_defvert_array_free(wpp->wpaint_prev, wpp->tot);
107         }
108         wpp->wpaint_prev = NULL;
109         wpp->tot = 0;
110 }
111
112 /** \} */
113
114 /* -------------------------------------------------------------------- */
115 /** \name Weight from Bones Operator
116  * \{ */
117
118 static bool weight_from_bones_poll(bContext *C)
119 {
120         Object *ob = CTX_data_active_object(C);
121
122         return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob));
123 }
124
125 static int weight_from_bones_exec(bContext *C, wmOperator *op)
126 {
127         Depsgraph *depsgraph = CTX_data_depsgraph(C);
128         Scene *scene = CTX_data_scene(C);
129         Object *ob = CTX_data_active_object(C);
130         Object *armob = modifiers_isDeformedByArmature(ob);
131         Mesh *me = ob->data;
132         int type = RNA_enum_get(op->ptr, "type");
133
134         ED_object_vgroup_calc_from_armature(op->reports, depsgraph, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X));
135
136         DEG_id_tag_update(&me->id, 0);
137         WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
138
139         return OPERATOR_FINISHED;
140 }
141
142 void PAINT_OT_weight_from_bones(wmOperatorType *ot)
143 {
144         static const EnumPropertyItem type_items[] = {
145                 {ARM_GROUPS_AUTO, "AUTOMATIC", 0, "Automatic", "Automatic weights from bones"},
146                 {ARM_GROUPS_ENVELOPE, "ENVELOPES", 0, "From Envelopes", "Weights from envelopes with user defined radius"},
147                 {0, NULL, 0, NULL, NULL}};
148
149         /* identifiers */
150         ot->name = "Weight from Bones";
151         ot->idname = "PAINT_OT_weight_from_bones";
152         ot->description = (
153                 "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,
215                          * since that's what is displayed to the user in the colors */
216                         if (ts->multipaint) {
217                                 int defbase_tot_sel;
218                                 const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
219                                 bool *defbase_sel = BKE_object_defgroup_selected_get(vc.obact, defbase_tot, &defbase_tot_sel);
220
221                                 if (defbase_tot_sel > 1) {
222                                         if (me->editflag & ME_EDIT_MIRROR_X) {
223                                                 BKE_object_defgroup_mirror_selection(
224                                                         vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
225                                         }
226
227                                         vgroup_weight = BKE_defvert_multipaint_collective_weight(
228                                                 &me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, ts->auto_normalize);
229
230                                         /* if autonormalize is enabled, but weights are not normalized,
231                                          * 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 didn't 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, ID_RECALC_GEOMETRY);
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
379  * rather than its own operator */
380 void PAINT_OT_weight_sample_group(wmOperatorType *ot)
381 {
382         PropertyRNA *prop = NULL;
383
384         /* identifiers */
385         ot->name = "Weight Paint Sample Group";
386         ot->idname = "PAINT_OT_weight_sample_group";
387         ot->description = "Select one of the vertex groups available under current mouse position";
388
389         /* api callbacks */
390         ot->exec = weight_sample_group_exec;
391         ot->invoke = WM_menu_invoke;
392         ot->poll = weight_paint_mode_poll;
393
394         /* flags */
395         ot->flag = OPTYPE_UNDO;
396
397         /* keyingset to use (dynamic enum) */
398         prop = RNA_def_enum(ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
399         RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf);
400         RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
401         ot->prop = prop;
402 }
403
404 /** \} */
405
406 /* -------------------------------------------------------------------- */
407 /** \name Weight Set Operator
408  * \{ */
409
410 /* fills in the selected faces with the current weight and vertex group */
411 static bool weight_paint_set(Object *ob, float paintweight)
412 {
413         Mesh *me = ob->data;
414         const MPoly *mp;
415         MDeformWeight *dw, *dw_prev;
416         int vgroup_active, vgroup_mirror = -1;
417         uint index;
418         const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
419
420         /* mutually exclusive, could be made into a */
421         const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me);
422
423         if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) {
424                 return false;
425         }
426
427         vgroup_active = ob->actdef - 1;
428
429         /* if mirror painting, find the other group */
430         if (me->editflag & ME_EDIT_MIRROR_X) {
431                 vgroup_mirror = ED_wpaint_mirror_vgroup_ensure(ob, vgroup_active);
432         }
433
434         struct WPaintPrev wpp;
435         wpaint_prev_create(&wpp, me->dvert, me->totvert);
436
437         for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) {
438                 uint fidx = mp->totloop - 1;
439
440                 if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) {
441                         continue;
442                 }
443
444                 do {
445                         uint vidx = me->mloop[mp->loopstart + fidx].v;
446
447                         if (!me->dvert[vidx].flag) {
448                                 if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) {
449                                         continue;
450                                 }
451
452                                 dw = defvert_verify_index(&me->dvert[vidx], vgroup_active);
453                                 if (dw) {
454                                         dw_prev = defvert_verify_index(wpp.wpaint_prev + vidx, vgroup_active);
455                                         dw_prev->weight = dw->weight; /* set the undo weight */
456                                         dw->weight = paintweight;
457
458                                         if (me->editflag & ME_EDIT_MIRROR_X) {  /* x mirror painting */
459                                                 int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology);
460                                                 if (j >= 0) {
461                                                         /* copy, not paint again */
462                                                         if (vgroup_mirror != -1) {
463                                                                 dw = defvert_verify_index(me->dvert + j, vgroup_mirror);
464                                                                 dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_mirror);
465                                                         }
466                                                         else {
467                                                                 dw = defvert_verify_index(me->dvert + j, vgroup_active);
468                                                                 dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_active);
469                                                         }
470                                                         dw_prev->weight = dw->weight; /* set the undo weight */
471                                                         dw->weight = paintweight;
472                                                 }
473                                         }
474                                 }
475                                 me->dvert[vidx].flag = 1;
476                         }
477
478                 } while (fidx--);
479         }
480
481         {
482                 MDeformVert *dv = me->dvert;
483                 for (index = me->totvert; index != 0; index--, dv++) {
484                         dv->flag = 0;
485                 }
486         }
487
488         wpaint_prev_destroy(&wpp);
489
490         DEG_id_tag_update(&me->id, 0);
491
492         return true;
493 }
494
495
496 static int weight_paint_set_exec(bContext *C, wmOperator *op)
497 {
498         struct Scene *scene = CTX_data_scene(C);
499         Object *obact = CTX_data_active_object(C);
500         ToolSettings *ts = CTX_data_tool_settings(C);
501         Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
502         float vgroup_weight = BKE_brush_weight_get(scene, brush);
503
504         if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, NULL) == false) {
505                 return OPERATOR_CANCELLED;
506         }
507
508         if (weight_paint_set(obact, vgroup_weight)) {
509                 ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */
510                 return OPERATOR_FINISHED;
511         }
512         else {
513                 return OPERATOR_CANCELLED;
514         }
515 }
516
517 void PAINT_OT_weight_set(wmOperatorType *ot)
518 {
519         /* identifiers */
520         ot->name = "Set Weight";
521         ot->idname = "PAINT_OT_weight_set";
522         ot->description = "Fill the active vertex group with the current paint weight";
523
524         /* api callbacks */
525         ot->exec = weight_paint_set_exec;
526         ot->poll = mask_paint_poll;
527
528         /* flags */
529         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
530 }
531
532 /** \} */
533
534 /* -------------------------------------------------------------------- */
535 /** \name Interactive Weight Gradient Operator
536  * \{ */
537
538 /* *** VGroups Gradient *** */
539 typedef struct WPGradient_vertStore {
540         float sco[2];
541         float weight_orig;
542         enum {
543                 VGRAD_STORE_NOP      = 0,
544                 VGRAD_STORE_DW_EXIST = (1 << 0),
545         } flag;
546 } WPGradient_vertStore;
547
548 typedef struct WPGradient_vertStoreBase {
549         struct WPaintPrev wpp;
550         WPGradient_vertStore elem[0];
551 } WPGradient_vertStoreBase;
552
553 typedef struct WPGradient_userData {
554         struct ARegion *ar;
555         Scene *scene;
556         Mesh *me;
557         Brush *brush;
558         const float *sco_start;     /* [2] */
559         const float *sco_end;       /* [2] */
560         float        sco_line_div;  /* store (1.0f / len_v2v2(sco_start, sco_end)) */
561         int def_nr;
562         bool is_init;
563         WPGradient_vertStoreBase *vert_cache;
564         /* only for init */
565         BLI_bitmap *vert_visit;
566
567         /* options */
568         short use_select;
569         short type;
570         float weightpaint;
571 } WPGradient_userData;
572
573 static void gradientVert_update(WPGradient_userData *grad_data, int index)
574 {
575         Mesh *me = grad_data->me;
576         WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
577         float alpha;
578
579         if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) {
580                 alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end);
581         }
582         else {
583                 BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL);
584                 alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div;
585         }
586         /* no need to clamp 'alpha' yet */
587
588         /* adjust weight */
589         alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f);
590
591         if (alpha != 0.0f) {
592                 MDeformVert *dv = &me->dvert[index];
593                 MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr);
594                 // dw->weight = alpha; // testing
595                 int tool = grad_data->brush->blend;
596                 float testw;
597
598                 /* init if we just added */
599                 testw = ED_wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha);
600                 CLAMP(testw, 0.0f, 1.0f);
601                 dw->weight = testw;
602         }
603         else {
604                 MDeformVert *dv = &me->dvert[index];
605                 if (vs->flag & VGRAD_STORE_DW_EXIST) {
606                         /* normally we NULL check, but in this case we know it exists */
607                         MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
608                         dw->weight = vs->weight_orig;
609                 }
610                 else {
611                         /* wasn't originally existing, remove */
612                         MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
613                         if (dw) {
614                                 defvert_remove_group(dv, dw);
615                         }
616                 }
617         }
618 }
619
620 static void gradientVertUpdate__mapFunc(
621         void *userData, int index, const float UNUSED(co[3]),
622         const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
623 {
624         WPGradient_userData *grad_data = userData;
625         WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
626
627         if (vs->sco[0] == FLT_MAX) {
628                 return;
629         }
630
631         gradientVert_update(grad_data, index);
632 }
633
634 static void gradientVertInit__mapFunc(
635         void *userData, int index, const float co[3],
636         const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
637 {
638         WPGradient_userData *grad_data = userData;
639         Mesh *me = grad_data->me;
640         WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
641
642         if (grad_data->use_select && !(me->mvert[index].flag & SELECT)) {
643                 copy_v2_fl(vs->sco, FLT_MAX);
644                 return;
645         }
646
647         /* run first pass only,
648          * the screen coords of the verts need to be cached because
649          * updating the mesh may move them about (entering feedback loop) */
650         if (BLI_BITMAP_TEST(grad_data->vert_visit, index)) {
651                 copy_v2_fl(vs->sco, FLT_MAX);
652                 return;
653         }
654
655         if (ED_view3d_project_float_object(
656                     grad_data->ar,
657                     co, vs->sco,
658                     V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK)
659         {
660                 return;
661         }
662
663         MDeformVert *dv = &me->dvert[index];
664         const MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
665         if (dw) {
666                 vs->weight_orig = dw->weight;
667                 vs->flag = VGRAD_STORE_DW_EXIST;
668         }
669         else {
670                 vs->weight_orig = 0.0f;
671                 vs->flag = VGRAD_STORE_NOP;
672         }
673         BLI_BITMAP_ENABLE(grad_data->vert_visit, index);
674         gradientVert_update(grad_data, index);
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                 if (vert_cache != NULL) {
695                         Mesh *me = ob->data;
696                         if (vert_cache->wpp.wpaint_prev) {
697                                 BKE_defvert_array_free_elems(me->dvert, me->totvert);
698                                 BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert);
699                                 wpaint_prev_destroy(&vert_cache->wpp);
700                         }
701                         MEM_freeN(vert_cache);
702                 }
703
704                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
705                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
706         }
707         else if (ret & OPERATOR_FINISHED) {
708                 wpaint_prev_destroy(&vert_cache->wpp);
709                 MEM_freeN(vert_cache);
710         }
711
712         return ret;
713 }
714
715 static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
716 {
717         wmGesture *gesture = op->customdata;
718         WPGradient_vertStoreBase *vert_cache;
719         struct ARegion *ar = CTX_wm_region(C);
720         Scene *scene = CTX_data_scene(C);
721         Object *ob = CTX_data_active_object(C);
722         Mesh *me = ob->data;
723         int x_start = RNA_int_get(op->ptr, "xstart");
724         int y_start = RNA_int_get(op->ptr, "ystart");
725         int x_end = RNA_int_get(op->ptr, "xend");
726         int y_end = RNA_int_get(op->ptr, "yend");
727         float sco_start[2] = {x_start, y_start};
728         float sco_end[2] = {x_end, y_end};
729         const bool is_interactive = (gesture != NULL);
730
731         Depsgraph *depsgraph = CTX_data_depsgraph(C);
732
733         WPGradient_userData data = {NULL};
734
735         if (is_interactive) {
736                 if (gesture->userdata == NULL) {
737                         gesture->userdata = MEM_mallocN(
738                                 sizeof(WPGradient_vertStoreBase) +
739                                 (sizeof(WPGradient_vertStore) * me->totvert),
740                                 __func__);
741                         gesture->userdata_free = false;
742                         data.is_init = true;
743
744                         wpaint_prev_create(&((WPGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert);
745
746                         /* on init only, convert face -> vert sel  */
747                         if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
748                                 BKE_mesh_flush_select_from_polys(me);
749                         }
750                 }
751
752                 vert_cache = gesture->userdata;
753         }
754         else {
755                 if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) {
756                         return OPERATOR_CANCELLED;
757                 }
758
759                 data.is_init = true;
760                 vert_cache = MEM_mallocN(
761                         sizeof(WPGradient_vertStoreBase) +
762                         (sizeof(WPGradient_vertStore) * me->totvert),
763                         __func__);
764         }
765
766         data.ar = ar;
767         data.scene = scene;
768         data.me = ob->data;
769         data.sco_start = sco_start;
770         data.sco_end   = sco_end;
771         data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end);
772         data.def_nr = ob->actdef - 1;
773         data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL));
774         data.vert_cache = vert_cache;
775         data.vert_visit = NULL;
776         data.type = RNA_enum_get(op->ptr, "type");
777
778         {
779                 ToolSettings *ts = CTX_data_tool_settings(C);
780                 VPaint *wp = ts->wpaint;
781                 struct Brush *brush = BKE_paint_brush(&wp->paint);
782
783                 curvemapping_initialize(brush->curve);
784
785                 data.brush = brush;
786                 data.weightpaint = BKE_brush_weight_get(scene, brush);
787         }
788
789         ED_view3d_init_mats_rv3d(ob, ar->regiondata);
790
791         Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
792         Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
793
794         Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, scene->customdata_mask | CD_MASK_ORIGINDEX);
795         if (data.is_init) {
796                 data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__);
797
798                 BKE_mesh_foreach_mapped_vert(me_eval, gradientVertInit__mapFunc, &data, MESH_FOREACH_NOP);
799
800                 MEM_freeN(data.vert_visit);
801                 data.vert_visit = NULL;
802         }
803         else {
804                 BKE_mesh_foreach_mapped_vert(me_eval, gradientVertUpdate__mapFunc, &data, MESH_FOREACH_NOP);
805         }
806
807         DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
808         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
809
810         if (is_interactive == false) {
811                 MEM_freeN(vert_cache);
812         }
813
814         return OPERATOR_FINISHED;
815 }
816
817 static int paint_weight_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event)
818 {
819         int ret;
820
821         if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) {
822                 return OPERATOR_CANCELLED;
823         }
824
825         ret = WM_gesture_straightline_invoke(C, op, event);
826         if (ret & OPERATOR_RUNNING_MODAL) {
827                 struct ARegion *ar = CTX_wm_region(C);
828                 if (ar->regiontype == RGN_TYPE_WINDOW) {
829                         /* TODO, hardcoded, extend WM_gesture_straightline_ */
830                         if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
831                                 wmGesture *gesture = op->customdata;
832                                 gesture->is_active = true;
833                         }
834                 }
835         }
836         return ret;
837 }
838
839 void PAINT_OT_weight_gradient(wmOperatorType *ot)
840 {
841         /* defined in DNA_space_types.h */
842         static const EnumPropertyItem gradient_types[] = {
843                 {WPAINT_GRADIENT_TYPE_LINEAR, "LINEAR", 0, "Linear", ""},
844                 {WPAINT_GRADIENT_TYPE_RADIAL, "RADIAL", 0, "Radial", ""},
845                 {0, NULL, 0, NULL, NULL}
846         };
847
848         PropertyRNA *prop;
849
850         /* identifiers */
851         ot->name = "Weight Gradient";
852         ot->idname = "PAINT_OT_weight_gradient";
853         ot->description = "Draw a line to apply a weight gradient to selected vertices";
854
855         /* api callbacks */
856         ot->invoke = paint_weight_gradient_invoke;
857         ot->modal = paint_weight_gradient_modal;
858         ot->exec = paint_weight_gradient_exec;
859         ot->poll = weight_paint_poll_ignore_tool;
860         ot->cancel = WM_gesture_straightline_cancel;
861
862         /* flags */
863         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
864
865         prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", "");
866         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
867
868         WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
869 }
870
871 /** \} */