Blender V4.5
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
8
9#include "DNA_mesh_types.h"
10#include "DNA_object_types.h"
11#include "DNA_scene_types.h"
12
13#include "BLI_array.hh"
14#include "BLI_function_ref.hh"
15#include "BLI_listbase.h"
16#include "BLI_math_base.h"
17#include "BLI_math_color.h"
18#include "BLI_vector.hh"
19
20#include "BKE_attribute_math.hh"
21#include "BKE_context.hh"
22#include "BKE_geometry_set.hh"
23#include "BKE_mesh.hh"
24
25#include "DEG_depsgraph.hh"
26
27#include "RNA_access.hh"
28#include "RNA_define.hh"
29
30#include "WM_api.hh"
31#include "WM_types.hh"
32
33#include "ED_mesh.hh"
34
35#include "paint_intern.hh" /* own include */
36#include "sculpt_intern.hh"
37
38using blender::Array;
45using blender::Vector;
46
47/* -------------------------------------------------------------------- */
50
52{
54 Mesh *mesh = BKE_mesh_from_object(ob);
55 return (ob && ELEM(ob->mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT)) &&
56 (mesh && mesh->faces_num && !mesh->deform_verts().is_empty());
57}
58
59static void tag_object_after_update(Object &object)
60{
61 BLI_assert(object.type == OB_MESH);
62 Mesh &mesh = *static_cast<Mesh *>(object.data);
64 /* NOTE: Original mesh is used for display, so tag it directly here. */
66}
67
69
70/* -------------------------------------------------------------------- */
73
75{
76 using namespace blender;
77
78 Mesh *mesh;
79 if ((mesh = BKE_mesh_from_object(&ob)) == nullptr ||
80 ED_mesh_color_ensure(mesh, nullptr) == false)
81 {
82 return false;
83 }
84
85 if (!mesh->attributes().contains(mesh->active_color_attribute)) {
87 return false;
88 }
89
90 const int active_vertex_group_index = mesh->vertex_group_active_index - 1;
91 const bDeformGroup *deform_group = static_cast<const bDeformGroup *>(
92 BLI_findlink(&mesh->vertex_group_names, active_vertex_group_index));
93 if (deform_group == nullptr) {
95 return false;
96 }
97
98 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
99
100 bke::GAttributeWriter color_attribute = attributes.lookup_for_write(
102 if (!color_attribute) {
104 return false;
105 }
106
107 /* Retrieve the vertex group with the domain and type of the existing color
108 * attribute, in order to let the attribute API handle both conversions. */
109 const GVArray vertex_group = *attributes.lookup(
110 deform_group->name,
113 if (!vertex_group) {
115 return false;
116 }
117
118 GVArraySpan interpolated{
119 attributes.adapt_domain(vertex_group, bke::AttrDomain::Point, color_attribute.domain)};
120
121 color_attribute.varray.set_all(interpolated.data());
122 color_attribute.finish();
124
125 return true;
126}
127
137
139{
140 /* identifiers */
141 ot->name = "Vertex Color from Weight";
142 ot->idname = "PAINT_OT_vertex_color_from_weight";
143 ot->description = "Convert active weight into gray scale vertex colors";
144
145 /* API callbacks. */
148
149 /* flags */
151
152 /* TODO: invert, alpha */
153}
154
156
157/* -------------------------------------------------------------------- */
160
162 const blender::bke::AttrDomain domain,
163 IndexMaskMemory &memory)
164{
165 using namespace blender;
166 const bke::AttributeAccessor attributes = mesh.attributes();
167
168 if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) {
169 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
170 ".select_poly", domain, false);
171 return IndexMask::from_bools(selection, memory);
172 }
173 if (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) {
174 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
175 ".select_vert", domain, false);
176 return IndexMask::from_bools(selection, memory);
177 }
178 return IndexMask(attributes.domain_size(domain));
179}
180
181static void face_corner_color_equalize_verts(Mesh &mesh, const IndexMask selection)
182{
183 using namespace blender;
184 const StringRef name = mesh.active_color_attribute;
185 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
186 bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(name);
187 if (!attribute) {
189 return;
190 }
191 if (attribute.domain == bke::AttrDomain::Point) {
192 return;
193 }
194
195 GVArray color_attribute_point = *attributes.lookup(name, bke::AttrDomain::Point);
196 GVArray color_attribute_corner = attributes.adapt_domain(
197 color_attribute_point, bke::AttrDomain::Point, bke::AttrDomain::Corner);
198 color_attribute_corner.materialize(selection, attribute.span.data());
199 attribute.finish();
200}
201
203{
204 using namespace blender;
205 Mesh *mesh;
206 if (((mesh = BKE_mesh_from_object(&ob)) == nullptr) ||
207 (ED_mesh_color_ensure(mesh, nullptr) == false))
208 {
209 return false;
210 }
211
212 IndexMaskMemory memory;
213 const IndexMask selection = get_selected_indices(*mesh, bke::AttrDomain::Corner, memory);
214
215 face_corner_color_equalize_verts(*mesh, selection);
216
218
219 return true;
220}
221
223{
225 if (vertex_color_smooth(*obact)) {
227 return OPERATOR_FINISHED;
228 }
229 return OPERATOR_CANCELLED;
230}
231
233{
234 /* identifiers */
235 ot->name = "Smooth Vertex Colors";
236 ot->idname = "PAINT_OT_vertex_color_smooth";
237 ot->description = "Smooth colors across vertices";
238
239 /* API callbacks. */
242
243 /* flags */
245}
246
248
249/* -------------------------------------------------------------------- */
252
254 Mesh &mesh, const FunctionRef<void(ColorGeometry4f &color)> transform_fn)
255{
256 using namespace blender;
257 const StringRef name = mesh.active_color_attribute;
258 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
259 if (!attributes.contains(name)) {
261 return;
262 }
263
264 bke::GAttributeWriter color_attribute = attributes.lookup_for_write(name);
265 if (!color_attribute) {
267 return;
268 }
269
270 IndexMaskMemory memory;
271 const IndexMask selection = get_selected_indices(mesh, color_attribute.domain, memory);
272
273 selection.foreach_segment(GrainSize(1024), [&](const IndexMaskSegment segment) {
275 [&](auto type_tag) {
276 using namespace blender;
277 using T = typename decltype(type_tag)::type;
278 for ([[maybe_unused]] const int i : segment) {
279 if constexpr (std::is_void_v<T>) {
281 }
282 else if constexpr (std::is_same_v<T, ColorGeometry4f>) {
283 ColorGeometry4f color = color_attribute.varray.get<ColorGeometry4f>(i);
284 transform_fn(color);
285 color_attribute.varray.set_by_copy(i, &color);
286 }
287 else if constexpr (std::is_same_v<T, ColorGeometry4b>) {
288 ColorGeometry4f color = color_attribute.varray.get<ColorGeometry4b>(i).decode();
289 transform_fn(color);
290 ColorGeometry4b color_encoded = color.encode();
291 color_attribute.varray.set_by_copy(i, &color_encoded);
292 }
293 }
294 });
295 });
296
297 color_attribute.finish();
298
299 DEG_id_tag_update(&mesh.id, 0);
300}
301
303 const FunctionRef<void(ColorGeometry4f &color)> transform_fn)
304{
305 using namespace blender;
306 using namespace blender::ed::sculpt_paint;
308
309 /* Ensure valid sculpt state. */
311
313
314 IndexMaskMemory memory;
315 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
316
317 Mesh &mesh = *static_cast<Mesh *>(obact.data);
318 transform_active_color_data(mesh, transform_fn);
319
320 pbvh.tag_attribute_changed(node_mask, mesh.active_color_attribute);
321
323}
324
326{
328
329 float gain, offset;
330 {
331 float brightness = RNA_float_get(op->ptr, "brightness");
332 float contrast = RNA_float_get(op->ptr, "contrast");
333 brightness /= 100.0f;
334 float delta = contrast / 200.0f;
335 /*
336 * The algorithm is by Werner D. Streidt
337 * (http://visca.com/ffactory/archives/5-99/msg00021.html)
338 * Extracted of OpenCV `demhist.c`.
339 */
340 if (contrast > 0) {
341 gain = 1.0f - delta * 2.0f;
342 gain = 1.0f / max_ff(gain, FLT_EPSILON);
343 offset = gain * (brightness - delta);
344 }
345 else {
346 delta *= -1;
347 gain = max_ff(1.0f - delta * 2.0f, 0.0f);
348 offset = gain * brightness + delta;
349 }
350 }
351
352 Mesh *mesh;
353 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
354 (ED_mesh_color_ensure(mesh, nullptr) == false))
355 {
356 return OPERATOR_CANCELLED;
357 }
358
360 for (int i = 0; i < 3; i++) {
361 color[i] = gain * color[i] + offset;
362 }
363 });
364
365 return OPERATOR_FINISHED;
366}
367
369{
370 PropertyRNA *prop;
371
372 /* identifiers */
373 ot->name = "Vertex Paint Brightness/Contrast";
374 ot->idname = "PAINT_OT_vertex_color_brightness_contrast";
375 ot->description = "Adjust vertex color brightness/contrast";
376
377 /* API callbacks. */
380
381 /* flags */
383
384 /* params */
385 const float min = -100, max = +100;
386 prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
387 prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
388 RNA_def_property_ui_range(prop, min, max, 1, 1);
389}
390
392{
394
395 const float hue = RNA_float_get(op->ptr, "h");
396 const float sat = RNA_float_get(op->ptr, "s");
397 const float val = RNA_float_get(op->ptr, "v");
398
399 Mesh *mesh;
400 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
401 (ED_mesh_color_ensure(mesh, nullptr) == false))
402 {
403 return OPERATOR_CANCELLED;
404 }
405
407 float hsv[3];
408 rgb_to_hsv_v(color, hsv);
409
410 hsv[0] += (hue - 0.5f);
411 if (hsv[0] > 1.0f) {
412 hsv[0] -= 1.0f;
413 }
414 else if (hsv[0] < 0.0f) {
415 hsv[0] += 1.0f;
416 }
417 hsv[1] *= sat;
418 hsv[2] *= val;
419
420 hsv_to_rgb_v(hsv, color);
421 });
422
423 return OPERATOR_FINISHED;
424}
425
427{
428 /* identifiers */
429 ot->name = "Vertex Paint Hue/Saturation/Value";
430 ot->idname = "PAINT_OT_vertex_color_hsv";
431 ot->description = "Adjust vertex color Hue/Saturation/Value";
432
433 /* API callbacks. */
436
437 /* flags */
439
440 /* params */
441 RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
442 RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
443 RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
444}
445
447{
449
450 Mesh *mesh;
451 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
452 (ED_mesh_color_ensure(mesh, nullptr) == false))
453 {
454 return OPERATOR_CANCELLED;
455 }
456
458 for (int i = 0; i < 3; i++) {
459 color[i] = 1.0f - color[i];
460 }
461 });
462
463 return OPERATOR_FINISHED;
464}
465
467{
468 /* identifiers */
469 ot->name = "Vertex Paint Invert";
470 ot->idname = "PAINT_OT_vertex_color_invert";
471 ot->description = "Invert RGB values";
472
473 /* API callbacks. */
476
477 /* flags */
479}
480
482{
484
485 const float gain = RNA_float_get(op->ptr, "gain");
486 const float offset = RNA_float_get(op->ptr, "offset");
487
488 Mesh *mesh;
489 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
490 (ED_mesh_color_ensure(mesh, nullptr) == false))
491 {
492 return OPERATOR_CANCELLED;
493 }
494
496 for (int i = 0; i < 3; i++) {
497 color[i] = gain * (color[i] + offset);
498 }
499 });
500
502
503 return OPERATOR_FINISHED;
504}
505
507{
508 /* identifiers */
509 ot->name = "Vertex Paint Levels";
510 ot->idname = "PAINT_OT_vertex_color_levels";
511 ot->description = "Adjust levels of vertex colors";
512
513 /* API callbacks. */
516
517 /* flags */
519
520 /* params */
522 ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
524 ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
525}
526
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
Mesh * BKE_mesh_from_object(Object *ob)
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2657
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
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:1026
@ 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
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
bool ED_mesh_color_ensure(Mesh *mesh, const char *name)
Definition mesh_data.cc:424
#define C
Definition RandGen.cpp:29
#define ND_DRAW
Definition WM_types.hh:458
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_OBJECT
Definition WM_types.hh:376
AttributeSet attributes
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
void to_static_type_tag(const Fn &fn) const
const void * data() const
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)
bool contains(StringRef attribute_id) const
GVArray adapt_domain(const GVArray &varray, const AttrDomain from_domain, const AttrDomain to_domain) const
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
int domain_size(const AttrDomain domain) const
GAttributeWriter lookup_for_write(StringRef attribute_id)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
void tag_attribute_changed(const IndexMask &node_mask, StringRef attribute_name)
Definition pbvh.cc:600
void foreach_segment(Fn &&fn) const
#define T
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2912
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2544
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:342
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
Definition BLI_color.hh:343
bool vertex_paint_mode_poll(bContext *C)
static wmOperatorStatus vertex_color_invert_exec(bContext *C, wmOperator *)
static wmOperatorStatus vertex_color_smooth_exec(bContext *C, wmOperator *)
static bool vertex_color_smooth(Object &ob)
void PAINT_OT_vertex_color_smooth(wmOperatorType *ot)
static bool vertex_weight_paint_mode_poll(bContext *C)
static wmOperatorStatus vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
void PAINT_OT_vertex_color_hsv(wmOperatorType *ot)
static wmOperatorStatus vertex_color_levels_exec(bContext *C, wmOperator *op)
static wmOperatorStatus vertex_paint_from_weight_exec(bContext *C, wmOperator *)
static IndexMask get_selected_indices(const Mesh &mesh, const blender::bke::AttrDomain domain, IndexMaskMemory &memory)
static void transform_active_color_data(Mesh &mesh, const FunctionRef< void(ColorGeometry4f &color)> transform_fn)
static bool vertex_paint_from_weight(Object &ob)
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)
static wmOperatorStatus vertex_color_hsv_exec(bContext *C, wmOperator *op)
void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot)
static void transform_active_color(bContext *C, const FunctionRef< void(ColorGeometry4f &color)> transform_fn)
void PAINT_OT_vertex_color_invert(wmOperatorType *ot)
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.cc:36
#define FLT_MAX
Definition stdcycles.h:14
ListBase vertex_group_names
char editflag
int vertex_group_active_index
int faces_num
char * active_color_attribute
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4225