Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Sat, 30 Sep 2017 01:00:29 +0000 (11:00 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 30 Sep 2017 01:00:29 +0000 (11:00 +1000)
12 files changed:
1  2 
intern/cycles/blender/blender_mesh.cpp
intern/cycles/blender/blender_object.cpp
release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/blenkernel/BKE_paint.h
source/blender/blenkernel/intern/paint.c
source/blender/editors/sculpt_paint/CMakeLists.txt
source/blender/editors/sculpt_paint/paint_intern.h
source/blender/editors/sculpt_paint/paint_vertex.c
source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
source/blender/editors/sculpt_paint/paint_vertex_color_utils.c
source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
source/blender/editors/sculpt_paint/sculpt.c

index 635f1d2eb46da2b465eccd46594387a65d8d2fe0,63138c060fb3b5ac1adc5ad01b4ec48c975a8cb1..02cb4dbbe8a108e73bdcc18542f1962a385cc457
@@@ -286,8 -285,31 +303,31 @@@ Object *BlenderSync::sync_object(BL::De
                return NULL;
        }
  
+       /* Visibility flags for both parent and child. */
+       bool use_holdout = (layer_flag & render_layer.holdout_layer) != 0;
+       uint visibility = object_ray_visibility(b_ob) & PATH_RAY_ALL_VISIBILITY;
+       if(b_parent.ptr.data != b_ob.ptr.data) {
+               visibility &= object_ray_visibility(b_parent);
+       }
+       /* Make holdout objects on excluded layer invisible for non-camera rays. */
+       if(use_holdout && (layer_flag & render_layer.exclude_layer)) {
+               visibility &= ~(PATH_RAY_ALL_VISIBILITY - PATH_RAY_CAMERA);
+       }
+       /* Hide objects not on render layer from camera rays. */
+       if(!(layer_flag & render_layer.layer)) {
+               visibility &= ~PATH_RAY_CAMERA;
+       }
+       /* Don't export completely invisible objects. */
+       if(visibility == 0) {
+               return NULL;
+       }
        /* key to lookup object */
 -      ObjectKey key(b_parent, persistent_id, b_ob);
 +      ObjectKey key(b_parent, persistent_id, b_ob_instance);
        Object *object;
  
        /* motion vector case */
        if(object_map.sync(&object, b_ob, b_parent, key))
                object_updated = true;
        
-       bool use_holdout = (layer_flag & render_layer.holdout_layer) != 0;
-       
        /* mesh sync */
 -      object->mesh = sync_mesh(b_ob, object_updated, hide_tris);
 +      object->mesh = sync_mesh(b_ob, b_ob_instance, object_updated, hide_tris);
  
        /* special case not tracked by object update flags */
  
Simple merge
index c3fb67816dac22782cc90235e8e1f6bc22646b51,4d6222a4301539b44ac22b7c627a65dba9493e14..9775c136586ed658e5b5bf10ff106ba539ca9375
  
  #include "RNA_access.h"
  #include "RNA_define.h"
- #include "RNA_enum_types.h"
  
- #include "BKE_DerivedMesh.h"
- #include "BKE_action.h"
  #include "BKE_brush.h"
  #include "BKE_context.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_deform.h"
  #include "BKE_mesh.h"
  #include "BKE_mesh_mapping.h"
  #include "BKE_object_deform.h"
  #include "BKE_paint.h"
  #include "BKE_report.h"
- #include "BKE_colortools.h"
  #include "BKE_subsurf.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "WM_api.h"
  #include "WM_types.h"
  
@@@ -4309,388 -2861,3 +2884,4 @@@ void PAINT_OT_vertex_paint(wmOperatorTy
  
        paint_stroke_operator_properties(ot);
  }
- /* ********************** weight from bones operator ******************* */
- static int weight_from_bones_poll(bContext *C)
- {
-       Object *ob = CTX_data_active_object(C);
-       return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob));
- }
- static int weight_from_bones_exec(bContext *C, wmOperator *op)
- {
-       Scene *scene = CTX_data_scene(C);
-       Object *ob = CTX_data_active_object(C);
-       Object *armob = modifiers_isDeformedByArmature(ob);
-       Mesh *me = ob->data;
-       int type = RNA_enum_get(op->ptr, "type");
-       EvaluationContext eval_ctx;
-       CTX_data_eval_ctx(C, &eval_ctx);
-       create_vgroups_from_armature(op->reports, &eval_ctx, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X));
-       DEG_id_tag_update(&me->id, 0);
-       WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
-       return OPERATOR_FINISHED;
- }
- void PAINT_OT_weight_from_bones(wmOperatorType *ot)
- {
-       static EnumPropertyItem type_items[] = {
-               {ARM_GROUPS_AUTO, "AUTOMATIC", 0, "Automatic", "Automatic weights from bones"},
-               {ARM_GROUPS_ENVELOPE, "ENVELOPES", 0, "From Envelopes", "Weights from envelopes with user defined radius"},
-               {0, NULL, 0, NULL, NULL}};
-       /* identifiers */
-       ot->name = "Weight from Bones";
-       ot->idname = "PAINT_OT_weight_from_bones";
-       ot->description = "Set the weights of the groups matching the attached armature's selected bones, "
-                         "using the distance between the vertices and the bones";
-       
-       /* api callbacks */
-       ot->exec = weight_from_bones_exec;
-       ot->invoke = WM_menu_invoke;
-       ot->poll = weight_from_bones_poll;
-       
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-       /* properties */
-       ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights");
- }
- /* *** VGroups Gradient *** */
- typedef struct DMGradient_vertStore {
-       float sco[2];
-       float weight_orig;
-       enum {
-               VGRAD_STORE_NOP      = 0,
-               VGRAD_STORE_DW_EXIST = (1 << 0)
-       } flag;
- } DMGradient_vertStore;
- typedef struct DMGradient_vertStoreBase {
-       struct WPaintPrev wpp;
-       DMGradient_vertStore elem[0];
- } DMGradient_vertStoreBase;
- typedef struct DMGradient_userData {
-       struct ARegion *ar;
-       Scene *scene;
-       Mesh *me;
-       Brush *brush;
-       const float *sco_start;     /* [2] */
-       const float *sco_end;       /* [2] */
-       float        sco_line_div;  /* store (1.0f / len_v2v2(sco_start, sco_end)) */
-       int def_nr;
-       bool is_init;
-       DMGradient_vertStoreBase *vert_cache;
-       /* only for init */
-       BLI_bitmap *vert_visit;
-       /* options */
-       short use_select;
-       short type;
-       float weightpaint;
- } DMGradient_userData;
- static void gradientVert_update(DMGradient_userData *grad_data, int index)
- {
-       Mesh *me = grad_data->me;
-       DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
-       float alpha;
-       if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) {
-               alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end);
-       }
-       else {
-               BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL);
-               alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div;
-       }
-       /* no need to clamp 'alpha' yet */
-       /* adjust weight */
-       alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f);
-       if (alpha != 0.0f) {
-               MDeformVert *dv = &me->dvert[index];
-               MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr);
-               // dw->weight = alpha; // testing
-               int tool = grad_data->brush->vertexpaint_tool;
-               float testw;
-               /* init if we just added */
-               testw = wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha);
-               CLAMP(testw, 0.0f, 1.0f);
-               dw->weight = testw;
-       }
-       else {
-               MDeformVert *dv = &me->dvert[index];
-               if (vs->flag & VGRAD_STORE_DW_EXIST) {
-                       /* normally we NULL check, but in this case we know it exists */
-                       MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
-                       dw->weight = vs->weight_orig;
-               }
-               else {
-                       /* wasn't originally existing, remove */
-                       MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
-                       if (dw) {
-                               defvert_remove_group(dv, dw);
-                       }
-               }
-       }
- }
- static void gradientVertUpdate__mapFunc(
-         void *userData, int index, const float UNUSED(co[3]),
-         const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
- {
-       DMGradient_userData *grad_data = userData;
-       Mesh *me = grad_data->me;
-       if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
-               DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
-               if (vs->sco[0] != FLT_MAX) {
-                       gradientVert_update(grad_data, index);
-               }
-       }
- }
- static void gradientVertInit__mapFunc(
-         void *userData, int index, const float co[3],
-         const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
- {
-       DMGradient_userData *grad_data = userData;
-       Mesh *me = grad_data->me;
-       if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
-               /* run first pass only,
-                * the screen coords of the verts need to be cached because
-                * updating the mesh may move them about (entering feedback loop) */
-               if (BLI_BITMAP_TEST(grad_data->vert_visit, index) == 0) {
-                       DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
-                       if (ED_view3d_project_float_object(grad_data->ar,
-                                                          co, vs->sco,
-                                                          V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK)
-                       {
-                               /* ok */
-                               MDeformVert *dv = &me->dvert[index];
-                               const MDeformWeight *dw;
-                               dw = defvert_find_index(dv, grad_data->def_nr);
-                               if (dw) {
-                                       vs->weight_orig = dw->weight;
-                                       vs->flag = VGRAD_STORE_DW_EXIST;
-                               }
-                               else {
-                                       vs->weight_orig = 0.0f;
-                                       vs->flag = VGRAD_STORE_NOP;
-                               }
-                               BLI_BITMAP_ENABLE(grad_data->vert_visit, index);
-                               gradientVert_update(grad_data, index);
-                       }
-                       else {
-                               /* no go */
-                               copy_v2_fl(vs->sco, FLT_MAX);
-                       }
-               }
-       }
- }
- static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
- {
-       int ret = WM_gesture_straightline_modal(C, op, event);
-       wmGesture *gesture = op->customdata;
-       DMGradient_vertStoreBase *vert_cache = gesture->userdata;
-       bool do_gesture_free = false;
-       if (ret & OPERATOR_RUNNING_MODAL) {
-               if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {  /* XXX, hardcoded */
-                       /* generally crap! redo! */
-                       do_gesture_free = true;
-                       ret &= ~OPERATOR_RUNNING_MODAL;
-                       ret |= OPERATOR_FINISHED;
-               }
-       }
-       if (ret & OPERATOR_CANCELLED) {
-               Object *ob = CTX_data_active_object(C);
-               Mesh *me = ob->data;
-               if (vert_cache->wpp.wpaint_prev) {
-                       BKE_defvert_array_free_elems(me->dvert, me->totvert);
-                       BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert);
-                       wpaint_prev_destroy(&vert_cache->wpp);
-               }
-               DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
-               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
-       }
-       else if (ret & OPERATOR_FINISHED) {
-               wpaint_prev_destroy(&vert_cache->wpp);
-       }
-       if (do_gesture_free) {
-               WM_gesture_straightline_cancel(C, op);
-       }
-       return ret;
- }
- static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
- {
-       wmGesture *gesture = op->customdata;
-       DMGradient_vertStoreBase *vert_cache;
-       struct ARegion *ar = CTX_wm_region(C);
-       Scene *scene = CTX_data_scene(C);
-       Object *ob = CTX_data_active_object(C);
-       EvaluationContext eval_ctx;
-       Mesh *me = ob->data;
-       int x_start = RNA_int_get(op->ptr, "xstart");
-       int y_start = RNA_int_get(op->ptr, "ystart");
-       int x_end = RNA_int_get(op->ptr, "xend");
-       int y_end = RNA_int_get(op->ptr, "yend");
-       float sco_start[2] = {x_start, y_start};
-       float sco_end[2] = {x_end, y_end};
-       const bool is_interactive = (gesture != NULL);
-       DerivedMesh *dm;
-       CTX_data_eval_ctx(C, &eval_ctx);
-       dm = mesh_get_derived_final(&eval_ctx, scene, ob, scene->customdata_mask);
-       DMGradient_userData data = {NULL};
-       if (is_interactive) {
-               if (gesture->userdata == NULL) {
-                       gesture->userdata = MEM_mallocN(
-                               sizeof(DMGradient_vertStoreBase) +
-                               (sizeof(DMGradient_vertStore) * me->totvert),
-                               __func__);
-                       data.is_init = true;
-                       wpaint_prev_create(&((DMGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert);
-                       /* on init only, convert face -> vert sel  */
-                       if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
-                               BKE_mesh_flush_select_from_polys(me);
-                       }
-               }
-               vert_cache = gesture->userdata;
-       }
-       else {
-               if (wpaint_ensure_data(C, op, 0, NULL) == false) {
-                       return OPERATOR_CANCELLED;
-               }
-               data.is_init = true;
-               vert_cache = MEM_mallocN(
-                       sizeof(DMGradient_vertStoreBase) +
-                       (sizeof(DMGradient_vertStore) * me->totvert),
-                       __func__);
-       }
-       data.ar = ar;
-       data.scene = scene;
-       data.me = ob->data;
-       data.sco_start = sco_start;
-       data.sco_end   = sco_end;
-       data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end);
-       data.def_nr = ob->actdef - 1;
-       data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL));
-       data.vert_cache = vert_cache;
-       data.vert_visit = NULL;
-       data.type = RNA_enum_get(op->ptr, "type");
-       {
-               ToolSettings *ts = CTX_data_tool_settings(C);
-               VPaint *wp = ts->wpaint;
-               struct Brush *brush = BKE_paint_brush(&wp->paint);
-               curvemapping_initialize(brush->curve);
-               data.brush = brush;
-               data.weightpaint = BKE_brush_weight_get(scene, brush);
-       }
-       ED_view3d_init_mats_rv3d(ob, ar->regiondata);
-       if (data.is_init) {
-               data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__);
-               dm->foreachMappedVert(dm, gradientVertInit__mapFunc, &data, DM_FOREACH_NOP);
-               MEM_freeN(data.vert_visit);
-               data.vert_visit = NULL;
-       }
-       else {
-               dm->foreachMappedVert(dm, gradientVertUpdate__mapFunc, &data, DM_FOREACH_NOP);
-       }
-       DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
-       WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
-       if (is_interactive == false) {
-               MEM_freeN(vert_cache);
-       }
-       return OPERATOR_FINISHED;
- }
- static int paint_weight_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event)
- {
-       int ret;
-       if (wpaint_ensure_data(C, op, 0, NULL) == false) {
-               return OPERATOR_CANCELLED;
-       }
-       ret = WM_gesture_straightline_invoke(C, op, event);
-       if (ret & OPERATOR_RUNNING_MODAL) {
-               struct ARegion *ar = CTX_wm_region(C);
-               if (ar->regiontype == RGN_TYPE_WINDOW) {
-                       /* TODO, hardcoded, extend WM_gesture_straightline_ */
-                       if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
-                               wmGesture *gesture = op->customdata;
-                               gesture->mode = 1;
-                       }
-               }
-       }
-       return ret;
- }
- void PAINT_OT_weight_gradient(wmOperatorType *ot)
- {
-       /* defined in DNA_space_types.h */
-       static EnumPropertyItem gradient_types[] = {
-               {WPAINT_GRADIENT_TYPE_LINEAR, "LINEAR", 0, "Linear", ""},
-               {WPAINT_GRADIENT_TYPE_RADIAL, "RADIAL", 0, "Radial", ""},
-               {0, NULL, 0, NULL, NULL}
-       };
-       PropertyRNA *prop;
-       /* identifiers */
-       ot->name = "Weight Gradient";
-       ot->idname = "PAINT_OT_weight_gradient";
-       ot->description = "Draw a line to apply a weight gradient to selected vertices";
-       /* api callbacks */
-       ot->invoke = paint_weight_gradient_invoke;
-       ot->modal = paint_weight_gradient_modal;
-       ot->exec = paint_weight_gradient_exec;
-       ot->poll = weight_paint_poll;
-       ot->cancel = WM_gesture_straightline_cancel;
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-       prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", "");
-       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-       WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
- }
 +
