Blender V4.3
paint_vertex_color_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "MEM_guardedalloc.h"
10
11#include "DNA_mesh_types.h"
12#include "DNA_object_types.h"
13#include "DNA_scene_types.h"
14
15#include "BLI_array.hh"
16#include "BLI_function_ref.hh"
17#include "BLI_math_base.h"
18#include "BLI_math_color.h"
19#include "BLI_vector.hh"
20
21#include "BKE_attribute_math.hh"
22#include "BKE_context.hh"
23#include "BKE_deform.hh"
24#include "BKE_geometry_set.hh"
25#include "BKE_mesh.hh"
26
27#include "DEG_depsgraph.hh"
28
29#include "RNA_access.hh"
30#include "RNA_define.hh"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35#include "ED_mesh.hh"
36
37#include "paint_intern.hh" /* own include */
38#include "sculpt_intern.hh"
39#include "sculpt_undo.hh"
40
41using blender::Array;
48using blender::Vector;
49
50/* -------------------------------------------------------------------- */
55{
57 Mesh *mesh = BKE_mesh_from_object(ob);
58 return (ob && ELEM(ob->mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT)) &&
59 (mesh && mesh->faces_num && !mesh->deform_verts().is_empty());
60}
61
62static void tag_object_after_update(Object &object)
63{
64 BLI_assert(object.type == OB_MESH);
65 Mesh &mesh = *static_cast<Mesh *>(object.data);
67 /* NOTE: Original mesh is used for display, so tag it directly here. */
69}
70
73/* -------------------------------------------------------------------- */
78{
79 using namespace blender;
80
81 Mesh *mesh;
82 if ((mesh = BKE_mesh_from_object(&ob)) == nullptr ||
83 ED_mesh_color_ensure(mesh, nullptr) == false)
84 {
85 return false;
86 }
87
88 if (!mesh->attributes().contains(mesh->active_color_attribute)) {
90 return false;
91 }
92
93 const int active_vertex_group_index = mesh->vertex_group_active_index - 1;
94 const bDeformGroup *deform_group = static_cast<const bDeformGroup *>(
95 BLI_findlink(&mesh->vertex_group_names, active_vertex_group_index));
96 if (deform_group == nullptr) {
98 return false;
99 }
100
101 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
102
103 bke::GAttributeWriter color_attribute = attributes.lookup_for_write(
104 mesh->active_color_attribute);
105 if (!color_attribute) {
107 return false;
108 }
109
110 /* Retrieve the vertex group with the domain and type of the existing color
111 * attribute, in order to let the attribute API handle both conversions. */
112 const GVArray vertex_group = *attributes.lookup(
113 deform_group->name,
114 bke::AttrDomain::Point,
115 bke::cpp_type_to_custom_data_type(color_attribute.varray.type()));
116 if (!vertex_group) {
118 return false;
119 }
120
121 GVArraySpan interpolated{
122 attributes.adapt_domain(vertex_group, bke::AttrDomain::Point, color_attribute.domain)};
123
124 color_attribute.varray.set_all(interpolated.data());
125 color_attribute.finish();
127
128 return true;
129}
130
132{
133 Object *obact = CTX_data_active_object(C);
134 if (vertex_paint_from_weight(*obact)) {
136 return OPERATOR_FINISHED;
137 }
138 return OPERATOR_CANCELLED;
139}
140
142{
143 /* identifiers */
144 ot->name = "Vertex Color from Weight";
145 ot->idname = "PAINT_OT_vertex_color_from_weight";
146 ot->description = "Convert active weight into gray scale vertex colors";
147
148 /* api callback */
151
152 /* flags */
154
155 /* TODO: invert, alpha */
156}
157
160/* -------------------------------------------------------------------- */
165 const blender::bke::AttrDomain domain,
166 IndexMaskMemory &memory)
167{
168 using namespace blender;
169 const bke::AttributeAccessor attributes = mesh.attributes();
170
171 if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) {
172 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
173 ".select_poly", domain, false);
174 return IndexMask::from_bools(selection, memory);
175 }
176 if (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) {
177 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
178 ".select_vert", domain, false);
179 return IndexMask::from_bools(selection, memory);
180 }
181 return IndexMask(attributes.domain_size(domain));
182}
183
184static void face_corner_color_equalize_verts(Mesh &mesh, const IndexMask selection)
185{
186 using namespace blender;
187 const StringRef name = mesh.active_color_attribute;
188 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
189 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(name);
190 if (!attribute) {
192 return;
193 }
194 if (attribute.domain == bke::AttrDomain::Point) {
195 return;
196 }
197
198 GVArray color_attribute_point = *attributes.lookup(name, bke::AttrDomain::Point);
199 GVArray color_attribute_corner = attributes.adapt_domain(
200 color_attribute_point, bke::AttrDomain::Point, bke::AttrDomain::Corner);
201 color_attribute_corner.materialize(selection, attribute.span.data());
202 attribute.finish();
203}
204
206{
207 using namespace blender;
208 Mesh *mesh;
209 if (((mesh = BKE_mesh_from_object(&ob)) == nullptr) ||
210 (ED_mesh_color_ensure(mesh, nullptr) == false))
211 {
212 return false;
213 }
214
215 IndexMaskMemory memory;
216 const IndexMask selection = get_selected_indices(*mesh, bke::AttrDomain::Corner, memory);
217
218 face_corner_color_equalize_verts(*mesh, selection);
219
221
222 return true;
223}
224
226{
227 Object *obact = CTX_data_active_object(C);
228 if (vertex_color_smooth(*obact)) {
230 return OPERATOR_FINISHED;
231 }
232 return OPERATOR_CANCELLED;
233}
234
236{
237 /* identifiers */
238 ot->name = "Smooth Vertex Colors";
239 ot->idname = "PAINT_OT_vertex_color_smooth";
240 ot->description = "Smooth colors across vertices";
241
242 /* api callbacks */
245
246 /* flags */
248}
249
252/* -------------------------------------------------------------------- */
257 Mesh &mesh, const FunctionRef<void(ColorGeometry4f &color)> transform_fn)
258{
259 using namespace blender;
260 const StringRef name = mesh.active_color_attribute;
261 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
262 if (!attributes.contains(name)) {
264 return;
265 }
266
267 bke::GAttributeWriter color_attribute = attributes.lookup_for_write(name);
268 if (!color_attribute) {
270 return;
271 }
272
273 IndexMaskMemory memory;
274 const IndexMask selection = get_selected_indices(mesh, color_attribute.domain, memory);
275
276 selection.foreach_segment(GrainSize(1024), [&](const IndexMaskSegment segment) {
277 color_attribute.varray.type().to_static_type_tag<ColorGeometry4f, ColorGeometry4b>(
278 [&](auto type_tag) {
279 using namespace blender;
280 using T = typename decltype(type_tag)::type;
281 for ([[maybe_unused]] const int i : segment) {
282 if constexpr (std::is_void_v<T>) {
284 }
285 else if constexpr (std::is_same_v<T, ColorGeometry4f>) {
286 ColorGeometry4f color = color_attribute.varray.get<ColorGeometry4f>(i);
287 transform_fn(color);
288 color_attribute.varray.set_by_copy(i, &color);
289 }
290 else if constexpr (std::is_same_v<T, ColorGeometry4b>) {
291 ColorGeometry4f color = color_attribute.varray.get<ColorGeometry4b>(i).decode();
292 transform_fn(color);
293 ColorGeometry4b color_encoded = color.encode();
294 color_attribute.varray.set_by_copy(i, &color_encoded);
295 }
296 }
297 });
298 });
299
300 color_attribute.finish();
301
302 DEG_id_tag_update(&mesh.id, 0);
303}
304
306 wmOperator *op,
307 const FunctionRef<void(ColorGeometry4f &color)> transform_fn)
308{
309 using namespace blender;
310 using namespace blender::ed::sculpt_paint;
311 const Scene &scene = *CTX_data_scene(C);
312 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
313 Object &obact = *CTX_data_active_object(C);
314
315 /* Ensure valid sculpt state. */
317
318 undo::push_begin(scene, obact, op);
319
320 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(obact);
321
322 IndexMaskMemory memory;
323 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
324 undo::push_nodes(depsgraph, obact, node_mask, undo::Type::Color);
325
326 Mesh &mesh = *static_cast<Mesh *>(obact.data);
327 transform_active_color_data(mesh, transform_fn);
328
329 pbvh.tag_attribute_changed(node_mask, mesh.active_color_attribute);
330
331 undo::push_end(obact);
333}
334
336{
337 Object *obact = CTX_data_active_object(C);
338
339 float gain, offset;
340 {
341 float brightness = RNA_float_get(op->ptr, "brightness");
342 float contrast = RNA_float_get(op->ptr, "contrast");
343 brightness /= 100.0f;
344 float delta = contrast / 200.0f;
345 /*
346 * The algorithm is by Werner D. Streidt
347 * (http://visca.com/ffactory/archives/5-99/msg00021.html)
348 * Extracted of OpenCV `demhist.c`.
349 */
350 if (contrast > 0) {
351 gain = 1.0f - delta * 2.0f;
352 gain = 1.0f / max_ff(gain, FLT_EPSILON);
353 offset = gain * (brightness - delta);
354 }
355 else {
356 delta *= -1;
357 gain = max_ff(1.0f - delta * 2.0f, 0.0f);
358 offset = gain * brightness + delta;
359 }
360 }
361
362 Mesh *mesh;
363 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
364 (ED_mesh_color_ensure(mesh, nullptr) == false))
365 {
366 return OPERATOR_CANCELLED;
367 }
368
369 transform_active_color(C, op, [&](ColorGeometry4f &color) {
370 for (int i = 0; i < 3; i++) {
371 color[i] = gain * color[i] + offset;
372 }
373 });
374
375 return OPERATOR_FINISHED;
376}
377
379{
380 PropertyRNA *prop;
381
382 /* identifiers */
383 ot->name = "Vertex Paint Brightness/Contrast";
384 ot->idname = "PAINT_OT_vertex_color_brightness_contrast";
385 ot->description = "Adjust vertex color brightness/contrast";
386
387 /* api callbacks */
390
391 /* flags */
393
394 /* params */
395 const float min = -100, max = +100;
396 prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
397 prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
398 RNA_def_property_ui_range(prop, min, max, 1, 1);
399}
400
402{
403 Object *obact = CTX_data_active_object(C);
404
405 const float hue = RNA_float_get(op->ptr, "h");
406 const float sat = RNA_float_get(op->ptr, "s");
407 const float val = RNA_float_get(op->ptr, "v");
408
409 Mesh *mesh;
410 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
411 (ED_mesh_color_ensure(mesh, nullptr) == false))
412 {
413 return OPERATOR_CANCELLED;
414 }
415
416 transform_active_color(C, op, [&](ColorGeometry4f &color) {
417 float hsv[3];
418 rgb_to_hsv_v(color, hsv);
419
420 hsv[0] += (hue - 0.5f);
421 if (hsv[0] > 1.0f) {
422 hsv[0] -= 1.0f;
423 }
424 else if (hsv[0] < 0.0f) {
425 hsv[0] += 1.0f;
426 }
427 hsv[1] *= sat;
428 hsv[2] *= val;
429
430 hsv_to_rgb_v(hsv, color);
431 });
432
433 return OPERATOR_FINISHED;
434}
435
437{
438 /* identifiers */
439 ot->name = "Vertex Paint Hue/Saturation/Value";
440 ot->idname = "PAINT_OT_vertex_color_hsv";
441 ot->description = "Adjust vertex color Hue/Saturation/Value";
442
443 /* api callbacks */
446
447 /* flags */
449
450 /* params */
451 RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
452 RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
453 RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
454}
455
457{
458 Object *obact = CTX_data_active_object(C);
459
460 Mesh *mesh;
461 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
462 (ED_mesh_color_ensure(mesh, nullptr) == false))
463 {
464 return OPERATOR_CANCELLED;
465 }
466
467 transform_active_color(C, op, [&](ColorGeometry4f &color) {
468 for (int i = 0; i < 3; i++) {
469 color[i] = 1.0f - color[i];
470 }
471 });
472
473 return OPERATOR_FINISHED;
474}
475
477{
478 /* identifiers */
479 ot->name = "Vertex Paint Invert";
480 ot->idname = "PAINT_OT_vertex_color_invert";
481 ot->description = "Invert RGB values";
482
483 /* api callbacks */
486
487 /* flags */
489}
490
492{
493 Object *obact = CTX_data_active_object(C);
494
495 const float gain = RNA_float_get(op->ptr, "gain");
496 const float offset = RNA_float_get(op->ptr, "offset");
497
498 Mesh *mesh;
499 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
500 (ED_mesh_color_ensure(mesh, nullptr) == false))
501 {
502 return OPERATOR_CANCELLED;
503 }
504
505 transform_active_color(C, op, [&](ColorGeometry4f &color) {
506 for (int i = 0; i < 3; i++) {
507 color[i] = gain * (color[i] + offset);
508 }
509 });
510
512
513 return OPERATOR_FINISHED;
514}
515
517{
518 /* identifiers */
519 ot->name = "Vertex Paint Levels";
520 ot->idname = "PAINT_OT_vertex_color_levels";
521 ot->description = "Adjust levels of vertex colors";
522
523 /* api callbacks */
526
527 /* flags */
529
530 /* params */
532 ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
534 ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
535}
536
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
support for deformation groups and hooks.
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
Mesh * BKE_mesh_from_object(Object *ob)
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2601
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float max_ff(float a, float b)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ME_EDIT_PAINT_VERT_SEL
@ ME_EDIT_PAINT_FACE_SEL
@ OB_MODE_WEIGHT_PAINT
@ OB_MODE_VERTEX_PAINT
Object is a sort of wrapper for general info.
@ OB_MESH
bool ED_mesh_color_ensure(Mesh *mesh, const char *name)
Definition mesh_data.cc:423
Read Guarded memory(de)allocation.
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_DRAW
Definition WM_types.hh:428
#define NC_OBJECT
Definition WM_types.hh:346
void materialize(void *dst) const
void get(int64_t index, void *r_value) const
void set_all(const void *src)
void set_by_copy(int64_t index, const void *value)
void tag_attribute_changed(const IndexMask &node_mask, StringRef attribute_name)
Definition pbvh.cc:593
const Depsgraph * depsgraph
bool vertex_paint_mode_poll(bContext *C)
static bool vertex_color_smooth(Object &ob)
void PAINT_OT_vertex_color_smooth(wmOperatorType *ot)
static bool vertex_weight_paint_mode_poll(bContext *C)
void PAINT_OT_vertex_color_hsv(wmOperatorType *ot)
static IndexMask get_selected_indices(const Mesh &mesh, const blender::bke::AttrDomain domain, IndexMaskMemory &memory)
static int vertex_color_levels_exec(bContext *C, wmOperator *op)
static void transform_active_color_data(Mesh &mesh, const FunctionRef< void(ColorGeometry4f &color)> transform_fn)
static bool vertex_paint_from_weight(Object &ob)
static int vertex_color_hsv_exec(bContext *C, wmOperator *op)
static void face_corner_color_equalize_verts(Mesh &mesh, const IndexMask selection)
static void tag_object_after_update(Object &object)
void PAINT_OT_vertex_color_levels(wmOperatorType *ot)
void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot)
void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
static int vertex_paint_from_weight_exec(bContext *C, wmOperator *)
void PAINT_OT_vertex_color_invert(wmOperatorType *ot)
static int vertex_color_invert_exec(bContext *C, wmOperator *op)
static void transform_active_color(bContext *C, wmOperator *op, const FunctionRef< void(ColorGeometry4f &color)> transform_fn)
static int vertex_color_smooth_exec(bContext *C, wmOperator *)
float RNA_float_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
#define min(a, b)
Definition sort.c:32
#define FLT_MAX
Definition stdcycles.h:14
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125