Blender V5.0
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
12#include "BLI_array.hh"
13#include "BLI_color.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,
112 bke::cpp_type_to_attribute_type(color_attribute.varray.type()));
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;
185 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
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;
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>) {
289 color_attribute.varray.get<ColorGeometry4b>(i));
290 transform_fn(color);
291 ColorGeometry4b color_encoded = color::encode(color);
292 color_attribute.varray.set_by_copy(i, &color_encoded);
293 }
294 }
295 });
296 });
297
298 color_attribute.finish();
299
300 DEG_id_tag_update(&mesh.id, 0);
301}
302
304 const FunctionRef<void(ColorGeometry4f &color)> transform_fn)
305{
306 using namespace blender;
307 using namespace blender::ed::sculpt_paint;
309
310 /* Ensure valid sculpt state. */
312
314
315 IndexMaskMemory memory;
316 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
317
318 Mesh &mesh = *static_cast<Mesh *>(obact.data);
319 transform_active_color_data(mesh, transform_fn);
320
321 pbvh.tag_attribute_changed(node_mask, mesh.active_color_attribute);
322
324}
325
327{
329
330 float gain, offset;
331 {
332 float brightness = RNA_float_get(op->ptr, "brightness");
333 float contrast = RNA_float_get(op->ptr, "contrast");
334 brightness /= 100.0f;
335 float delta = contrast / 200.0f;
336 /*
337 * The algorithm is by Werner D. Streidt
338 * (http://visca.com/ffactory/archives/5-99/msg00021.html)
339 * Extracted of OpenCV `demhist.c`.
340 */
341 if (contrast > 0) {
342 gain = 1.0f - delta * 2.0f;
343 gain = 1.0f / max_ff(gain, FLT_EPSILON);
344 offset = gain * (brightness - delta);
345 }
346 else {
347 delta *= -1;
348 gain = max_ff(1.0f - delta * 2.0f, 0.0f);
349 offset = gain * brightness + delta;
350 }
351 }
352
353 Mesh *mesh;
354 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
355 (ED_mesh_color_ensure(mesh, nullptr) == false))
356 {
357 return OPERATOR_CANCELLED;
358 }
359
361 for (int i = 0; i < 3; i++) {
362 color[i] = gain * color[i] + offset;
363 }
364 });
365
366 return OPERATOR_FINISHED;
367}
368
370{
371 PropertyRNA *prop;
372
373 /* identifiers */
374 ot->name = "Vertex Paint Brightness/Contrast";
375 ot->idname = "PAINT_OT_vertex_color_brightness_contrast";
376 ot->description = "Adjust vertex color brightness/contrast";
377
378 /* API callbacks. */
381
382 /* flags */
384
385 /* params */
386 const float min = -100, max = +100;
387 prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max);
388 prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max);
389 RNA_def_property_ui_range(prop, min, max, 1, 1);
390}
391
393{
395
396 const float hue = RNA_float_get(op->ptr, "h");
397 const float sat = RNA_float_get(op->ptr, "s");
398 const float val = RNA_float_get(op->ptr, "v");
399
400 Mesh *mesh;
401 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
402 (ED_mesh_color_ensure(mesh, nullptr) == false))
403 {
404 return OPERATOR_CANCELLED;
405 }
406
408 float hsv[3];
409 rgb_to_hsv_v(color, hsv);
410
411 hsv[0] += (hue - 0.5f);
412 if (hsv[0] > 1.0f) {
413 hsv[0] -= 1.0f;
414 }
415 else if (hsv[0] < 0.0f) {
416 hsv[0] += 1.0f;
417 }
418 hsv[1] *= sat;
419 hsv[2] *= val;
420
421 hsv_to_rgb_v(hsv, color);
422 });
423
424 return OPERATOR_FINISHED;
425}
426
428{
429 /* identifiers */
430 ot->name = "Vertex Paint Hue/Saturation/Value";
431 ot->idname = "PAINT_OT_vertex_color_hsv";
432 ot->description = "Adjust vertex color Hue/Saturation/Value";
433
434 /* API callbacks. */
437
438 /* flags */
440
441 /* params */
442 RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f);
443 RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f);
444 RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f);
445}
446
448{
450
451 Mesh *mesh;
452 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
453 (ED_mesh_color_ensure(mesh, nullptr) == false))
454 {
455 return OPERATOR_CANCELLED;
456 }
457
459 for (int i = 0; i < 3; i++) {
460 color[i] = 1.0f - color[i];
461 }
462 });
463
464 return OPERATOR_FINISHED;
465}
466
468{
469 /* identifiers */
470 ot->name = "Vertex Paint Invert";
471 ot->idname = "PAINT_OT_vertex_color_invert";
472 ot->description = "Invert RGB values";
473
474 /* API callbacks. */
477
478 /* flags */
480}
481
483{
485
486 const float gain = RNA_float_get(op->ptr, "gain");
487 const float offset = RNA_float_get(op->ptr, "offset");
488
489 Mesh *mesh;
490 if (((mesh = BKE_mesh_from_object(obact)) == nullptr) ||
491 (ED_mesh_color_ensure(mesh, nullptr) == false))
492 {
493 return OPERATOR_CANCELLED;
494 }
495
497 for (int i = 0; i < 3; i++) {
498 color[i] = gain * (color[i] + offset);
499 }
500 });
501
503
504 return OPERATOR_FINISHED;
505}
506
508{
509 /* identifiers */
510 ot->name = "Vertex Paint Levels";
511 ot->idname = "PAINT_OT_vertex_color_levels";
512 ot->description = "Adjust levels of vertex colors";
513
514 /* API callbacks. */
517
518 /* flags */
520
521 /* params */
523 ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f);
525 ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f);
526}
527
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:2797
#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:1118
@ 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:382
#define C
Definition RandGen.cpp:29
#define ND_DRAW
Definition WM_types.hh:461
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_OBJECT
Definition WM_types.hh:379
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)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
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
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:676
void foreach_segment(Fn &&fn) const
#define T
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2628
AttrType cpp_type_to_attribute_type(const CPPType &type)
BLI_INLINE ColorSceneLinear4f< Alpha > decode(const ColorSceneLinearByteEncoded4b< Alpha > &color)
Definition BLI_color.hh:77
BLI_INLINE ColorSceneLinearByteEncoded4b< Alpha > encode(const ColorSceneLinear4f< Alpha > &color)
Definition BLI_color.hh:50
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
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)
const char * name
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:4237