index 3cec9dfc063dcf5b3e00c118db5fa4b6e96a559f,4acd4ddbd8d8ba955b9020bfef8f4fd6a76ecadf..32515fa81cbe933e00632ff3ff46c0faa04169ba
  #include "DNA_mesh_types.h"
  #include "DNA_meshdata_types.h"
  #include "DNA_object_types.h"
+ #include "DNA_scene_types.h"
+ #include "BLI_math_base.h"
+ #include "BLI_math_color.h"
  
  #include "BKE_context.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_mesh.h"
  #include "BKE_deform.h"
  
 +#include "DEG_depsgraph.h"
 +
+ #include "RNA_access.h"
+ #include "RNA_define.h"
  #include "WM_api.h"
  #include "WM_types.h"
  
@@@ -50,9 -56,90 +57,90 @@@ static int vertex_weight_paint_mode_pol
               (me && me->totpoly && me->dvert);
  }
  
- static bool vertex_paint_from_weight(bContext *C)
+ /* -------------------------------------------------------------------- */
+ /** \name Set Vertex Colors Operator
+  * \{ */
+ static bool vertex_color_set(Object *ob, uint paintcol)
+ {
+       Mesh *me;
+       const MPoly *mp;
+       int i, j;
+       if (((me = BKE_mesh_from_object(ob)) == NULL) ||
+           (ED_mesh_color_ensure(me, NULL) == false))
+       {
+               return false;
+       }
+       const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+       const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
+       mp = me->mpoly;
+       for (i = 0; i < me->totpoly; i++, mp++) {
+               MLoopCol *lcol = me->mloopcol + mp->loopstart;
+               if (use_face_sel && !(mp->flag & ME_FACE_SEL))
+                       continue;
+               j = 0;
+               do {
+                       uint vidx = me->mloop[mp->loopstart + j].v;
+                       if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) {
+                               *(int *)lcol = paintcol;
+                       }
+                       lcol++;
+                       j++;
+               } while (j < mp->totloop);
+       }
+       /* remove stale me->mcol, will be added later */
+       BKE_mesh_tessface_clear(me);
 -      DAG_id_tag_update(&me->id, 0);
