Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||
| /** \file | /** \file | ||||
| * \ingroup edsculpt | * \ingroup edsculpt | ||||
| */ | */ | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "DNA_mesh_types.h" | #include "DNA_mesh_types.h" | ||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "BLI_array.hh" | #include "BLI_array.hh" | ||||
| #include "BLI_index_mask_ops.hh" | |||||
| #include "BLI_math_base.h" | #include "BLI_math_base.h" | ||||
| #include "BLI_math_color.h" | #include "BLI_math_color.h" | ||||
| #include "BLI_vector.hh" | |||||
| #include "BKE_attribute_math.hh" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_deform.h" | #include "BKE_deform.h" | ||||
| #include "BKE_geometry_set.hh" | |||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "ED_mesh.h" | #include "ED_mesh.h" | ||||
| #include "paint_intern.h" /* own include */ | #include "paint_intern.h" /* own include */ | ||||
| using blender::Array; | using blender::Array; | ||||
| using blender::ColorGeometry4f; | |||||
| using blender::GMutableSpan; | |||||
| using blender::IndexMask; | |||||
| using blender::Vector; | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Internal Utility Functions | /** \name Internal Utility Functions | ||||
| * \{ */ | * \{ */ | ||||
| static bool vertex_weight_paint_mode_poll(bContext *C) | static bool vertex_weight_paint_mode_poll(bContext *C) | ||||
| { | { | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| Show All 14 Lines | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Vertex Color from Weight Operator | /** \name Vertex Color from Weight Operator | ||||
| * \{ */ | * \{ */ | ||||
| static bool vertex_paint_from_weight(Object *ob) | static bool vertex_paint_from_weight(Object *ob) | ||||
| { | { | ||||
| Mesh *me; | using namespace blender; | ||||
| const MPoly *mp; | |||||
| int vgroup_active; | |||||
| Mesh *me; | |||||
| if (((me = BKE_mesh_from_object(ob)) == nullptr || | if (((me = BKE_mesh_from_object(ob)) == nullptr || | ||||
| (ED_mesh_color_ensure(me, nullptr)) == false)) { | (ED_mesh_color_ensure(me, nullptr)) == false)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* TODO: respect selection. */ | const CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&me->id); | ||||
| /* TODO: Do we want to take weights from evaluated mesh instead? 2.7x was not doing it anyway. */ | if (active_color_layer == nullptr) { | ||||
| mp = me->mpoly; | BLI_assert_unreachable(); | ||||
| vgroup_active = me->vertex_group_active_index - 1; | return false; | ||||
| for (int i = 0; i < me->totpoly; i++, mp++) { | } | ||||
| MLoopCol *lcol = &me->mloopcol[mp->loopstart]; | |||||
| uint j = 0; | const int active_vertex_group_index = me->vertex_group_active_index - 1; | ||||
| do { | const bDeformGroup *deform_group = static_cast<const bDeformGroup *>( | ||||
| uint vidx = me->mloop[mp->loopstart + j].v; | BLI_findlink(&me->vertex_group_names, active_vertex_group_index)); | ||||
| const float weight = BKE_defvert_find_weight(&me->dvert[vidx], vgroup_active); | if (deform_group == nullptr) { | ||||
| const uchar grayscale = weight * 255; | BLI_assert_unreachable(); | ||||
| lcol->r = grayscale; | return false; | ||||
| lcol->b = grayscale; | } | ||||
| lcol->g = grayscale; | |||||
| lcol++; | MeshComponent component; | ||||
| j++; | component.replace(me, GeometryOwnershipType::Editable); | ||||
| } while (j < mp->totloop); | |||||
| bke::WriteAttributeLookup color_attribute = component.attribute_try_get_for_write( | |||||
| active_color_layer->name); | |||||
| if (!color_attribute) { | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | } | ||||
| /* Retrieve the vertex group with the domain and type of the existing color | |||||
| * attribute, in order to let the attribute API handle both conversions. */ | |||||
| const GVArray vertex_group = component.attribute_get_for_read( | |||||
| deform_group->name, | |||||
| ATTR_DOMAIN_POINT, | |||||
| bke::cpp_type_to_custom_data_type(color_attribute.varray.type())); | |||||
| if (!vertex_group) { | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| GVArray_GSpan interpolated{component.attribute_try_adapt_domain( | |||||
| vertex_group, ATTR_DOMAIN_POINT, color_attribute.domain)}; | |||||
| color_attribute.varray.set_all(interpolated.data()); | |||||
| if (color_attribute.tag_modified_fn) { | |||||
| color_attribute.tag_modified_fn(); | |||||
| } | |||||
| tag_object_after_update(ob); | tag_object_after_update(ob); | ||||
| return true; | return true; | ||||
| } | } | ||||
| static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op)) | static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| Object *obact = CTX_data_active_object(C); | Object *obact = CTX_data_active_object(C); | ||||
| Show All 22 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Smooth Vertex Colors Operator | /** \name Smooth Vertex Colors Operator | ||||
| * \{ */ | * \{ */ | ||||
| static void vertex_color_smooth_looptag(Mesh *me, const bool *mlooptag) | static IndexMask get_selected_indices(const Mesh &mesh, | ||||
| { | const eAttrDomain domain, | ||||
| const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; | Vector<int64_t> &indices) | ||||
| const MPoly *mp; | { | ||||
| bool has_shared = false; | using namespace blender; | ||||
| Span<MVert> verts(mesh.mvert, mesh.totvert); | |||||
| if (me->mloopcol == nullptr || me->totvert == 0 || me->totpoly == 0) { | Span<MPoly> faces(mesh.mpoly, mesh.totpoly); | ||||
| MeshComponent component; | |||||
| component.replace(&const_cast<Mesh &>(mesh), GeometryOwnershipType::ReadOnly); | |||||
| if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) { | |||||
| const VArray<bool> selection = component.attribute_try_adapt_domain( | |||||
| VArray<bool>::ForFunc(faces.size(), | |||||
| [&](const int i) { return faces[i].flag & ME_FACE_SEL; }), | |||||
| ATTR_DOMAIN_FACE, | |||||
| domain); | |||||
| return index_mask_ops::find_indices_based_on_predicate( | |||||
| IndexMask(component.attribute_domain_num(domain)), 4096, indices, [&](const int i) { | |||||
| return selection[i]; | |||||
| }); | |||||
| } | |||||
| if (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) { | |||||
| const VArray<bool> selection = component.attribute_try_adapt_domain( | |||||
| VArray<bool>::ForFunc(verts.size(), [&](const int i) { return verts[i].flag & SELECT; }), | |||||
| ATTR_DOMAIN_POINT, | |||||
| domain); | |||||
| return index_mask_ops::find_indices_based_on_predicate( | |||||
| IndexMask(component.attribute_domain_num(domain)), 4096, indices, [&](const int i) { | |||||
| return selection[i]; | |||||
| }); | |||||
| } | |||||
| return IndexMask(component.attribute_domain_num(domain)); | |||||
| } | |||||
| static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask selection) | |||||
| { | |||||
| using namespace blender; | |||||
| const CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&mesh.id); | |||||
| if (active_color_layer == nullptr) { | |||||
| BLI_assert_unreachable(); | |||||
| return; | return; | ||||
| } | } | ||||
| int(*scol)[4] = static_cast<int(*)[4]>(MEM_callocN(sizeof(int) * me->totvert * 5, "scol")); | MeshComponent component; | ||||
| component.replace(&mesh, GeometryOwnershipType::Editable); | |||||
| int i; | if (component.attribute_get_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) { | ||||
| for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) { | return; | ||||
| 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) { | GVArray color_attribute_point = component.attribute_try_get_for_read(active_color_layer->name, | ||||
| for (i = 0; i < me->totvert; i++) { | ATTR_DOMAIN_POINT); | ||||
| 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++) { | GVArray color_attribute_corner = component.attribute_try_adapt_domain( | ||||
| if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) { | color_attribute_point, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER); | ||||
| 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); | color_attribute_corner.materialize(selection, active_color_layer->data); | ||||
| } | } | ||||
| static bool vertex_color_smooth(Object *ob) | static bool vertex_color_smooth(Object *ob) | ||||
| { | { | ||||
| Mesh *me; | Mesh *me; | ||||
| const MPoly *mp; | |||||
| int i, j; | |||||
| if (((me = BKE_mesh_from_object(ob)) == nullptr) || | if (((me = BKE_mesh_from_object(ob)) == nullptr) || | ||||
| (ED_mesh_color_ensure(me, nullptr) == false)) { | (ED_mesh_color_ensure(me, nullptr) == false)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; | Vector<int64_t> indices; | ||||
| const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; | const IndexMask selection = get_selected_indices(*me, ATTR_DOMAIN_CORNER, indices); | ||||
| 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 */ | face_corner_color_equalize_vertices(*me, selection); | ||||
| BKE_mesh_tessface_clear(me); | |||||
| vertex_color_smooth_looptag(me, loop_tag.data()); | |||||
| tag_object_after_update(ob); | tag_object_after_update(ob); | ||||
| return true; | return true; | ||||
| } | } | ||||
| static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op)) | static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| Show All 21 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Vertex Color Transformation Operators | /** \name Vertex Color Transformation Operators | ||||
| * \{ */ | * \{ */ | ||||
| struct VPaintTx_BrightContrastData { | template<typename TransformFn> | ||||
| /* pre-calculated */ | static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn) | ||||
| 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 *>( | using namespace blender; | ||||
| user_data); | |||||
| for (int i = 0; i < 3; i++) { | const CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&mesh.id); | ||||
| r_col[i] = data->gain * col[i] + data->offset; | if (active_color_layer == nullptr) { | ||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | } | ||||
| MeshComponent component; | |||||
| component.replace(&mesh, GeometryOwnershipType::Editable); | |||||
| bke::WriteAttributeLookup color_attribute = component.attribute_try_get_for_write( | |||||
| active_color_layer->name); | |||||
| if (!color_attribute) { | |||||
| BLI_assert_unreachable(); | |||||
| return false; | |||||
| } | |||||
| Vector<int64_t> indices; | |||||
| const IndexMask selection = get_selected_indices(mesh, color_attribute.domain, indices); | |||||
| attribute_math::convert_to_static_type(color_attribute.varray.type(), [&](auto dummy) { | |||||
| using T = decltype(dummy); | |||||
| threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { | |||||
| for (const int i : selection.slice(range)) { | |||||
| if constexpr (std::is_same_v<T, ColorGeometry4f>) { | |||||
| ColorGeometry4f color = color_attribute.varray.get<ColorGeometry4f>(i); | |||||
| transform_fn(color); | |||||
| color_attribute.varray.set_by_copy(i, &color); | |||||
| } | |||||
| else if constexpr (std::is_same_v<T, ColorGeometry4b>) { | |||||
| ColorGeometry4f color = color_attribute.varray.get<ColorGeometry4b>(i).decode(); | |||||
| transform_fn(color); | |||||
| ColorGeometry4b color_encoded = color.encode(); | |||||
| color_attribute.varray.set_by_copy(i, &color_encoded); | |||||
| } | |||||
| } | |||||
| }); | |||||
| }); | |||||
| DEG_id_tag_update(&mesh.id, 0); | |||||
| return true; | |||||
| } | } | ||||
| static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op) | static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Object *obact = CTX_data_active_object(C); | Object *obact = CTX_data_active_object(C); | ||||
| float gain, offset; | float gain, offset; | ||||
| { | { | ||||
| Show All 13 Lines | float gain, offset; | ||||
| } | } | ||||
| else { | else { | ||||
| delta *= -1; | delta *= -1; | ||||
| gain = max_ff(1.0f - delta * 2.0f, 0.0f); | gain = max_ff(1.0f - delta * 2.0f, 0.0f); | ||||
| offset = gain * brightness + delta; | offset = gain * brightness + delta; | ||||
| } | } | ||||
| } | } | ||||
| VPaintTx_BrightContrastData user_data{}; | Mesh *me; | ||||
| user_data.gain = gain; | if (((me = BKE_mesh_from_object(obact)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { | ||||
| user_data.offset = offset; | return OPERATOR_CANCELLED; | ||||
| } | |||||
| transform_active_color(*me, [&](ColorGeometry4f &color) { | |||||
| for (int i = 0; i < 3; i++) { | |||||
| color[i] = gain * color[i] + offset; | |||||
| } | |||||
| }); | |||||
| if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) { | |||||
| WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); | WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot) | void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot) | ||||
| { | { | ||||
| PropertyRNA *prop; | PropertyRNA *prop; | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Vertex Paint Brightness/Contrast"; | ot->name = "Vertex Paint Brightness/Contrast"; | ||||
| ot->idname = "PAINT_OT_vertex_color_brightness_contrast"; | ot->idname = "PAINT_OT_vertex_color_brightness_contrast"; | ||||
| ot->description = "Adjust vertex color brightness/contrast"; | ot->description = "Adjust vertex color brightness/contrast"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = vertex_color_brightness_contrast_exec; | ot->exec = vertex_color_brightness_contrast_exec; | ||||
| ot->poll = vertex_paint_mode_poll; | ot->poll = vertex_paint_mode_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* params */ | /* params */ | ||||
| const float min = -100, max = +100; | 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, "brightness", 0.0f, min, max, "Brightness", "", min, max); | ||||
| prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", 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); | RNA_def_property_ui_range(prop, min, max, 1, 1); | ||||
| } | } | ||||
| struct VPaintTx_HueSatData { | static int vertex_color_hsv_exec(bContext *C, wmOperator *op) | ||||
| 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); | Object *obact = CTX_data_active_object(C); | ||||
| const float hue = RNA_float_get(op->ptr, "h"); | |||||
| const float sat = RNA_float_get(op->ptr, "s"); | |||||
| const float val = RNA_float_get(op->ptr, "v"); | |||||
| Mesh *me; | |||||
| if (((me = BKE_mesh_from_object(obact)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| transform_active_color(*me, [&](ColorGeometry4f &color) { | |||||
| float hsv[3]; | float hsv[3]; | ||||
| rgb_to_hsv_v(col, hsv); | rgb_to_hsv_v(color, hsv); | ||||
| hsv[0] += (data->hue - 0.5f); | hsv[0] += (hue - 0.5f); | ||||
| if (hsv[0] > 1.0f) { | if (hsv[0] > 1.0f) { | ||||
| hsv[0] -= 1.0f; | hsv[0] -= 1.0f; | ||||
| } | } | ||||
| else if (hsv[0] < 0.0f) { | else if (hsv[0] < 0.0f) { | ||||
| hsv[0] += 1.0f; | hsv[0] += 1.0f; | ||||
| } | } | ||||
| hsv[1] *= data->sat; | hsv[1] *= sat; | ||||
| hsv[2] *= data->val; | hsv[2] *= val; | ||||
| hsv_to_rgb_v(hsv, r_col); | hsv_to_rgb_v(hsv, color); | ||||
| } | }); | ||||
| 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); | WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| void PAINT_OT_vertex_color_hsv(wmOperatorType *ot) | void PAINT_OT_vertex_color_hsv(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Vertex Paint Hue Saturation Value"; | ot->name = "Vertex Paint Hue Saturation Value"; | ||||
| ot->idname = "PAINT_OT_vertex_color_hsv"; | ot->idname = "PAINT_OT_vertex_color_hsv"; | ||||
| ot->description = "Adjust vertex color HSV values"; | ot->description = "Adjust vertex color HSV values"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = vertex_color_hsv_exec; | ot->exec = vertex_color_hsv_exec; | ||||
| ot->poll = vertex_paint_mode_poll; | ot->poll = vertex_paint_mode_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* params */ | /* params */ | ||||
| RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f); | 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, "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); | 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)) | static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op)) | ||||
| { | { | ||||
| Object *obact = CTX_data_active_object(C); | Object *obact = CTX_data_active_object(C); | ||||
| if (ED_vpaint_color_transform(obact, vpaint_tx_invert, nullptr)) { | Mesh *me; | ||||
| if (((me = BKE_mesh_from_object(obact)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| transform_active_color(*me, [&](ColorGeometry4f &color) { | |||||
| for (int i = 0; i < 3; i++) { | |||||
| color[i] = 1.0f - color[i]; | |||||
| } | |||||
| }); | |||||
| WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); | WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| void PAINT_OT_vertex_color_invert(wmOperatorType *ot) | void PAINT_OT_vertex_color_invert(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Vertex Paint Invert"; | ot->name = "Vertex Paint Invert"; | ||||
| ot->idname = "PAINT_OT_vertex_color_invert"; | ot->idname = "PAINT_OT_vertex_color_invert"; | ||||
| ot->description = "Invert RGB values"; | ot->description = "Invert RGB values"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = vertex_color_invert_exec; | ot->exec = vertex_color_invert_exec; | ||||
| ot->poll = vertex_paint_mode_poll; | ot->poll = vertex_paint_mode_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | 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) | static int vertex_color_levels_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Object *obact = CTX_data_active_object(C); | Object *obact = CTX_data_active_object(C); | ||||
| VPaintTx_LevelsData user_data{}; | const float gain = RNA_float_get(op->ptr, "gain"); | ||||
| user_data.gain = RNA_float_get(op->ptr, "gain"); | const float offset = RNA_float_get(op->ptr, "offset"); | ||||
| user_data.offset = RNA_float_get(op->ptr, "offset"); | |||||
| Mesh *me; | |||||
| if (((me = BKE_mesh_from_object(obact)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| transform_active_color(*me, [&](ColorGeometry4f &color) { | |||||
| for (int i = 0; i < 3; i++) { | |||||
| color[i] = gain * (color[i] + offset); | |||||
| } | |||||
| }); | |||||
| if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) { | |||||
| WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); | WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| void PAINT_OT_vertex_color_levels(wmOperatorType *ot) | void PAINT_OT_vertex_color_levels(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Vertex Paint Levels"; | ot->name = "Vertex Paint Levels"; | ||||
| ot->idname = "PAINT_OT_vertex_color_levels"; | ot->idname = "PAINT_OT_vertex_color_levels"; | ||||
| ot->description = "Adjust levels of vertex colors"; | ot->description = "Adjust levels of vertex colors"; | ||||
| Show All 15 Lines | |||||