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
8
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/* -------------------------------------------------------------------- */
53
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
72
73/* -------------------------------------------------------------------- */
76
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(
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,
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{
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
159
160/* -------------------------------------------------------------------- */
163
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
185{
186 using namespace blender;
187 const StringRef name = mesh.active_color_attribute;
188 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
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;
217
219
221
222 return true;
223}
224
226{
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
251
252/* -------------------------------------------------------------------- */
255
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) {
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);
314
315 /* Ensure valid sculpt state. */
317
318 undo::push_begin(scene, obact, op);
319
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{
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
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{
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
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{
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
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{
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
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.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its hue
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
#define C
Definition RandGen.cpp:29
#define ND_DRAW
Definition WM_types.hh:428
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_OBJECT
Definition WM_types.hh:346
BPy_StructRNA * depsgraph
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)
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
bool contains(const StringRef attribute_id) 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:593
#define T
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2612
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
void push_nodes(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, const Type type)
void push_begin(const Scene &scene, Object &ob, const wmOperator *op)
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
Definition BLI_color.hh:338
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
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
ListBase vertex_group_names
char editflag
int vertex_group_active_index
int faces_num
char * active_color_attribute
struct PointerRNA * ptr
float max
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125