++      DEG_id_tag_update(&me->id, 0);
+       return true;
+ }
+ static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op))
+ {
+       Scene *scene = CTX_data_scene(C);
+       Object *obact = CTX_data_active_object(C);
+       unsigned int paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint);
+       if (vertex_color_set(obact, paintcol)) {
+               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void PAINT_OT_vertex_color_set(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Set Vertex Colors";
+       ot->idname = "PAINT_OT_vertex_color_set";
+       ot->description = "Fill the active vertex color layer with the current paint color";
+       /* api callbacks */
+       ot->exec = vertex_color_set_exec;
+       ot->poll = vertex_paint_mode_poll;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ }
+ /** \} */
+ /* -------------------------------------------------------------------- */
+ /** \name Vertex Color from Weight Operator
+  * \{ */
+ static bool vertex_paint_from_weight(Object *ob)
  {
-       Object *ob = CTX_data_active_object(C);
        Mesh *me;
        const MPoly *mp;
        int vgroup_active;
                } while (j < mp->totloop);
        }
  
 -      DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +      DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
-       WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
        return true;
  }
  
@@@ -110,3 -201,374 +202,374 @@@ void PAINT_OT_vertex_color_from_weight(
  
        /* TODO: invert, alpha */
  }
 -      DAG_id_tag_update(&me->id, 0);
