diff options
author | Hans Goudey <h.goudey@me.com> | 2022-06-20 21:57:21 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-06-20 21:57:21 +0300 |
commit | 3545d8a5008322e5f8f0b8b1e85a52329b666150 (patch) | |
tree | a6124aab3c7673c28c160f89d1620253703aa953 /source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc | |
parent | 522dcc54af3564d30cf0db3ec277a14c6c7046e6 (diff) |
Cleanup: Move paint_vertex_color_ops.c to C++
Diffstat (limited to 'source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc')
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc new file mode 100644 index 00000000000..6a47aceb2b0 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc @@ -0,0 +1,483 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_array.hh" +#include "BLI_math_base.h" +#include "BLI_math_color.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_mesh.h" + +#include "DEG_depsgraph.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_mesh.h" + +#include "paint_intern.h" /* own include */ + +using blender::Array; + +/* -------------------------------------------------------------------- */ +/** \name Internal Utility Functions + * \{ */ + +static bool vertex_weight_paint_mode_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = BKE_mesh_from_object(ob); + return (ob && (ELEM(ob->mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT))) && + (me && me->totpoly && me->dvert); +} + +static void tag_object_after_update(Object *object) +{ + BLI_assert(object->type == OB_MESH); + Mesh *mesh = static_cast<Mesh *>(object->data); + DEG_id_tag_update(&mesh->id, ID_RECALC_COPY_ON_WRITE); + /* NOTE: Original mesh is used for display, so tag it directly here. */ + BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Color from Weight Operator + * \{ */ + +static bool vertex_paint_from_weight(Object *ob) +{ + Mesh *me; + const MPoly *mp; + int vgroup_active; + + if (((me = BKE_mesh_from_object(ob)) == nullptr || + (ED_mesh_color_ensure(me, nullptr)) == false)) { + return false; + } + + /* TODO: respect selection. */ + /* TODO: Do we want to take weights from evaluated mesh instead? 2.7x was not doing it anyway. */ + mp = me->mpoly; + vgroup_active = me->vertex_group_active_index - 1; + for (int i = 0; i < me->totpoly; i++, mp++) { + MLoopCol *lcol = &me->mloopcol[mp->loopstart]; + uint j = 0; + do { + uint vidx = me->mloop[mp->loopstart + j].v; + const float weight = BKE_defvert_find_weight(&me->dvert[vidx], vgroup_active); + const uchar grayscale = weight * 255; + lcol->r = grayscale; + lcol->b = grayscale; + lcol->g = grayscale; + lcol++; + j++; + } while (j < mp->totloop); + } + + tag_object_after_update(ob); + + return true; +} + +static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *obact = CTX_data_active_object(C); + if (vertex_paint_from_weight(obact)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Color from Weight"; + ot->idname = "PAINT_OT_vertex_color_from_weight"; + ot->description = "Convert active weight into gray scale vertex colors"; + + /* api callback */ + ot->exec = vertex_paint_from_weight_exec; + ot->poll = vertex_weight_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* TODO: invert, alpha */ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Smooth Vertex Colors Operator + * \{ */ + +static void vertex_color_smooth_looptag(Mesh *me, const bool *mlooptag) +{ + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const MPoly *mp; + bool has_shared = false; + + if (me->mloopcol == nullptr || me->totvert == 0 || me->totpoly == 0) { + return; + } + + int(*scol)[4] = static_cast<int(*)[4]>(MEM_callocN(sizeof(int) * me->totvert * 5, "scol")); + + int i; + 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 (int 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 (int 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; + + if (((me = BKE_mesh_from_object(ob)) == nullptr) || + (ED_mesh_color_ensure(me, nullptr) == 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; + + Array<bool> loop_tag(me->totloop, false); + + /* simply tag loops of selected faces */ + mp = me->mpoly; + for (i = 0; i < me->totpoly; i++, mp++) { + const MLoop *ml = me->mloop + mp->loopstart; + + if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { + continue; + } + + j = 0; + do { + if (!(use_vert_sel && !(me->mvert[ml->v].flag & SELECT))) { + loop_tag[mp->loopstart + j] = true; + } + ml++; + j++; + } while (j < mp->totloop); + } + + /* remove stale me->mcol, will be added later */ + BKE_mesh_tessface_clear(me); + + vertex_color_smooth_looptag(me, loop_tag.data()); + + tag_object_after_update(ob); + + 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; + } + 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 VPaintTx_BrightContrastData *data = static_cast<const VPaintTx_BrightContrastData *>( + 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; + /* + * 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 - delta * 2.0f; + gain = 1.0f / max_ff(gain, FLT_EPSILON); + offset = gain * (brightness - delta); + } + else { + delta *= -1; + gain = max_ff(1.0f - delta * 2.0f, 0.0f); + offset = gain * brightness + delta; + } + } + + VPaintTx_BrightContrastData user_data{}; + user_data.gain = gain; + user_data.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; + } + return OPERATOR_CANCELLED; +} + +void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Vertex Paint Brightness/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 VPaintTx_HueSatData *data = static_cast<const VPaintTx_HueSatData *>(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); + + VPaintTx_HueSatData user_data{}; + user_data.hue = RNA_float_get(op->ptr, "h"); + user_data.sat = RNA_float_get(op->ptr, "s"); + user_data.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; + } + 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, nullptr)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + 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 VPaintTx_LevelsData *data = static_cast<const VPaintTx_LevelsData *>(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); + + VPaintTx_LevelsData user_data{}; + user_data.gain = RNA_float_get(op->ptr, "gain"); + user_data.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; + } + 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); +} + +/** \} */ |