+ /** \} */
+ /* -------------------------------------------------------------------- */
+ /** \name Smooth Vertex Colors Operator
+  * \{ */
+ static void vertex_color_smooth_looptag(Mesh *me, bool *mlooptag)
+ {
+       const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+       const MPoly *mp;
+       int (*scol)[4];
+       int i, j;
+       bool has_shared = false;
+       /* if no mloopcol: do not do */
+       /* if mtexpoly: only the involved faces, otherwise all */
+       if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) return;
+       scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol");
+       for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
+               if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
+                       const MLoop *ml = me->mloop + mp->loopstart;
+                       MLoopCol *lcol = me->mloopcol + mp->loopstart;
+                       for (j = 0; j < mp->totloop; j++, ml++, lcol++) {
+                               scol[ml->v][0] += lcol->r;
+                               scol[ml->v][1] += lcol->g;
+                               scol[ml->v][2] += lcol->b;
+                               scol[ml->v][3] += 1;
+                               has_shared = 1;
+                       }
+               }
+       }
+       if (has_shared) {
+               for (i = 0; i < me->totvert; i++) {
+                       if (scol[i][3] != 0) {
+                               scol[i][0] = divide_round_i(scol[i][0], scol[i][3]);
+                               scol[i][1] = divide_round_i(scol[i][1], scol[i][3]);
+                               scol[i][2] = divide_round_i(scol[i][2], scol[i][3]);
+                       }
+               }
+               for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) {
+                       if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) {
+                               const MLoop *ml = me->mloop + mp->loopstart;
+                               MLoopCol *lcol = me->mloopcol + mp->loopstart;
+                               for (j = 0; j < mp->totloop; j++, ml++, lcol++) {
+                                       if (mlooptag[mp->loopstart + j]) {
+                                               lcol->r = scol[ml->v][0];
+                                               lcol->g = scol[ml->v][1];
+                                               lcol->b = scol[ml->v][2];
+                                       }
+                               }
+                       }
+               }
+       }
+       MEM_freeN(scol);
+ }
+ static bool vertex_color_smooth(Object *ob)
+ {
+       Mesh *me;
+       const MPoly *mp;
+       int i, j;
+       bool *mlooptag;
+       if (((me = BKE_mesh_from_object(ob)) == NULL) ||
+           (ED_mesh_color_ensure(me, NULL) == false))
+       {
+               return false;
+       }
+       const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+       mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag");
+       /* simply tag loops of selected faces */
+       mp = me->mpoly;
+       for (i = 0; i < me->totpoly; i++, mp++) {
+               const MLoop *ml = me->mloop + mp->loopstart;
+               int ml_index = mp->loopstart;
+               if (use_face_sel && !(mp->flag & ME_FACE_SEL))
+                       continue;
+               for (j = 0; j < mp->totloop; j++, ml_index++, ml++) {
+                       mlooptag[ml_index] = true;
+               }
+       }
+       /* remove stale me->mcol, will be added later */
+       BKE_mesh_tessface_clear(me);
+       vertex_color_smooth_looptag(me, mlooptag);
+       MEM_freeN(mlooptag);
 -/** \} */
++      DEG_id_tag_update(&me->id, 0);
+       return true;
+ }
+ static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obact = CTX_data_active_object(C);
+       if (vertex_color_smooth(obact)) {
+               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void PAINT_OT_vertex_color_smooth(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Smooth Vertex Colors";
+       ot->idname = "PAINT_OT_vertex_color_smooth";
+       ot->description = "Smooth colors across vertices";
+       /* api callbacks */
+       ot->exec = vertex_color_smooth_exec;
+       ot->poll = vertex_paint_mode_poll;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ }
+ /** \} */
+ /* -------------------------------------------------------------------- */
+ /** \name Vertex Color Transformation Operators
+  * \{ */
+ struct VPaintTx_BrightContrastData {
+       /* pre-calculated */
+       float gain;
+       float offset;
+ };
+ static void vpaint_tx_brightness_contrast(const float col[3], const void *user_data, float r_col[3])
+ {
+       const struct VPaintTx_BrightContrastData *data = user_data;
+       for (int i = 0; i < 3; i++) {
+               r_col[i] = data->gain * col[i] + data->offset;
+       }
+ }
+ static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
+ {
+       Object *obact = CTX_data_active_object(C);
+       float gain, offset;
+       {
+               float brightness = RNA_float_get(op->ptr, "brightness");
+               float contrast = RNA_float_get(op->ptr, "contrast");
+               brightness /= 100.0f;
+               float delta = contrast / 200.0f;
+               gain = 1.0f - delta * 2.0f;
+               /*
+                * The algorithm is by Werner D. Streidt
+                * (http://visca.com/ffactory/archives/5-99/msg00021.html)
+                * Extracted of OpenCV demhist.c
+                */
+               if (contrast > 0) {
+                       gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
+                       offset = gain * (brightness - delta);
+               }
+               else {
+                       delta *= -1;
+                       offset = gain * (brightness + delta);
+               }
+       }
+       const struct VPaintTx_BrightContrastData user_data = {
+               .gain = gain,
+               .offset = offset,
+       };
+       if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) {
+               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
+ {
+       PropertyRNA *prop;
+       /* identifiers */
+       ot->name = "Vertex Paint Bright/Contrast";
+       ot->idname = "PAINT_OT_vertex_color_brightness_contrast";
+       ot->description = "Adjust vertex color brightness/contrast";
+       /* api callbacks */
+       ot->exec = vertex_color_brightness_contrast_exec;
+       ot->poll = vertex_paint_mode_poll;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+       /* params */
+       const float min = -100, max = +100;
+       prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
+       prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
+       RNA_def_property_ui_range(prop, min, max, 1, 1);
+ }
+ struct VPaintTx_HueSatData {
+       float hue;
+       float sat;
+       float val;
+ };
+ static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3])
+ {
+       const struct VPaintTx_HueSatData *data = user_data;
+       float hsv[3];
+       rgb_to_hsv_v(col, hsv);
+       hsv[0] += (data->hue - 0.5f);
+       if (hsv[0] > 1.0f) {
+               hsv[0] -= 1.0f;
+       }
+       else if (hsv[0] < 0.0f) {
+               hsv[0] += 1.0f;
+       }
+       hsv[1] *= data->sat;
+       hsv[2] *= data->val;
+       hsv_to_rgb_v(hsv, r_col);
+ }
+ static int vertex_color_hsv_exec(bContext *C, wmOperator *op)
+ {
+       Object *obact = CTX_data_active_object(C);
+       const struct VPaintTx_HueSatData user_data = {
+               .hue = RNA_float_get(op->ptr, "h"),
+               .sat = RNA_float_get(op->ptr, "s"),
+               .val = RNA_float_get(op->ptr, "v"),
+       };
+       if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) {
+               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void PAINT_OT_vertex_color_hsv(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Vertex Paint Hue Saturation Value";
+       ot->idname = "PAINT_OT_vertex_color_hsv";
+       ot->description = "Adjust vertex color HSV values";
+       /* api callbacks */
+       ot->exec = vertex_color_hsv_exec;
+       ot->poll = vertex_paint_mode_poll;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+       /* params */
+       RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
+       RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
+       RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
+ }
+ static void vpaint_tx_invert(const float col[3], const void *UNUSED(user_data), float r_col[3])
+ {
+       for (int i = 0; i < 3; i++) {
+               r_col[i] = 1.0f - col[i];
+       }
+ }
+ static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op))
+ {
+       Object *obact = CTX_data_active_object(C);
+       if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) {
+               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void PAINT_OT_vertex_color_invert(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Vertex Paint Invert";
+       ot->idname = "PAINT_OT_vertex_color_invert";
+       ot->description = "Invert RGB values";
+       /* api callbacks */
+       ot->exec = vertex_color_invert_exec;
+       ot->poll = vertex_paint_mode_poll;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ }
+ struct VPaintTx_LevelsData {
+       float gain;
+       float offset;
+ };
+ static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3])
+ {
+       const struct VPaintTx_LevelsData *data = user_data;
+       for (int i = 0; i < 3; i++) {
+               r_col[i] = data->gain * (col[i] + data->offset);
+       }
+ }
+ static int vertex_color_levels_exec(bContext *C, wmOperator *op)
+ {
+       Object *obact = CTX_data_active_object(C);
+       const struct VPaintTx_LevelsData user_data = {
+               .gain = RNA_float_get(op->ptr, "gain"),
+               .offset = RNA_float_get(op->ptr, "offset"),
+       };
+       if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) {
+               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact);
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void PAINT_OT_vertex_color_levels(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Vertex Paint Levels";
+       ot->idname = "PAINT_OT_vertex_color_levels";
+       ot->description = "Adjust levels of vertex colors";
+       /* api callbacks */
+       ot->exec = vertex_color_levels_exec;
+       ot->poll = vertex_paint_mode_poll;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+       /* params */
+       RNA_def_float(ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
+       RNA_def_float(ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
+ }
++/** \} */
index 0000000000000000000000000000000000000000,a2ba8818c1f9dd7e94c54317339b8fc83402a8f3..a3da5a76deced2c95c78243b8fa2e32be7a86707
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,648 +1,649 @@@
 -#include "BKE_depsgraph.h"
+ /*
+  * ***** BEGIN GPL LICENSE BLOCK *****
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+ /** \file blender/editors/sculpt_paint/paint_vertex_color_utils.c
+  *  \ingroup edsculpt
+  *
+  * Intended for use by `paint_vertex.c` & `paint_vertex_color_ops.c`.
+  */
+ #include "MEM_guardedalloc.h"
+ #include "DNA_brush_types.h"
+ #include "DNA_mesh_types.h"
+ #include "DNA_meshdata_types.h"
+ #include "DNA_object_types.h"
+ #include "DNA_scene_types.h"
+ #include "BLI_math_base.h"
+ #include "BLI_math_color.h"
+ #include "IMB_colormanagement.h"
+ #include "BKE_context.h"
 -      DAG_id_tag_update(&me->id, 0);
+ #include "BKE_mesh.h"
++#include "DEG_depsgraph.h"
++
+ #include "ED_mesh.h"
+ #include "paint_intern.h"  /* own include */
+ #define EPS_SATURATION 0.0005f
+ /**
+  * Apply callback to each vertex of the active vertex color layer.
+  */
+ bool ED_vpaint_color_transform(
+         struct Object *ob,
+         VPaintTransform_Callback vpaint_tx_fn,
+         const void *user_data)
+ {
+       Mesh *me;
+       const MPoly *mp;
+       if (((me = BKE_mesh_from_object(ob)) == NULL) ||
+           (ED_mesh_color_ensure(me, NULL) == false))
+       {
+               return false;
+       }
+       const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
+       mp = me->mpoly;
+       for (int i = 0; i < me->totpoly; i++, mp++) {
+               MLoopCol *lcol = &me->mloopcol[mp->loopstart];
+               if (use_face_sel && !(mp->flag & ME_FACE_SEL)) {
+                       continue;
+               }
+               for (int j = 0; j < mp->totloop; j++, lcol++) {
+                       float col[3];
+                       rgb_uchar_to_float(col, &lcol->r);
+                       vpaint_tx_fn(col, user_data, col);
+                       rgb_float_to_uchar(&lcol->r, col);
+               }
+       }
+       /* remove stale me->mcol, will be added later */
+       BKE_mesh_tessface_clear(me);
++      DEG_id_tag_update(&me->id, 0);
+       return true;
+ }
+ /* -------------------------------------------------------------------- */
+ /** \name Color Blending Modes
+  * \{ */
+ BLI_INLINE uint mcol_blend(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       if (fac >= 255) {
+               return col2;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
+       /* Updated to use the rgb squared color model which blends nicer. */
+       int r1 = cp1[0] * cp1[0];
+       int g1 = cp1[1] * cp1[1];
+       int b1 = cp1[2] * cp1[2];
+       int a1 = cp1[3] * cp1[3];
+       int r2 = cp2[0] * cp2[0];
+       int g2 = cp2[1] * cp2[1];
+       int b2 = cp2[2] * cp2[2];
+       int a2 = cp2[3] * cp2[3];
+       cp[0] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * r1 + fac * r2), 255)));
+       cp[1] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * g1 + fac * g2), 255)));
+       cp[2] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * b1 + fac * b2), 255)));
+       cp[3] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * a1 + fac * a2), 255)));
+       return col;
+ }
+ BLI_INLINE uint mcol_add(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
+       temp = cp1[0] + divide_round_i((fac * cp2[0]), 255);
+       cp[0] = (temp > 254) ? 255 : temp;
+       temp = cp1[1] + divide_round_i((fac * cp2[1]), 255);
+       cp[1] = (temp > 254) ? 255 : temp;
+       temp = cp1[2] + divide_round_i((fac * cp2[2]), 255);
+       cp[2] = (temp > 254) ? 255 : temp;
+       temp = cp1[3] + divide_round_i((fac * cp2[3]), 255);
+       cp[3] = (temp > 254) ? 255 : temp;
+       return col;
+ }
+ BLI_INLINE uint mcol_sub(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
+       temp = cp1[0] - divide_round_i((fac * cp2[0]), 255);
+       cp[0] = (temp < 0) ? 0 : temp;
+       temp = cp1[1] - divide_round_i((fac * cp2[1]), 255);
+       cp[1] = (temp < 0) ? 0 : temp;
+       temp = cp1[2] - divide_round_i((fac * cp2[2]), 255);
+       cp[2] = (temp < 0) ? 0 : temp;
+       temp = cp1[3] - divide_round_i((fac * cp2[3]), 255);
+       cp[3] = (temp < 0) ? 0 : temp;
+       return col;
+ }
+ BLI_INLINE uint mcol_mul(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
+       /* first mul, then blend the fac */
+       cp[0] = divide_round_i(mfac * cp1[0] * 255 + fac * cp2[0] * cp1[0], 255 * 255);
+       cp[1] = divide_round_i(mfac * cp1[1] * 255 + fac * cp2[1] * cp1[1], 255 * 255);
+       cp[2] = divide_round_i(mfac * cp1[2] * 255 + fac * cp2[2] * cp1[2], 255 * 255);
+       cp[3] = divide_round_i(mfac * cp1[3] * 255 + fac * cp2[3] * cp1[3], 255 * 255);
+       return col;
+ }
+ BLI_INLINE uint mcol_lighten(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       else if (fac >= 255) {
+               return col2;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
+       /* See if are lighter, if so mix, else don't do anything.
+        * if the paint col is darker then the original, then ignore */
+       if (IMB_colormanagement_get_luminance_byte(cp1) > IMB_colormanagement_get_luminance_byte(cp2)) {
+               return col1;
+       }
+       cp[0] = divide_round_i(mfac * cp1[0] + fac * cp2[0], 255);
+       cp[1] = divide_round_i(mfac * cp1[1] + fac * cp2[1], 255);
+       cp[2] = divide_round_i(mfac * cp1[2] + fac * cp2[2], 255);
+       cp[3] = divide_round_i(mfac * cp1[3] + fac * cp2[3], 255);
+       return col;
+ }
+ BLI_INLINE uint mcol_darken(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       else if (fac >= 255) {
+               return col2;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp  = (uchar *)&col;
+       /* See if were darker, if so mix, else don't do anything.
+        * if the paint col is brighter then the original, then ignore */
+       if (IMB_colormanagement_get_luminance_byte(cp1) < IMB_colormanagement_get_luminance_byte(cp2)) {
+               return col1;
+       }
+       cp[0] = divide_round_i((mfac * cp1[0] + fac * cp2[0]), 255);
+       cp[1] = divide_round_i((mfac * cp1[1] + fac * cp2[1]), 255);
+       cp[2] = divide_round_i((mfac * cp1[2] + fac * cp2[2]), 255);
+       cp[3] = divide_round_i((mfac * cp1[3] + fac * cp2[3]), 255);
+       return col;
+ }
+ BLI_INLINE uint mcol_colordodge(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac,temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       temp = (cp2[0] == 255) ? 255 : min_ii((cp1[0] * 225) / (255 - cp2[0]), 255);
+       cp[0] = (mfac * cp1[0] + temp * fac) / 255;
+       temp = (cp2[1] == 255) ? 255 : min_ii((cp1[1] * 225) / (255 - cp2[1]), 255);
+       cp[1] = (mfac * cp1[1] + temp * fac) / 255;
+       temp = (cp2[2] == 255) ? 255 : min_ii((cp1[2] * 225 )/ (255 - cp2[2]), 255);
+       cp[2] = (mfac * cp1[2] + temp * fac) / 255;
+       temp = (cp2[3] == 255) ? 255 : min_ii((cp1[3] * 225) / (255 - cp2[3]), 255);
+       cp[3] = (mfac * cp1[3] + temp * fac) / 255;
+       return col;
+ }
+ BLI_INLINE uint mcol_difference(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac, temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       temp = abs(cp1[0] - cp2[0]);
+       cp[0] = (mfac * cp1[0] + temp * fac) / 255;
+       temp = abs(cp1[1] - cp2[1]);
+       cp[1] = (mfac * cp1[1] + temp * fac) / 255;
+       temp = abs(cp1[2] - cp2[2]);
+       cp[2] = (mfac * cp1[2] + temp * fac) / 255;
+       temp = abs(cp1[3] - cp2[3]);
+       cp[3] = (mfac * cp1[3] + temp * fac) / 255;
+       return col;
+ }
+ BLI_INLINE uint mcol_screen(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac, temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       temp = max_ii(255 - (((255 - cp1[0]) * (255 - cp2[0])) / 255), 0);
+       cp[0] = (mfac * cp1[0] + temp * fac) / 255;
+       temp = max_ii(255 - (((255 - cp1[1]) * (255 - cp2[1])) / 255), 0);
+       cp[1] = (mfac * cp1[1] + temp * fac) / 255;
+       temp = max_ii(255 - (((255 - cp1[2]) * (255 - cp2[2])) / 255), 0);
+       cp[2] = (mfac * cp1[2] + temp * fac) / 255;
+       temp = max_ii(255 - (((255 - cp1[3]) * (255 - cp2[3])) / 255), 0);
+       cp[3] = (mfac * cp1[3] + temp * fac) / 255;
+       return col;
+ }
+ BLI_INLINE uint mcol_hardlight(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac, temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       int i = 0;
+       for (i = 0; i < 4; i++) {
+               if (cp2[i] > 127) {
+                       temp = 255 - ((255 - 2 * (cp2[i] - 127)) * (255 - cp1[i]) / 255);
+               }
+               else {
+                       temp = (2 * cp2[i] * cp1[i]) >> 8;
+               }
+               cp[i] = min_ii((mfac * cp1[i] + temp * fac) / 255, 255);
+       }
+       return col;
+ }
+ BLI_INLINE uint mcol_overlay(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac, temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       int i = 0;
+       for (i = 0; i < 4; i++) {
+               if (cp1[i] > 127) {
+                       temp = 255 - ((255 - 2 * (cp1[i] - 127)) * (255 - cp2[i]) / 255);
+               }
+               else {
+                       temp = (2 * cp2[i] * cp1[i]) >> 8;
+               }
+               cp[i] = min_ii((mfac * cp1[i] + temp * fac) / 255, 255);
+       }
+       return col;
+ }
+ BLI_INLINE uint mcol_softlight(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac, temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       int i = 0;
+       for (i = 0; i < 4; i++) {
+               if (cp1[i] < 127) {
+                       temp = ((2 * ((cp2[i] / 2) + 64)) * cp1[i]) / 255;
+               }
+               else {
+                       temp = 255 - (2 * (255 - ((cp2[i] / 2) + 64)) * (255 - cp1[i]) / 255);
+               }
+               cp[i] = (temp * fac + cp1[i] * mfac) / 255;
+       }
+       return col;
+ }
+ BLI_INLINE uint mcol_exclusion(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac, temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       int i = 0;
+       for (i = 0; i < 4; i++) {
+               temp = 127 - ((2 * (cp1[i] - 127) * (cp2[i] - 127)) / 255);
+               cp[i] = (temp * fac + cp1[i] * mfac) / 255;
+       }
+       return col;
+ }
+ BLI_INLINE uint mcol_luminosity(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       float h1, s1, v1;
+       float h2, s2, v2;
+       float r, g, b;
+       rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1);
+       rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2);
+       v1 = v2;
+       hsv_to_rgb(h1, s1, v1, &r, &g, &b);
+       cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255;
+       cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255;
+       cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255;
+       cp[3] = ((int)(cp2[3])     * fac + mfac * cp1[3]) / 255;
+       return col;
+ }
+ BLI_INLINE uint mcol_saturation(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       float h1, s1, v1;
+       float h2, s2, v2;
+       float r, g, b;
+       rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1);
+       rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2);
+       if (s1 > EPS_SATURATION) {
+               s1 = s2;
+       }
+       hsv_to_rgb(h1, s1, v1, &r, &g, &b);
+       cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255;
+       cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255;
+       cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255;
+       return col;
+ }
+ BLI_INLINE uint mcol_hue(uint col1, uint col2, int fac)
+ {
+       uchar *cp1, *cp2, *cp;
+       int mfac;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       mfac = 255 - fac;
+       cp1 = (uchar *)&col1;
+       cp2 = (uchar *)&col2;
+       cp = (uchar *)&col;
+       float h1, s1, v1;
+       float h2, s2, v2;
+       float r, g, b;
+       rgb_to_hsv(cp1[0] / 255.0f, cp1[1] / 255.0f, cp1[2] / 255.0f, &h1, &s1, &v1);
+       rgb_to_hsv(cp2[0] / 255.0f, cp2[1] / 255.0f, cp2[2] / 255.0f, &h2, &s2, &v2);
+       h1 = h2;
+       hsv_to_rgb(h1, s1, v1, &r, &g, &b);
+       cp[0] = ((int)(r * 255.0f) * fac + mfac * cp1[0]) / 255;
+       cp[1] = ((int)(g * 255.0f) * fac + mfac * cp1[1]) / 255;
+       cp[2] = ((int)(b * 255.0f) * fac + mfac * cp1[2]) / 255;
+       cp[3] = ((int)(cp2[3])     * fac + mfac * cp1[3]) / 255;
+       return col;
+ }
+ BLI_INLINE uint mcol_alpha_add(uint col1, int fac)
+ {
+       uchar *cp1, *cp;
+       int temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       cp1 = (uchar *)&col1;
+       cp  = (uchar *)&col;
+       temp = cp1[3] + fac;
+       cp[3] = (temp > 254) ? 255 : temp;
+       return col;
+ }
+ BLI_INLINE uint mcol_alpha_sub(uint col1, int fac)
+ {
+       uchar *cp1, *cp;
+       int temp;
+       uint col = 0;
+       if (fac == 0) {
+               return col1;
+       }
+       cp1 = (uchar *)&col1;
+       cp  = (uchar *)&col;
+       temp = cp1[3] - fac;
+       cp[3] = temp < 0 ? 0 : temp;
+       return col;
+ }
+ /* wpaint has 'ED_wpaint_blend_tool' */
+ uint ED_vpaint_blend_tool(
+         const int tool, const uint col,
+         const uint paintcol, const int alpha_i)
+ {
+       switch (tool) {
+               case PAINT_BLEND_MIX:
+               case PAINT_BLEND_BLUR:       return mcol_blend(col, paintcol, alpha_i);
+               case PAINT_BLEND_AVERAGE:    return mcol_blend(col, paintcol, alpha_i);
+               case PAINT_BLEND_SMEAR:      return mcol_blend(col, paintcol, alpha_i);
+               case PAINT_BLEND_ADD:        return mcol_add(col, paintcol, alpha_i);
+               case PAINT_BLEND_SUB:        return mcol_sub(col, paintcol, alpha_i);
+               case PAINT_BLEND_MUL:        return mcol_mul(col, paintcol, alpha_i);
+               case PAINT_BLEND_LIGHTEN:    return mcol_lighten(col, paintcol, alpha_i);
+               case PAINT_BLEND_DARKEN:     return mcol_darken(col, paintcol, alpha_i);
+               case PAINT_BLEND_COLORDODGE: return mcol_colordodge(col, paintcol, alpha_i);
+               case PAINT_BLEND_DIFFERENCE: return mcol_difference(col, paintcol, alpha_i);
+               case PAINT_BLEND_SCREEN:     return mcol_screen(col, paintcol, alpha_i);
+               case PAINT_BLEND_HARDLIGHT:  return mcol_hardlight(col, paintcol, alpha_i);
+               case PAINT_BLEND_OVERLAY:    return mcol_overlay(col, paintcol, alpha_i);
+               case PAINT_BLEND_SOFTLIGHT:  return mcol_softlight(col, paintcol, alpha_i);
+               case PAINT_BLEND_EXCLUSION:  return mcol_exclusion(col, paintcol, alpha_i);
+               case PAINT_BLEND_LUMINOCITY: return mcol_luminosity(col, paintcol, alpha_i);
+               case PAINT_BLEND_SATURATION: return mcol_saturation(col, paintcol, alpha_i);
+               case PAINT_BLEND_HUE:        return mcol_hue(col, paintcol, alpha_i);
+               /* non-color */
+               case PAINT_BLEND_ALPHA_SUB:  return mcol_alpha_sub(col, alpha_i);
+               case PAINT_BLEND_ALPHA_ADD:  return mcol_alpha_add(col, alpha_i);
+               default:
+                       BLI_assert(0);
+                       return 0;
+       }
+ }
+ /** \} */
index 0000000000000000000000000000000000000000,4e807ccb4ef95734524ca8dde76ff3deb328432b..30d62d5a64e91fabb4d7e6bb94081b032dbe106d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,861 +1,871 @@@
 -#include "BKE_depsgraph.h"
+ /*
+  * ***** BEGIN GPL LICENSE BLOCK *****
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+ /** \file blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+  *  \ingroup edsculpt
+  */
+ #include "MEM_guardedalloc.h"
+ #include "BLI_blenlib.h"
+ #include "BLI_math.h"
+ #include "BLI_array_utils.h"
+ #include "BLI_bitmap.h"
+ #include "BLI_task.h"
+ #include "BLI_string_utils.h"
+ #include "IMB_imbuf.h"
+ #include "IMB_imbuf_types.h"
+ #include "IMB_colormanagement.h"
+ //#include "DNA_armature_types.h"
+ #include "DNA_mesh_types.h"
+ #include "DNA_particle_types.h"
+ #include "DNA_brush_types.h"
+ #include "DNA_object_types.h"
+ #include "DNA_scene_types.h"
+ #include "RNA_access.h"
+ #include "RNA_define.h"
+ #include "RNA_enum_types.h"
+ #include "BKE_DerivedMesh.h"
+ #include "BKE_brush.h"
+ #include "BKE_context.h"
 -      create_vgroups_from_armature(op->reports, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X));
+ #include "BKE_deform.h"
+ #include "BKE_mesh.h"
+ #include "BKE_mesh_mapping.h"
+ #include "BKE_modifier.h"
+ #include "BKE_object_deform.h"
+ #include "BKE_paint.h"
+ #include "BKE_report.h"
+ #include "BKE_colortools.h"
++#include "DEG_depsgraph.h"
++
+ #include "WM_api.h"
+ #include "WM_types.h"
+ #include "ED_armature.h"
+ #include "ED_mesh.h"
+ #include "ED_screen.h"
+ #include "ED_view3d.h"
+ #include "paint_intern.h"  /* own include */
+ /* -------------------------------------------------------------------- */
+ /** \name Store Previous Weights
+  *
+  * Use to avoid feedback loop w/ mirrored edits.
+  * \{ */
+ struct WPaintPrev {
+       /* previous vertex weights */
+       struct MDeformVert *wpaint_prev;
+       /* allocation size of prev buffers */
+       int tot;
+ };
+ static void wpaint_prev_init(struct WPaintPrev *wpp)
+ {
+       wpp->wpaint_prev = NULL;
+       wpp->tot = 0;
+ }
+ static void wpaint_prev_create(struct WPaintPrev *wpp, MDeformVert *dverts, int dcount)
+ {
+       wpaint_prev_init(wpp);
+       if (dverts && dcount) {
+               wpp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev");
+               wpp->tot = dcount;
+               BKE_defvert_array_copy(wpp->wpaint_prev, dverts, dcount);
+       }
+ }
+ static void wpaint_prev_destroy(struct WPaintPrev *wpp)
+ {
+       if (wpp->wpaint_prev) {
+               BKE_defvert_array_free(wpp->wpaint_prev, wpp->tot);
+       }
+       wpp->wpaint_prev = NULL;
+       wpp->tot = 0;
+ }
+ /** \} */
+ /* -------------------------------------------------------------------- */
+ /** \name Weight from Bones Operator
+  * \{ */
+ static int weight_from_bones_poll(bContext *C)
+ {
+       Object *ob = CTX_data_active_object(C);
+       return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob));
+ }
+ static int weight_from_bones_exec(bContext *C, wmOperator *op)
+ {
+       Scene *scene = CTX_data_scene(C);
+       Object *ob = CTX_data_active_object(C);
+       Object *armob = modifiers_isDeformedByArmature(ob);
+       Mesh *me = ob->data;
+       int type = RNA_enum_get(op->ptr, "type");
 -      DAG_id_tag_update(&me->id, 0);
++      EvaluationContext eval_ctx;
++
++      CTX_data_eval_ctx(C, &eval_ctx);
++
++      create_vgroups_from_armature(op->reports, &eval_ctx, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X));
 -      DAG_id_tag_update(&vc.obact->id, OB_RECALC_DATA);
++      DEG_id_tag_update(&me->id, 0);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
+       return OPERATOR_FINISHED;
+ }
+ void PAINT_OT_weight_from_bones(wmOperatorType *ot)
+ {
+       static EnumPropertyItem type_items[] = {
+               {ARM_GROUPS_AUTO, "AUTOMATIC", 0, "Automatic", "Automatic weights from bones"},
+               {ARM_GROUPS_ENVELOPE, "ENVELOPES", 0, "From Envelopes", "Weights from envelopes with user defined radius"},
+               {0, NULL, 0, NULL, NULL}};
+       /* identifiers */
+       ot->name = "Weight from Bones";
+       ot->idname = "PAINT_OT_weight_from_bones";
+       ot->description = "Set the weights of the groups matching the attached armature's selected bones, "
+                         "using the distance between the vertices and the bones";
+       /* api callbacks */
+       ot->exec = weight_from_bones_exec;
+       ot->invoke = WM_menu_invoke;
+       ot->poll = weight_from_bones_poll;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+       /* properties */
+       ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights");
+ }
+ /** \} */
+ /* -------------------------------------------------------------------- */
+ /** \name Sample Weight Operator
+  * \{ */
+ /* sets wp->weight to the closest weight value to vertex */
+ /* note: we cant sample frontbuf, weight colors are interpolated too unpredictable */
+ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+ {
+       ViewContext vc;
+       Mesh *me;
+       bool changed = false;
+       view3d_set_viewcontext(C, &vc);
+       me = BKE_mesh_from_object(vc.obact);
+       if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) {
+               const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
+               int v_idx_best = -1;
+               uint index;
+               view3d_operator_needs_opengl(C);
+               ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
+               if (use_vert_sel) {
+                       if (ED_mesh_pick_vert(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_VERT_SIZE, true)) {
+                               v_idx_best = index;
+                       }
+               }
+               else {
+                       if (ED_mesh_pick_face_vert(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
+                               v_idx_best = index;
+                       }
+                       else if (ED_mesh_pick_face(C, vc.obact, event->mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
+                               /* this relies on knowning the internal worksings of ED_mesh_pick_face_vert() */
+                               BKE_report(op->reports, RPT_WARNING, "The modifier used does not support deformed locations");
+                       }
+               }
+               if (v_idx_best != -1) { /* should always be valid */
+                       ToolSettings *ts = vc.scene->toolsettings;
+                       Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
+                       const int vgroup_active = vc.obact->actdef - 1;
+                       float vgroup_weight = defvert_find_weight(&me->dvert[v_idx_best], vgroup_active);
+                       /* use combined weight in multipaint mode, since that's what is displayed to the user in the colors */
+                       if (ts->multipaint) {
+                               int defbase_tot_sel;
+                               const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
+                               bool *defbase_sel = BKE_object_defgroup_selected_get(vc.obact, defbase_tot, &defbase_tot_sel);
+                               if (defbase_tot_sel > 1) {
+                                       if (me->editflag & ME_EDIT_MIRROR_X) {
+                                               BKE_object_defgroup_mirror_selection(
+                                                       vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
+                                       }
+                                       vgroup_weight = BKE_defvert_multipaint_collective_weight(
+                                               &me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, ts->auto_normalize);
+                                       /* if autonormalize is enabled, but weights are not normalized, the value can exceed 1 */
+                                       CLAMP(vgroup_weight, 0.0f, 1.0f);
+                               }
+                               MEM_freeN(defbase_sel);
+                       }
+                       BKE_brush_weight_set(vc.scene, brush, vgroup_weight);
+                       changed = true;
+               }
+       }
+       if (changed) {
+               /* not really correct since the brush didnt change, but redraws the toolbar */
+               WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL); /* ts->wpaint->paint.brush */
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void PAINT_OT_weight_sample(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Weight Paint Sample Weight";
+       ot->idname = "PAINT_OT_weight_sample";
+       ot->description = "Use the mouse to sample a weight in the 3D view";
+       /* api callbacks */
+       ot->invoke = weight_sample_invoke;
+       ot->poll = weight_paint_mode_poll;
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+ }
+ /** \} */
+ /* -------------------------------------------------------------------- */
+ /** \name Weight Paint Sample Group Operator
+  * \{ */
+ /* samples cursor location, and gives menu with vertex groups to activate */
+ static bool weight_paint_sample_enum_itemf__helper(const MDeformVert *dvert, const int defbase_tot, int *groups)
+ {
+       /* this func fills in used vgroup's */
+       bool found = false;
+       int i = dvert->totweight;
+       MDeformWeight *dw;
+       for (dw = dvert->dw; i > 0; dw++, i--) {
+               if (dw->def_nr < defbase_tot) {
+                       groups[dw->def_nr] = true;
+                       found = true;
+               }
+       }
+       return found;
+ }
+ static EnumPropertyItem *weight_paint_sample_enum_itemf(
+         bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+ {
+       if (C) {
+               wmWindow *win = CTX_wm_window(C);
+               if (win && win->eventstate) {
+                       ViewContext vc;
+                       Mesh *me;
+                       view3d_set_viewcontext(C, &vc);
+                       me = BKE_mesh_from_object(vc.obact);
+                       if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) {
+                               const int defbase_tot = BLI_listbase_count(&vc.obact->defbase);
+                               const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
+                               int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups");
+                               bool found = false;
+                               uint index;
+                               const int mval[2] = {
+                                   win->eventstate->x - vc.ar->winrct.xmin,
+                                   win->eventstate->y - vc.ar->winrct.ymin,
+                               };
+                               view3d_operator_needs_opengl(C);
+                               ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d);
+                               if (use_vert_sel) {
+                                       if (ED_mesh_pick_vert(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_VERT_SIZE, true)) {
+                                               MDeformVert *dvert = &me->dvert[index];
+                                               found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
+                                       }
+                               }
+                               else {
+                                       if (ED_mesh_pick_face(C, vc.obact, mval, &index, ED_MESH_PICK_DEFAULT_FACE_SIZE)) {
+                                               const MPoly *mp = &me->mpoly[index];
+                                               uint fidx = mp->totloop - 1;
+                                               do {
+                                                       MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v];
+                                                       found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
+                                               } while (fidx--);
+                                       }
+                               }
+                               if (found == false) {
+                                       MEM_freeN(groups);
+                               }
+                               else {
+                                       EnumPropertyItem *item = NULL, item_tmp = {0};
+                                       int totitem = 0;
+                                       int i = 0;
+                                       bDeformGroup *dg;
+                                       for (dg = vc.obact->defbase.first; dg && i < defbase_tot; i++, dg = dg->next) {
+                                               if (groups[i]) {
+                                                       item_tmp.identifier = item_tmp.name = dg->name;
+                                                       item_tmp.value = i;
+                                                       RNA_enum_item_add(&item, &totitem, &item_tmp);
+                                               }
+                                       }
+                                       RNA_enum_item_end(&item, &totitem);
+                                       *r_free = true;
+                                       MEM_freeN(groups);
+                                       return item;
+                               }
+                       }
+               }
+       }
+       return DummyRNA_NULL_items;
+ }
+ static int weight_sample_group_exec(bContext *C, wmOperator *op)
+ {
+       int type = RNA_enum_get(op->ptr, "group");
+       ViewContext vc;
+       view3d_set_viewcontext(C, &vc);
+       BLI_assert(type + 1 >= 0);
+       vc.obact->actdef = type + 1;
 -      DAG_id_tag_update(&me->id, 0);
++      DEG_id_tag_update(&vc.obact->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, vc.obact);
+       return OPERATOR_FINISHED;
+ }
+ /* TODO, we could make this a menu into OBJECT_OT_vertex_group_set_active rather than its own operator */
+ void PAINT_OT_weight_sample_group(wmOperatorType *ot)
+ {
+       PropertyRNA *prop = NULL;
+       /* identifiers */
+       ot->name = "Weight Paint Sample Group";
+       ot->idname = "PAINT_OT_weight_sample_group";
+       ot->description = "Select one of the vertex groups available under current mouse position";
+       /* api callbacks */
+       ot->exec = weight_sample_group_exec;
+       ot->invoke = WM_menu_invoke;
+       ot->poll = weight_paint_mode_poll;
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+       /* keyingset to use (dynamic enum) */
+       prop = RNA_def_enum(ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
+       RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf);
+       RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
+       ot->prop = prop;
+ }
+ /** \} */
+ /* -------------------------------------------------------------------- */
+ /** \name Weight Set Operator
+  * \{ */
+ /* fills in the selected faces with the current weight and vertex group */
+ static bool weight_paint_set(Object *ob, float paintweight)
+ {
+       Mesh *me = ob->data;
+       const MPoly *mp;
+       MDeformWeight *dw, *dw_prev;
+       int vgroup_active, vgroup_mirror = -1;
+       uint index;
+       const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
+       /* mutually exclusive, could be made into a */
+       const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me);
+       if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) {
+               return false;
+       }
+       vgroup_active = ob->actdef - 1;
+       /* if mirror painting, find the other group */
+       if (me->editflag & ME_EDIT_MIRROR_X) {
+               vgroup_mirror = ED_wpaint_mirror_vgroup_ensure(ob, vgroup_active);
+       }
+       struct WPaintPrev wpp;
+       wpaint_prev_create(&wpp, me->dvert, me->totvert);
+       for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) {
+               uint fidx = mp->totloop - 1;
+               if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) {
+                       continue;
+               }
+               do {
+                       uint vidx = me->mloop[mp->loopstart + fidx].v;
+                       if (!me->dvert[vidx].flag) {
+                               if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) {
+                                       continue;
+                               }
+                               dw = defvert_verify_index(&me->dvert[vidx], vgroup_active);
+                               if (dw) {
+                                       dw_prev = defvert_verify_index(wpp.wpaint_prev + vidx, vgroup_active);
+                                       dw_prev->weight = dw->weight; /* set the undo weight */
+                                       dw->weight = paintweight;
+                                       if (me->editflag & ME_EDIT_MIRROR_X) {  /* x mirror painting */
+                                               int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology);
+                                               if (j >= 0) {
+                                                       /* copy, not paint again */
+                                                       if (vgroup_mirror != -1) {
+                                                               dw = defvert_verify_index(me->dvert + j, vgroup_mirror);
+                                                               dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_mirror);
+                                                       }
+                                                       else {
+                                                               dw = defvert_verify_index(me->dvert + j, vgroup_active);
+                                                               dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_active);
+                                                       }
+                                                       dw_prev->weight = dw->weight; /* set the undo weight */
+                                                       dw->weight = paintweight;
+                                               }
+                                       }
+                               }
+                               me->dvert[vidx].flag = 1;
+                       }
+               } while (fidx--);
+       }
+       {
+               MDeformVert *dv = me->dvert;
+               for (index = me->totvert; index != 0; index--, dv++) {
+                       dv->flag = 0;
+               }
+       }
+       wpaint_prev_destroy(&wpp);
 -              DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
++      DEG_id_tag_update(&me->id, 0);
+       return true;
+ }
+ static int weight_paint_set_exec(bContext *C, wmOperator *op)
+ {
+       struct Scene *scene = CTX_data_scene(C);
+       Object *obact = CTX_data_active_object(C);
+       ToolSettings *ts = CTX_data_tool_settings(C);
+       Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
+       float vgroup_weight = BKE_brush_weight_get(scene, brush);
+       if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, NULL) == false) {
+               return OPERATOR_CANCELLED;
+       }
+       if (weight_paint_set(obact, vgroup_weight)) {
+               ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+ }
+ void PAINT_OT_weight_set(wmOperatorType *ot)
+ {
+       /* identifiers */
+       ot->name = "Set Weight";
+       ot->idname = "PAINT_OT_weight_set";
+       ot->description = "Fill the active vertex group with the current paint weight";
+       /* api callbacks */
+       ot->exec = weight_paint_set_exec;
+       ot->poll = mask_paint_poll;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ }
+ /** \} */
+ /* -------------------------------------------------------------------- */
+ /** \name Interactive Weight Gradient Operator
+  * \{ */
+ /* *** VGroups Gradient *** */
+ typedef struct DMGradient_vertStore {
+       float sco[2];
+       float weight_orig;
+       enum {
+               VGRAD_STORE_NOP      = 0,
+               VGRAD_STORE_DW_EXIST = (1 << 0)
+       } flag;
+ } DMGradient_vertStore;
+ typedef struct DMGradient_vertStoreBase {
+       struct WPaintPrev wpp;
+       DMGradient_vertStore elem[0];
+ } DMGradient_vertStoreBase;
+ typedef struct DMGradient_userData {
+       struct ARegion *ar;
+       Scene *scene;
+       Mesh *me;
+       Brush *brush;
+       const float *sco_start;     /* [2] */
+       const float *sco_end;       /* [2] */
+       float        sco_line_div;  /* store (1.0f / len_v2v2(sco_start, sco_end)) */
+       int def_nr;
+       bool is_init;
+       DMGradient_vertStoreBase *vert_cache;
+       /* only for init */
+       BLI_bitmap *vert_visit;
+       /* options */
+       short use_select;
+       short type;
+       float weightpaint;
+ } DMGradient_userData;
+ static void gradientVert_update(DMGradient_userData *grad_data, int index)
+ {
+       Mesh *me = grad_data->me;
+       DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
+       float alpha;
+       if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) {
+               alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end);
+       }
+       else {
+               BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL);
+               alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div;
+       }
+       /* no need to clamp 'alpha' yet */
+       /* adjust weight */
+       alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f);
+       if (alpha != 0.0f) {
+               MDeformVert *dv = &me->dvert[index];
+               MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr);
+               // dw->weight = alpha; // testing
+               int tool = grad_data->brush->vertexpaint_tool;
+               float testw;
+               /* init if we just added */
+               testw = ED_wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha);
+               CLAMP(testw, 0.0f, 1.0f);
+               dw->weight = testw;
+       }
+       else {
+               MDeformVert *dv = &me->dvert[index];
+               if (vs->flag & VGRAD_STORE_DW_EXIST) {
+                       /* normally we NULL check, but in this case we know it exists */
+                       MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
+                       dw->weight = vs->weight_orig;
+               }
+               else {
+                       /* wasn't originally existing, remove */
+                       MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr);
+                       if (dw) {
+                               defvert_remove_group(dv, dw);
+                       }
+               }
+       }
+ }
+ static void gradientVertUpdate__mapFunc(
+         void *userData, int index, const float UNUSED(co[3]),
+         const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
+ {
+       DMGradient_userData *grad_data = userData;
+       Mesh *me = grad_data->me;
+       if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
+               DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
+               if (vs->sco[0] != FLT_MAX) {
+                       gradientVert_update(grad_data, index);
+               }
+       }
+ }
+ static void gradientVertInit__mapFunc(
+         void *userData, int index, const float co[3],
+         const float UNUSED(no_f[3]), const short UNUSED(no_s[3]))
+ {
+       DMGradient_userData *grad_data = userData;
+       Mesh *me = grad_data->me;
+       if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) {
+               /* run first pass only,
+                * the screen coords of the verts need to be cached because
+                * updating the mesh may move them about (entering feedback loop) */
+               if (BLI_BITMAP_TEST(grad_data->vert_visit, index) == 0) {
+                       DMGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
+                       if (ED_view3d_project_float_object(grad_data->ar,
+                                                          co, vs->sco,
+                                                          V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK)
+                       {
+                               /* ok */
+                               MDeformVert *dv = &me->dvert[index];
+                               const MDeformWeight *dw;
+                               dw = defvert_find_index(dv, grad_data->def_nr);
+                               if (dw) {
+                                       vs->weight_orig = dw->weight;
+                                       vs->flag = VGRAD_STORE_DW_EXIST;
+                               }
+                               else {
+                                       vs->weight_orig = 0.0f;
+                                       vs->flag = VGRAD_STORE_NOP;
+                               }
+                               BLI_BITMAP_ENABLE(grad_data->vert_visit, index);
+                               gradientVert_update(grad_data, index);
+                       }
+                       else {
+                               /* no go */
+                               copy_v2_fl(vs->sco, FLT_MAX);
+                       }
+               }
+       }
+ }
+ static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
+ {
+       int ret = WM_gesture_straightline_modal(C, op, event);
+       wmGesture *gesture = op->customdata;
+       DMGradient_vertStoreBase *vert_cache = gesture->userdata;
+       bool do_gesture_free = false;
+       if (ret & OPERATOR_RUNNING_MODAL) {
+               if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {  /* XXX, hardcoded */
+                       /* generally crap! redo! */
+                       do_gesture_free = true;
+                       ret &= ~OPERATOR_RUNNING_MODAL;
+                       ret |= OPERATOR_FINISHED;
+               }
+       }
+       if (ret & OPERATOR_CANCELLED) {
+               Object *ob = CTX_data_active_object(C);
+               Mesh *me = ob->data;
+               if (vert_cache->wpp.wpaint_prev) {
+                       BKE_defvert_array_free_elems(me->dvert, me->totvert);
+                       BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert);
+                       wpaint_prev_destroy(&vert_cache->wpp);
+               }
 -      DerivedMesh *dm = mesh_get_derived_final(scene, ob, scene->customdata_mask);
++              DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
+               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+       }
+       else if (ret & OPERATOR_FINISHED) {
+               wpaint_prev_destroy(&vert_cache->wpp);
+       }
+       if (do_gesture_free) {
+               WM_gesture_straightline_cancel(C, op);
+       }
+       return ret;
+ }
+ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
+ {
+       wmGesture *gesture = op->customdata;
+       DMGradient_vertStoreBase *vert_cache;
+       struct ARegion *ar = CTX_wm_region(C);
+       Scene *scene = CTX_data_scene(C);
+       Object *ob = CTX_data_active_object(C);
+       Mesh *me = ob->data;
+       int x_start = RNA_int_get(op->ptr, "xstart");
+       int y_start = RNA_int_get(op->ptr, "ystart");
+       int x_end = RNA_int_get(op->ptr, "xend");
+       int y_end = RNA_int_get(op->ptr, "yend");
+       float sco_start[2] = {x_start, y_start};
+       float sco_end[2] = {x_end, y_end};
+       const bool is_interactive = (gesture != NULL);
 -      DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
++
++      EvaluationContext eval_ctx;
++
++      CTX_data_eval_ctx(C, &eval_ctx);
++
++      DerivedMesh *dm = mesh_get_derived_final(&eval_ctx, scene, ob, scene->customdata_mask);
+       DMGradient_userData data = {NULL};
+       if (is_interactive) {
+               if (gesture->userdata == NULL) {
+                       gesture->userdata = MEM_mallocN(
+                               sizeof(DMGradient_vertStoreBase) +
+                               (sizeof(DMGradient_vertStore) * me->totvert),
+                               __func__);
+                       data.is_init = true;
+                       wpaint_prev_create(&((DMGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert);
+                       /* on init only, convert face -> vert sel  */
+                       if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
+                               BKE_mesh_flush_select_from_polys(me);
+                       }
+               }
+               vert_cache = gesture->userdata;
+       }
+       else {
+               if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) {
+                       return OPERATOR_CANCELLED;
+               }
+               data.is_init = true;
+               vert_cache = MEM_mallocN(
+                       sizeof(DMGradient_vertStoreBase) +
+                       (sizeof(DMGradient_vertStore) * me->totvert),
+                       __func__);
+       }
+       data.ar = ar;
+       data.scene = scene;
+       data.me = ob->data;
+       data.sco_start = sco_start;
+       data.sco_end   = sco_end;
+       data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end);
+       data.def_nr = ob->actdef - 1;
+       data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL));
+       data.vert_cache = vert_cache;
+       data.vert_visit = NULL;
+       data.type = RNA_enum_get(op->ptr, "type");
+       {
+               ToolSettings *ts = CTX_data_tool_settings(C);
+               VPaint *wp = ts->wpaint;
+               struct Brush *brush = BKE_paint_brush(&wp->paint);
+               curvemapping_initialize(brush->curve);
+               data.brush = brush;
+               data.weightpaint = BKE_brush_weight_get(scene, brush);
+       }
+       ED_view3d_init_mats_rv3d(ob, ar->regiondata);
+       if (data.is_init) {
+               data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__);
+               dm->foreachMappedVert(dm, gradientVertInit__mapFunc, &data, DM_FOREACH_NOP);
+               MEM_freeN(data.vert_visit);
+               data.vert_visit = NULL;
+       }
+       else {
+               dm->foreachMappedVert(dm, gradientVertUpdate__mapFunc, &data, DM_FOREACH_NOP);
+       }
++      DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+       if (is_interactive == false) {
+               MEM_freeN(vert_cache);
+       }
+       return OPERATOR_FINISHED;
+ }
+ static int paint_weight_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+ {
+       int ret;
+       if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) {
+               return OPERATOR_CANCELLED;
+       }
+       ret = WM_gesture_straightline_invoke(C, op, event);
+       if (ret & OPERATOR_RUNNING_MODAL) {
+               struct ARegion *ar = CTX_wm_region(C);
+               if (ar->regiontype == RGN_TYPE_WINDOW) {
+                       /* TODO, hardcoded, extend WM_gesture_straightline_ */
+                       if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
+                               wmGesture *gesture = op->customdata;
+                               gesture->mode = 1;
+                       }
+               }
+       }
+       return ret;
+ }
+ void PAINT_OT_weight_gradient(wmOperatorType *ot)
+ {
+       /* defined in DNA_space_types.h */
+       static EnumPropertyItem gradient_types[] = {
+               {WPAINT_GRADIENT_TYPE_LINEAR, "LINEAR", 0, "Linear", ""},
+               {WPAINT_GRADIENT_TYPE_RADIAL, "RADIAL", 0, "Radial", ""},
+               {0, NULL, 0, NULL, NULL}
+       };
+       PropertyRNA *prop;
+       /* identifiers */
+       ot->name = "Weight Gradient";
+       ot->idname = "PAINT_OT_weight_gradient";
+       ot->description = "Draw a line to apply a weight gradient to selected vertices";
+       /* api callbacks */
+       ot->invoke = paint_weight_gradient_invoke;
+       ot->modal = paint_weight_gradient_modal;
+       ot->exec = paint_weight_gradient_exec;
+       ot->poll = weight_paint_poll;
+       ot->cancel = WM_gesture_straightline_cancel;
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+       prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", "");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+       WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
+ }
+ /** \} */