Blender V4.3
paint_weight.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
14#include "MEM_guardedalloc.h"
15
16#include "BLI_array_utils.h"
17#include "BLI_color.hh"
18#include "BLI_color_mix.hh"
20#include "BLI_listbase.h"
21#include "BLI_math_base.hh"
22#include "BLI_rect.h"
23#include "BLI_string.h"
24#include "BLI_task.h"
25#include "BLI_task.hh"
26#include "BLI_vector.hh"
27
28#include "DNA_brush_types.h"
29#include "DNA_mesh_types.h"
30#include "DNA_meshdata_types.h"
31#include "DNA_object_types.h"
32#include "DNA_particle_types.h"
33#include "DNA_scene_types.h"
34
35#include "RNA_access.hh"
36
37#include "BKE_attribute.hh"
38#include "BKE_brush.hh"
39#include "BKE_context.hh"
40#include "BKE_deform.hh"
41#include "BKE_editmesh.hh"
42#include "BKE_lib_id.hh"
43#include "BKE_mesh.hh"
44#include "BKE_mesh_mapping.hh"
45#include "BKE_object.hh"
46#include "BKE_object_deform.h"
47#include "BKE_paint.hh"
48#include "BKE_report.hh"
49
50#include "DEG_depsgraph.hh"
51
52#include "WM_api.hh"
53#include "WM_message.hh"
54#include "WM_toolsystem.hh"
55#include "WM_types.hh"
56
57#include "ED_image.hh"
58#include "ED_mesh.hh"
59#include "ED_object.hh"
60#include "ED_paint.hh"
61#include "ED_screen.hh"
62#include "ED_view3d.hh"
63
64/* For IMB_BlendMode only. */
65#include "IMB_imbuf.hh"
66
67#include "BKE_ccg.hh"
68#include "bmesh.hh"
69
70#include "mesh_brush_common.hh"
71#include "paint_intern.hh" /* own include */
72#include "sculpt_automask.hh"
73#include "sculpt_intern.hh"
74
75using namespace blender;
76using namespace blender::ed::sculpt_paint;
78
81 double value;
82};
83
95 int index;
102 const bool *lock;
103};
104
105struct WPaintData : public PaintModeData {
108
110
111 /* variables for auto normalize */
112 const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */
113 const bool *lock_flags;
114 const bool *vgroup_locked; /* mask of locked defbones */
115 const bool *vgroup_unlocked; /* mask of unlocked defbones */
116
117 /* variables for multipaint */
118 const bool *defbase_sel; /* set of selected groups */
119 int defbase_tot_sel; /* number of selected groups */
120 bool do_multipaint; /* true if multipaint enabled and multiple groups selected */
122
124
125 /* original weight values for use in blur/smear */
128
140};
141
142/* struct to avoid passing many args each call to do_weight_paint_vertex()
143 * this _could_ be made a part of the operators 'WPaintData' struct, or at
144 * least a member, but for now keep its own struct, initialized on every
145 * paint stroke update - campbell */
147
149
151
152 /* both must add up to 'defbase_tot' */
155
157
158 /* boolean array for locked bones,
159 * length of defbase_tot */
160 const bool *lock_flags;
161 /* boolean array for selected bones,
162 * length of defbase_tot, can't be const because of how it's passed */
163 const bool *defbase_sel;
164 /* same as WeightPaintData.vgroup_validmap,
165 * only added here for convenience */
166 const bool *vgroup_validmap;
167 /* same as WeightPaintData.vgroup_locked/unlocked,
168 * only added here for convenience */
169 const bool *vgroup_locked;
170 const bool *vgroup_unlocked;
171
177
178 float brush_alpha_value; /* result of BKE_brush_alpha_get() */
179};
180
182 MDeformVert *dvert_curr,
183 int index)
184{
185 const MDeformVert *dv_curr = &dvert_curr[index];
186 MDeformVert *dv_prev = &dvert_prev[index];
187 if (dv_prev->flag == 1) {
188 dv_prev->flag = 0;
189 BKE_defvert_copy(dv_prev, dv_curr);
190 }
191 return dv_prev;
192}
193
194static float wpaint_blend(const VPaint &wp,
195 float weight,
196 const float alpha,
197 float paintval,
198 const float /*brush_alpha_value*/,
199 const bool do_flip)
200{
201 const Brush &brush = *BKE_paint_brush_for_read(&wp.paint);
203
204 if (do_flip) {
205 switch (blend) {
206 case IMB_BLEND_MIX:
207 paintval = 1.0f - paintval;
208 break;
209 case IMB_BLEND_ADD:
211 break;
212 case IMB_BLEND_SUB:
214 break;
217 break;
218 case IMB_BLEND_DARKEN:
220 break;
221 default:
222 break;
223 }
224 }
225
226 weight = ED_wpaint_blend_tool(blend, weight, paintval, alpha);
227
228 CLAMP(weight, 0.0f, 1.0f);
229
230 return weight;
231}
232
233static float wpaint_clamp_monotonic(float oldval, float curval, float newval)
234{
235 if (newval < oldval) {
236 return std::min(newval, curval);
237 }
238 if (newval > oldval) {
239 return std::max(newval, curval);
240 }
241 return newval;
242}
243
245 float weight, float old_weight, float locked_weight, float free_weight, bool auto_normalize)
246{
247 /* In auto-normalize mode, or when there is no unlocked weight,
248 * compute based on locked weight. */
249 if (auto_normalize || free_weight <= 0.0f) {
250 if (locked_weight < 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
251 weight *= (1.0f - locked_weight);
252 }
253 else {
254 weight = 0;
255 }
256 }
257 else {
258 /* When dealing with full unlocked weight, don't paint, as it is always displayed as 1. */
259 if (old_weight >= free_weight) {
260 weight = old_weight;
261 }
262 /* Try to compute a weight value that would produce the desired effect if normalized. */
263 else if (weight < 1.0f) {
264 weight = weight * (free_weight - old_weight) / (1 - weight);
265 }
266 else {
267 weight = 1.0f;
268 }
269 }
270
271 return weight;
272}
273
274/* ----------------------------------------------------- */
275
277 const int defbase_tot,
278 const bool *vgroup_validmap)
279{
280 float sum = 0.0f, fac;
281 uint i, tot = 0;
282 MDeformWeight *dw;
283
284 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
285 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
286 tot++;
287 sum += dw->weight;
288 }
289 }
290
291 if ((tot == 0) || (sum == 1.0f)) {
292 return;
293 }
294
295 if (sum != 0.0f) {
296 fac = 1.0f / sum;
297
298 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
299 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
300 dw->weight *= fac;
301 }
302 }
303 }
304 else {
305 /* hrmf, not a factor in this case */
306 fac = 1.0f / tot;
307
308 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
309 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
310 dw->weight = fac;
311 }
312 }
313 }
314}
315
321 const int defbase_tot,
322 const bool *vgroup_validmap,
323 const bool *lock_flags)
324{
325 float sum = 0.0f, fac;
326 float sum_unlock = 0.0f;
327 float lock_weight = 0.0f;
328 uint i, tot = 0;
329 MDeformWeight *dw;
330
331 if (lock_flags == nullptr) {
332 do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap);
333 return true;
334 }
335
336 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
337 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
338 sum += dw->weight;
339
340 if (lock_flags[dw->def_nr]) {
341 lock_weight += dw->weight;
342 }
343 else {
344 tot++;
345 sum_unlock += dw->weight;
346 }
347 }
348 }
349
350 if (sum == 1.0f) {
351 return true;
352 }
353
354 if (tot == 0) {
355 return false;
356 }
357
358 if (lock_weight >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
359 /* locked groups make it impossible to fully normalize,
360 * zero out what we can and return false */
361 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
362 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
363 if (lock_flags[dw->def_nr] == false) {
364 dw->weight = 0.0f;
365 }
366 }
367 }
368
369 return (lock_weight == 1.0f);
370 }
371 if (sum_unlock != 0.0f) {
372 fac = (1.0f - lock_weight) / sum_unlock;
373
374 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
375 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
376 if (lock_flags[dw->def_nr] == false) {
377 dw->weight *= fac;
378 /* paranoid but possibly with float error */
379 CLAMP(dw->weight, 0.0f, 1.0f);
380 }
381 }
382 }
383 }
384 else {
385 /* hrmf, not a factor in this case */
386 fac = (1.0f - lock_weight) / tot;
387 /* paranoid but possibly with float error */
388 CLAMP(fac, 0.0f, 1.0f);
389
390 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
391 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
392 if (lock_flags[dw->def_nr] == false) {
393 dw->weight = fac;
394 }
395 }
396 }
397 }
398
399 return true;
400}
401
407 const int defbase_tot,
408 const bool *vgroup_validmap,
409 const bool *lock_flags,
410 const bool *lock_with_active)
411{
412 /* first pass with both active and explicitly locked groups restricted from change */
413
415 dvert, defbase_tot, vgroup_validmap, lock_with_active);
416
417 if (!success) {
427 do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_flags);
428 }
429}
430
431#if 0 /* UNUSED */
432static bool has_unselected_unlocked_bone_group(int defbase_tot,
433 bool *defbase_sel,
434 int selected,
435 const bool *lock_flags,
436 const bool *vgroup_validmap)
437{
438 int i;
439 if (defbase_tot == selected) {
440 return false;
441 }
442 for (i = 0; i < defbase_tot; i++) {
443 if (vgroup_validmap[i] && !defbase_sel[i] && !lock_flags[i]) {
444 return true;
445 }
446 }
447 return false;
448}
449#endif
451 const int defbase_tot,
452 const bool *defbase_sel,
453 float *change_p)
454{
455 int i;
456 MDeformWeight *dw;
457 float val;
458 float change = *change_p;
459
460 /* verify that the change does not cause values exceeding 1 and clamp it */
461 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
462 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
463 if (dw->weight) {
464 val = dw->weight * change;
465 if (val > 1) {
466 change = 1.0f / dw->weight;
467 }
468 }
469 }
470 }
471
472 *change_p = change;
473}
474
476 const int defbase_tot,
477 float change,
478 const bool *defbase_sel)
479{
480 int i;
481 MDeformWeight *dw;
482 float val;
483
484 /* in case the change is reduced, you need to recheck
485 * the earlier values to make sure they are not 0
486 * (precision error) */
487 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
488 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
489 if (dw->weight) {
490 val = dw->weight * change;
491 /* the value should never reach zero while multi-painting if it
492 * was nonzero beforehand */
493 if (val <= 0) {
494 return false;
495 }
496 }
497 }
498 }
499
500 return true;
501}
502
504 const int defbase_tot,
505 float change,
506 const bool *defbase_sel)
507{
508 int i;
509 MDeformWeight *dw;
510
511 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
512 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
513 if (dw->weight) {
514 dw->weight = dw->weight * change;
515 CLAMP(dw->weight, 0.0f, 1.0f);
516 }
517 }
518 }
519}
520
522 Object &ob,
523 const WeightPaintInfo &wpi,
524 const uint index,
525 float alpha,
526 float paintweight)
527{
528 Mesh *mesh = (Mesh *)ob.data;
529 MDeformVert *dv = &wpi.dvert[index];
530 bool topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
531
532 MDeformWeight *dw;
533 float weight_prev, weight_cur;
534 float dw_rel_locked = 0.0f, dw_rel_free = 1.0f;
535
536 int index_mirr;
537 int vgroup_mirr;
538
539 MDeformVert *dv_mirr;
540 MDeformWeight *dw_mirr;
541
542 /* Check if we should mirror vertex groups (X-axis). */
544 index_mirr = mesh_get_x_mirror_vert(&ob, nullptr, index, topology);
545 vgroup_mirr = wpi.mirror.index;
546
547 /* another possible error - mirror group _and_ active group are the same (which is fine),
548 * but we also are painting onto a center vertex - this would paint the same weight twice */
549 if (index_mirr == index && vgroup_mirr == wpi.active.index) {
550 index_mirr = vgroup_mirr = -1;
551 }
552 }
553 else {
554 index_mirr = vgroup_mirr = -1;
555 }
556
557 /* Check if painting should create new deform weight entries. */
558 bool restrict_to_existing = (wp.flag & VP_FLAG_VGROUP_RESTRICT) != 0;
559
560 if (wpi.do_lock_relative || wpi.do_auto_normalize) {
561 /* Without do_lock_relative only dw_rel_locked is reliable, while dw_rel_free may be fake 0. */
563 dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi.defbase_tot, wpi.vgroup_locked);
564 CLAMP(dw_rel_locked, 0.0f, 1.0f);
565
566 /* Do not create entries if there is not enough free weight to paint.
567 * This logic is the same as in wpaint_undo_lock_relative and auto-normalize. */
568 if (wpi.do_auto_normalize || dw_rel_free <= 0.0f) {
569 if (dw_rel_locked >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
570 restrict_to_existing = true;
571 }
572 }
573 }
574
575 if (restrict_to_existing) {
576 dw = BKE_defvert_find_index(dv, wpi.active.index);
577 }
578 else {
580 }
581
582 if (dw == nullptr) {
583 return;
584 }
585
586 if (index_mirr != -1) {
587 dv_mirr = &wpi.dvert[index_mirr];
588 if (wp.flag & VP_FLAG_VGROUP_RESTRICT) {
589 dw_mirr = BKE_defvert_find_index(dv_mirr, vgroup_mirr);
590
591 if (dw_mirr == nullptr) {
592 index_mirr = vgroup_mirr = -1;
593 dv_mirr = nullptr;
594 }
595 }
596 else {
597 if (index != index_mirr) {
598 dw_mirr = BKE_defvert_ensure_index(dv_mirr, vgroup_mirr);
599 }
600 else {
601 /* dv and dv_mirr are the same */
602 int totweight_prev = dv_mirr->totweight;
603 int dw_offset = int(dw - dv_mirr->dw);
604 dw_mirr = BKE_defvert_ensure_index(dv_mirr, vgroup_mirr);
605
606 /* if we added another, get our old one back */
607 if (totweight_prev != dv_mirr->totweight) {
608 dw = &dv_mirr->dw[dw_offset];
609 }
610 }
611 }
612 }
613 else {
614 dv_mirr = nullptr;
615 dw_mirr = nullptr;
616 }
617
618 weight_cur = dw->weight;
619
620 /* Handle weight caught up in locked defgroups for Lock Relative. */
621 if (wpi.do_lock_relative) {
622 weight_cur = BKE_defvert_calc_lock_relative_weight(weight_cur, dw_rel_locked, dw_rel_free);
623 }
624
626 MDeformVert *dvert_prev = ob.sculpt->mode.wpaint.dvert_prev.data();
627 MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi.dvert.data(), index);
628 if (index_mirr != -1) {
629 defweight_prev_init(dvert_prev, wpi.dvert.data(), index_mirr);
630 }
631
632 weight_prev = BKE_defvert_find_weight(dv_prev, wpi.active.index);
633
634 if (wpi.do_lock_relative) {
636 weight_prev, dv_prev, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
637 }
638 }
639 else {
640 weight_prev = weight_cur;
641 }
642
643 /* If there are no normalize-locks or multipaint,
644 * then there is no need to run the more complicated checks */
645
646 {
647 float new_weight = wpaint_blend(
648 wp, weight_prev, alpha, paintweight, wpi.brush_alpha_value, wpi.do_flip);
649
650 float weight = wpaint_clamp_monotonic(weight_prev, weight_cur, new_weight);
651
652 /* Undo the lock relative weight correction. */
653 if (wpi.do_lock_relative) {
654 if (index_mirr == index) {
655 /* When painting a center vertex with X Mirror and L/R pair,
656 * handle both groups together. This avoids weird fighting
657 * in the non-normalized weight mode. */
658 float orig_weight = dw->weight + dw_mirr->weight;
659 weight = 0.5f *
661 weight * 2, orig_weight, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
662 }
663 else {
665 weight, dw->weight, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
666 }
667
668 CLAMP(weight, 0.0f, 1.0f);
669 }
670
671 dw->weight = weight;
672
673 /* WATCH IT: take care of the ordering of applying mirror -> normalize,
674 * can give wrong results #26193, least confusing if normalize is done last */
675
676 if (index_mirr != -1) {
677 dw_mirr->weight = dw->weight;
678 }
679
680 if (wpi.do_auto_normalize) {
681 /* note on normalize - this used to be applied after painting and normalize all weights,
682 * in some ways this is good because there is feedback where the more weights involved would
683 * 'resist' so you couldn't instantly zero out other weights by painting 1.0 on the active.
684 *
685 * However this gave a problem since applying mirror, then normalize both verts
686 * the resulting weight won't match on both sides.
687 *
688 * If this 'resisting', slower normalize is nicer, we could call
689 * do_weight_paint_normalize_all() and only use...
690 * do_weight_paint_normalize_all_active() when normalizing the mirror vertex.
691 * - campbell
692 */
694 dv, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
695
696 if (index_mirr != -1) {
697 /* only normalize if this is not a center vertex,
698 * else we get a conflict, normalizing twice */
699 if (index != index_mirr) {
701 dv_mirr, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.mirror.lock);
702 }
703 else {
704 /* This case accounts for:
705 * - Painting onto a center vertex of a mesh.
706 * - X-mirror is enabled.
707 * - Auto normalize is enabled.
708 * - The group you are painting onto has a L / R version.
709 *
710 * We want L/R vgroups to have the same weight but this can't be if both are over 0.5,
711 * We _could_ have special check for that, but this would need its own
712 * normalize function which holds 2 groups from changing at once.
713 *
714 * So! just balance out the 2 weights, it keeps them equal and everything normalized.
715 *
716 * While it won't hit the desired weight immediately as the user waggles their mouse,
717 * constant painting and re-normalizing will get there. this is also just simpler logic.
718 * - campbell */
719 dw_mirr->weight = dw->weight = (dw_mirr->weight + dw->weight) * 0.5f;
720 }
721 }
722 }
723 }
724}
725
727 Object &ob,
728 const WeightPaintInfo &wpi,
729 const uint index,
730 float alpha,
731 float paintweight)
732{
733 Mesh *mesh = (Mesh *)ob.data;
734 MDeformVert *dv = &wpi.dvert[index];
735 bool topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
736
737 int index_mirr = -1;
738 MDeformVert *dv_mirr = nullptr;
739
740 float curw, curw_real, oldw, neww, change, curw_mirr, change_mirr;
741 float dw_rel_free, dw_rel_locked;
742
743 /* Check if we should mirror vertex groups (X-axis). */
745 index_mirr = mesh_get_x_mirror_vert(&ob, nullptr, index, topology);
746
747 if (!ELEM(index_mirr, -1, index)) {
748 dv_mirr = &wpi.dvert[index_mirr];
749 }
750 else {
751 index_mirr = -1;
752 }
753 }
754
755 /* compute weight change by applying the brush to average or sum of group weights */
758
759 if (curw == 0.0f) {
760 /* NOTE: no weight to assign to this vertex, could add all groups? */
761 return;
762 }
763
764 /* Handle weight caught up in locked defgroups for Lock Relative. */
765 if (wpi.do_lock_relative) {
767 dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi.defbase_tot, wpi.vgroup_locked);
768 CLAMP(dw_rel_locked, 0.0f, 1.0f);
769
770 curw = BKE_defvert_calc_lock_relative_weight(curw, dw_rel_locked, dw_rel_free);
771 }
772
774 MDeformVert *dvert_prev = ob.sculpt->mode.wpaint.dvert_prev.data();
775 MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi.dvert.data(), index);
776 if (index_mirr != -1) {
777 defweight_prev_init(dvert_prev, wpi.dvert.data(), index_mirr);
778 }
779
781 dv_prev, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
782
783 if (wpi.do_lock_relative) {
785 oldw, dv_prev, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
786 }
787 }
788 else {
789 oldw = curw;
790 }
791
792 neww = wpaint_blend(wp, oldw, alpha, paintweight, wpi.brush_alpha_value, wpi.do_flip);
793 neww = wpaint_clamp_monotonic(oldw, curw, neww);
794
795 if (wpi.do_lock_relative) {
797 neww, curw_real, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
798 }
799
800 change = neww / curw_real;
801
802 /* verify for all groups that 0 < result <= 1 */
803 multipaint_clamp_change(dv, wpi.defbase_tot, wpi.defbase_sel, &change);
804
805 if (dv_mirr != nullptr) {
807 dv_mirr, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
808
809 if (curw_mirr == 0.0f) {
810 /* can't mirror into a zero weight vertex */
811 dv_mirr = nullptr;
812 }
813 else {
814 /* mirror is changed to achieve the same collective weight value */
815 float orig = change_mirr = curw_real * change / curw_mirr;
816
817 multipaint_clamp_change(dv_mirr, wpi.defbase_tot, wpi.defbase_sel, &change_mirr);
818
819 if (!multipaint_verify_change(dv_mirr, wpi.defbase_tot, change_mirr, wpi.defbase_sel)) {
820 return;
821 }
822
823 change *= change_mirr / orig;
824 }
825 }
826
827 if (!multipaint_verify_change(dv, wpi.defbase_tot, change, wpi.defbase_sel)) {
828 return;
829 }
830
831 /* apply validated change to vertex and mirror */
832 multipaint_apply_change(dv, wpi.defbase_tot, change, wpi.defbase_sel);
833
834 if (dv_mirr != nullptr) {
835 multipaint_apply_change(dv_mirr, wpi.defbase_tot, change_mirr, wpi.defbase_sel);
836 }
837
838 if (wpi.do_auto_normalize) {
840 dv, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
841
842 if (dv_mirr != nullptr) {
844 dv_mirr, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
845 }
846 }
847}
848
849static void do_weight_paint_vertex(const VPaint &wp,
850 Object &ob,
851 const WeightPaintInfo &wpi,
852 const uint index,
853 float alpha,
854 float paintweight)
855{
856 if (wpi.do_multipaint) {
857 do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight);
858 }
859 else {
860 do_weight_paint_vertex_single(wp, ob, wpi, index, alpha, paintweight);
861 }
862}
863
864static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
865{
866 Scene &scene = *CTX_data_scene(C);
867 PaintStroke &stroke = *(PaintStroke *)op->customdata;
868 ToolSettings &ts = *scene.toolsettings;
870 Mesh &mesh = *BKE_mesh_from_object(&ob);
871 WPaintVGroupIndex vgroup_index;
872 int defbase_tot, defbase_tot_sel;
873 bool *defbase_sel;
874 SculptSession &ss = *ob.sculpt;
877
878 if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) {
879 return false;
880 }
881
882 {
883 /* check if we are attempting to paint onto a locked vertex group,
884 * and other options disallow it from doing anything useful */
885 bDeformGroup *dg;
886 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, vgroup_index.active);
887 if (dg->flag & DG_LOCK_WEIGHT) {
888 BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting");
889 return false;
890 }
891 if (vgroup_index.mirror != -1) {
892 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, vgroup_index.mirror);
893 if (dg->flag & DG_LOCK_WEIGHT) {
894 BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting");
895 return false;
896 }
897 }
898 }
899
900 /* check that multipaint groups are unlocked */
901 defbase_tot = BLI_listbase_count(&mesh.vertex_group_names);
902 defbase_sel = BKE_object_defgroup_selected_get(&ob, defbase_tot, &defbase_tot_sel);
903
904 if (ts.multipaint && defbase_tot_sel > 1) {
905 int i;
906 bDeformGroup *dg;
907
910 &ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
911 }
912
913 for (i = 0; i < defbase_tot; i++) {
914 if (defbase_sel[i]) {
915 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, i);
916 if (dg->flag & DG_LOCK_WEIGHT) {
917 BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting");
918 MEM_freeN(defbase_sel);
919 return false;
920 }
921 }
922 }
923 }
924
925 std::unique_ptr<WPaintData> wpd = std::make_unique<WPaintData>();
927
928 const Brush *brush = BKE_paint_brush_for_read(&vp.paint);
929 vwpaint::view_angle_limits_init(&wpd->normal_angle_precalc,
930 brush->falloff_angle,
931 (brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0);
932
933 wpd->active.index = vgroup_index.active;
934 wpd->mirror.index = vgroup_index.mirror;
935
936 /* multipaint */
937 wpd->defbase_tot = defbase_tot;
938 wpd->defbase_sel = defbase_sel;
939 wpd->defbase_tot_sel = defbase_tot_sel > 1 ? defbase_tot_sel : 1;
940 wpd->do_multipaint = (ts.multipaint && defbase_tot_sel > 1);
941
942 /* set up auto-normalize, and generate map for detecting which
943 * vgroups affect deform bones */
944 wpd->lock_flags = BKE_object_defgroup_lock_flags_get(&ob, wpd->defbase_tot);
945 if (ts.auto_normalize || ts.multipaint || wpd->lock_flags != nullptr || ts.wpaint_lock_relative)
946 {
947 wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(&ob, wpd->defbase_tot);
948 }
949
950 /* Compute the set of all locked deform groups when Lock Relative is active. */
951 if (ts.wpaint_lock_relative &&
953 wpd->lock_flags, wpd->vgroup_validmap, wpd->active.index) &&
954 (!wpd->do_multipaint || BKE_object_defgroup_check_lock_relative_multi(
955 defbase_tot, wpd->lock_flags, defbase_sel, defbase_tot_sel)))
956 {
957 wpd->do_lock_relative = true;
958 }
959
960 if (wpd->do_lock_relative || (ts.auto_normalize && wpd->lock_flags && !wpd->do_multipaint)) {
961 bool *unlocked = (bool *)MEM_dupallocN(wpd->vgroup_validmap);
962
963 if (wpd->lock_flags) {
964 bool *locked = (bool *)MEM_mallocN(sizeof(bool) * wpd->defbase_tot, __func__);
966 wpd->defbase_tot, wpd->lock_flags, wpd->vgroup_validmap, locked, unlocked);
967 wpd->vgroup_locked = locked;
968 }
969
970 wpd->vgroup_unlocked = unlocked;
971 }
972
973 if (wpd->do_multipaint && ts.auto_normalize) {
974 bool *tmpflags;
975 tmpflags = (bool *)MEM_mallocN(sizeof(bool) * defbase_tot, __func__);
976 if (wpd->lock_flags) {
977 BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot);
978 }
979 else {
980 memcpy(tmpflags, wpd->defbase_sel, sizeof(*tmpflags) * wpd->defbase_tot);
981 }
982 wpd->active.lock = tmpflags;
983 }
984 else if (ts.auto_normalize) {
985 bool *tmpflags;
986
987 tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) :
988 (bool *)MEM_callocN(sizeof(bool) * defbase_tot, __func__);
989 tmpflags[wpd->active.index] = true;
990 wpd->active.lock = tmpflags;
991
992 tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) :
993 (bool *)MEM_callocN(sizeof(bool) * defbase_tot, __func__);
994 tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true;
995 wpd->mirror.lock = tmpflags;
996 }
997
998 /* If not previously created, create vertex/weight paint mode session data */
1000 vwpaint::update_cache_invariants(C, vp, ss, op, mouse);
1002
1003 /* Brush may have changed after initialization. */
1004 brush = BKE_paint_brush(&vp.paint);
1006 wpd->precomputed_weight = (float *)MEM_mallocN(sizeof(float) * mesh.verts_num, __func__);
1007 }
1008
1009 if (!ob.sculpt->mode.wpaint.dvert_prev.is_empty()) {
1011 for (int i = 0; i < mesh.verts_num; i++, dv++) {
1012 /* Use to show this isn't initialized, never apply to the mesh data. */
1013 dv->flag = 1;
1014 }
1015 }
1016
1017 paint_stroke_set_mode_data(&stroke, std::move(wpd));
1018
1019 return true;
1020}
1021
1022static float wpaint_get_active_weight(const MDeformVert &dv, const WeightPaintInfo &wpi)
1023{
1024 float weight;
1025
1026 if (wpi.do_multipaint) {
1028 &dv, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
1029 }
1030 else {
1031 weight = BKE_defvert_find_weight(&dv, wpi.active.index);
1032 }
1033
1034 if (wpi.do_lock_relative) {
1036 weight, &dv, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
1037 }
1038
1039 CLAMP(weight, 0.0f, 1.0f);
1040 return weight;
1041}
1042
1044 Object &ob, const Brush &brush, WPaintData &wpd, WeightPaintInfo &wpi, Mesh &mesh)
1045{
1046 using namespace blender;
1047 if (wpd.precomputed_weight_ready &&
1049 {
1050 return;
1051 }
1052
1053 threading::parallel_for(IndexRange(mesh.verts_num), 512, [&](const IndexRange range) {
1054 for (const int i : range) {
1055 const MDeformVert &dv = wpi.dvert[i];
1056 wpd.precomputed_weight[i] = wpaint_get_active_weight(dv, wpi);
1057 }
1058 });
1059
1060 wpd.precomputed_weight_ready = true;
1061}
1062
1063/* -------------------------------------------------------------------- */
1068 const IndexMask &node_mask,
1069 FunctionRef<void(IndexRange)> fn)
1070{
1071 /* NOTE: current mirroring code cannot be run in parallel */
1073 fn(node_mask.index_range());
1074 }
1075 else {
1077 node_mask.index_range(), 1, [&](const IndexRange range) { fn(range); });
1078 }
1079}
1080
1081static void filter_factors_with_selection(const Span<bool> select_vert,
1082 const Span<int> verts,
1083 const MutableSpan<float> factors)
1084{
1085 BLI_assert(verts.size() == factors.size());
1086
1087 for (const int i : verts.index_range()) {
1088 if (!select_vert[verts[i]]) {
1089 factors[i] = 0.0f;
1090 }
1091 }
1092}
1093
1094static void do_wpaint_brush_blur(const Depsgraph &depsgraph,
1095 const Scene &scene,
1096 Object &ob,
1097 const Brush &brush,
1098 VPaint &vp,
1099 WPaintData &wpd,
1100 const WeightPaintInfo &wpi,
1101 Mesh &mesh,
1102 const IndexMask &node_mask)
1103{
1104 using namespace blender;
1105 SculptSession &ss = *ob.sculpt;
1107 const StrokeCache &cache = *ss.cache;
1108 const GroupedSpan<int> vert_to_face = mesh.vert_to_face_map();
1109
1110 float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
1112 scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
1113 const bool use_normal = vwpaint::use_normal(vp);
1114 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1115 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1116
1117 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1118 ss, brush.falloff_shape);
1119
1120 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1121 const OffsetIndices faces = mesh.faces();
1122 const Span<int> corner_verts = mesh.corner_verts();
1123 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1124 const bke::AttributeAccessor attributes = mesh.attributes();
1125 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1126 VArraySpan<bool> select_vert;
1127 if (use_vert_sel || use_face_sel) {
1128 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1129 }
1130
1131 struct LocalData {
1134 };
1136 parallel_nodes_loop_with_mirror_check(mesh, node_mask, [&](const IndexRange range) {
1137 LocalData &tls = all_tls.local();
1138 node_mask.slice(range).foreach_index([&](const int i) {
1139 const Span<int> verts = nodes[i].verts();
1140 tls.factors.resize(verts.size());
1141 const MutableSpan<float> factors = tls.factors;
1142 fill_factor_from_hide(hide_vert, verts, factors);
1143 if (!select_vert.is_empty()) {
1144 filter_factors_with_selection(select_vert, verts, factors);
1145 }
1146
1147 tls.distances.resize(verts.size());
1148 const MutableSpan<float> distances = tls.distances;
1150 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1151 filter_distances_with_radius(cache.radius, distances, factors);
1152 calc_brush_strength_factors(cache, brush, distances, factors);
1153
1154 for (const int i : verts.index_range()) {
1155 const int vert = verts[i];
1156 if (factors[i] == 0.0f) {
1157 continue;
1158 }
1159
1160 /* Get the average face weight */
1161 int total_hit_loops = 0;
1162 float weight_final = 0.0f;
1163 for (const int face : vert_to_face[vert]) {
1164 total_hit_loops += faces[face].size();
1165 for (const int vert : corner_verts.slice(faces[face])) {
1166 weight_final += wpd.precomputed_weight[vert];
1167 }
1168 }
1169
1170 if (total_hit_loops == 0) {
1171 continue;
1172 }
1173
1174 float brush_strength = cache.bstrength;
1175 const float angle_cos = use_normal ?
1176 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1177 1.0f;
1179 brush, wpd.normal_angle_precalc, angle_cos, &brush_strength))
1180 {
1181 continue;
1182 }
1183
1184 const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure;
1185
1186 if ((brush.flag & BRUSH_ACCUMULATE) == 0) {
1187 if (ss.mode.wpaint.alpha_weight[vert] < final_alpha) {
1188 ss.mode.wpaint.alpha_weight[vert] = final_alpha;
1189 }
1190 else {
1191 continue;
1192 }
1193 }
1194
1195 weight_final /= total_hit_loops;
1196 do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, weight_final);
1197 }
1198 });
1199 });
1200}
1201
1202static void do_wpaint_brush_smear(const Depsgraph &depsgraph,
1203 const Scene &scene,
1204 Object &ob,
1205 const Brush &brush,
1206 VPaint &vp,
1207 WPaintData &wpd,
1208 const WeightPaintInfo &wpi,
1209 Mesh &mesh,
1210 const IndexMask &node_mask)
1211{
1212 using namespace blender;
1213 SculptSession &ss = *ob.sculpt;
1215 const GroupedSpan<int> vert_to_face = mesh.vert_to_face_map();
1216 const StrokeCache &cache = *ss.cache;
1217 if (!cache.is_last_valid) {
1218 return;
1219 }
1220
1221 float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
1223 scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
1224 const bool use_normal = vwpaint::use_normal(vp);
1225 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1226 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1227 float brush_dir[3];
1228
1229 sub_v3_v3v3(brush_dir, cache.location_symm, cache.last_location_symm);
1230 project_plane_v3_v3v3(brush_dir, brush_dir, cache.view_normal_symm);
1231 if (normalize_v3(brush_dir) == 0.0f) {
1232 return;
1233 }
1234
1235 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1236 const OffsetIndices faces = mesh.faces();
1237 const Span<int> corner_verts = mesh.corner_verts();
1238 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1239 const bke::AttributeAccessor attributes = mesh.attributes();
1240 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1241 VArraySpan<bool> select_vert;
1242 if (use_vert_sel || use_face_sel) {
1243 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1244 }
1245
1246 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1247 ss, brush.falloff_shape);
1248
1249 struct LocalData {
1252 };
1254 parallel_nodes_loop_with_mirror_check(mesh, node_mask, [&](const IndexRange range) {
1255 LocalData &tls = all_tls.local();
1256 node_mask.slice(range).foreach_index([&](const int i) {
1257 const Span<int> verts = nodes[i].verts();
1258 tls.factors.resize(verts.size());
1259 const MutableSpan<float> factors = tls.factors;
1260 fill_factor_from_hide(hide_vert, verts, factors);
1261 if (!select_vert.is_empty()) {
1262 filter_factors_with_selection(select_vert, verts, factors);
1263 }
1264
1265 tls.distances.resize(verts.size());
1266 const MutableSpan<float> distances = tls.distances;
1268 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1269 filter_distances_with_radius(cache.radius, distances, factors);
1270 calc_brush_strength_factors(cache, brush, distances, factors);
1271
1272 for (const int i : verts.index_range()) {
1273 const int vert = verts[i];
1274 if (factors[i] == 0.0f) {
1275 continue;
1276 }
1277
1278 float brush_strength = cache.bstrength;
1279 const float angle_cos = use_normal ?
1280 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1281 1.0f;
1283 brush, wpd.normal_angle_precalc, angle_cos, &brush_strength))
1284 {
1285 continue;
1286 }
1287
1288 bool do_color = false;
1289 /* Minimum dot product between brush direction and current
1290 * to neighbor direction is 0.0, meaning orthogonal. */
1291 float stroke_dot_max = 0.0f;
1292
1293 /* Get the color of the loop in the opposite direction of the brush movement
1294 * (this callback is specifically for smear.) */
1295 float weight_final = 0.0;
1296 for (const int face : vert_to_face[vert]) {
1297 for (const int vert_other : corner_verts.slice(faces[face])) {
1298 if (vert_other == vert) {
1299 continue;
1300 }
1301
1302 /* Get the direction from the selected vert to the neighbor. */
1303 float other_dir[3];
1304 sub_v3_v3v3(other_dir, vert_positions[vert], vert_positions[vert_other]);
1305 project_plane_v3_v3v3(other_dir, other_dir, cache.view_normal_symm);
1306
1307 normalize_v3(other_dir);
1308
1309 const float stroke_dot = dot_v3v3(other_dir, brush_dir);
1310
1311 if (stroke_dot > stroke_dot_max) {
1312 stroke_dot_max = stroke_dot;
1313 weight_final = wpd.precomputed_weight[vert_other];
1314 do_color = true;
1315 }
1316 }
1317 if (!do_color) {
1318 continue;
1319 }
1320 const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure;
1321 do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, float(weight_final));
1322 }
1323 }
1324 });
1325 });
1326}
1327
1328static void do_wpaint_brush_draw(const Depsgraph &depsgraph,
1329 const Scene &scene,
1330 Object &ob,
1331 const Brush &brush,
1332 VPaint &vp,
1333 WPaintData &wpd,
1334 const WeightPaintInfo &wpi,
1335 Mesh &mesh,
1336 const float strength,
1337 const IndexMask &node_mask)
1338{
1339 using namespace blender;
1340 SculptSession &ss = *ob.sculpt;
1342
1343 const StrokeCache &cache = *ss.cache;
1344 /* NOTE: normally `BKE_brush_weight_get(scene, brush)` is used,
1345 * however in this case we calculate a new weight each time. */
1346 const float paintweight = strength;
1347 float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
1349 scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
1350 const bool use_normal = vwpaint::use_normal(vp);
1351 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1352 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1353
1354 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1355 ss, brush.falloff_shape);
1356
1357 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1358 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1359 const bke::AttributeAccessor attributes = mesh.attributes();
1360 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1361 VArraySpan<bool> select_vert;
1362 if (use_vert_sel || use_face_sel) {
1363 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1364 }
1365
1366 struct LocalData {
1369 };
1371 parallel_nodes_loop_with_mirror_check(mesh, node_mask, [&](const IndexRange range) {
1372 LocalData &tls = all_tls.local();
1373 node_mask.slice(range).foreach_index([&](const int i) {
1374 const Span<int> verts = nodes[i].verts();
1375 tls.factors.resize(verts.size());
1376 const MutableSpan<float> factors = tls.factors;
1377 fill_factor_from_hide(hide_vert, verts, factors);
1378 if (!select_vert.is_empty()) {
1379 filter_factors_with_selection(select_vert, verts, factors);
1380 }
1381
1382 tls.distances.resize(verts.size());
1383 const MutableSpan<float> distances = tls.distances;
1385 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1386 filter_distances_with_radius(cache.radius, distances, factors);
1387 calc_brush_strength_factors(cache, brush, distances, factors);
1388
1389 for (const int i : verts.index_range()) {
1390 const int vert = verts[i];
1391 if (factors[i] == 0.0f) {
1392 continue;
1393 }
1394 float brush_strength = cache.bstrength;
1395 const float angle_cos = use_normal ?
1396 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1397 1.0f;
1399 brush, wpd.normal_angle_precalc, angle_cos, &brush_strength))
1400 {
1401 continue;
1402 }
1403 const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure;
1404
1405 if ((brush.flag & BRUSH_ACCUMULATE) == 0) {
1406 if (ss.mode.wpaint.alpha_weight[vert] < final_alpha) {
1407 ss.mode.wpaint.alpha_weight[vert] = final_alpha;
1408 }
1409 else {
1410 continue;
1411 }
1412 }
1413
1414 do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, paintweight);
1415 }
1416 });
1417 });
1418}
1419
1420static float calculate_average_weight(const Depsgraph &depsgraph,
1421 Object &ob,
1422 const Mesh &mesh,
1423 const Brush &brush,
1424 const VPaint &vp,
1425 WeightPaintInfo &wpi,
1426 const IndexMask &node_mask)
1427{
1428 using namespace blender;
1429 SculptSession &ss = *ob.sculpt;
1431 const StrokeCache &cache = *ss.cache;
1432
1433 const bool use_normal = vwpaint::use_normal(vp);
1434 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1435 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1436
1437 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1438 ss, brush.falloff_shape);
1439
1440 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1441 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1442 const bke::AttributeAccessor attributes = mesh.attributes();
1443 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1444 VArraySpan<bool> select_vert;
1445 if (use_vert_sel || use_face_sel) {
1446 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1447 }
1448
1449 struct LocalData {
1452 };
1455 node_mask.index_range(),
1456 1,
1458 [&](const IndexRange range, WPaintAverageAccum accum) {
1459 LocalData &tls = all_tls.local();
1460 node_mask.slice(range).foreach_index([&](const int i) {
1461 const Span<int> verts = nodes[i].verts();
1462 tls.factors.resize(verts.size());
1463 const MutableSpan<float> factors = tls.factors;
1464 fill_factor_from_hide(hide_vert, verts, factors);
1465 if (!select_vert.is_empty()) {
1466 filter_factors_with_selection(select_vert, verts, factors);
1467 }
1468
1469 tls.distances.resize(verts.size());
1470 const MutableSpan<float> distances = tls.distances;
1471 calc_brush_distances(
1472 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1473 filter_distances_with_radius(cache.radius, distances, factors);
1474 calc_brush_strength_factors(cache, brush, distances, factors);
1475
1476 for (const int i : verts.index_range()) {
1477 const int vert = verts[i];
1478 if (factors[i] == 0.0f) {
1479 continue;
1480 }
1481 const float angle_cos = use_normal ?
1482 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1483 1.0f;
1484 if (angle_cos <= 0.0f) {
1485 continue;
1486 }
1487 const MDeformVert &dv = wpi.dvert[vert];
1488 accum.len++;
1489 accum.value += wpaint_get_active_weight(dv, wpi);
1490 }
1491 });
1492 return accum;
1493 },
1494 [](const WPaintAverageAccum &a, const WPaintAverageAccum &b) {
1495 return WPaintAverageAccum{a.len + b.len, a.value + b.value};
1496 });
1497 return math::safe_divide(value.value, double(value.len));
1498}
1499
1501 Object &ob,
1502 VPaint &vp,
1503 WPaintData &wpd,
1504 WeightPaintInfo &wpi,
1505 Mesh &mesh,
1506 const IndexMask &node_mask)
1507{
1508 const Scene &scene = *CTX_data_scene(C);
1509 const Brush &brush = *ob.sculpt->cache->brush;
1510 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1511
1515 depsgraph,
1516 scene,
1517 ob,
1518 brush,
1519 vp,
1520 wpd,
1521 wpi,
1522 mesh,
1523 calculate_average_weight(depsgraph, ob, mesh, brush, vp, wpi, node_mask),
1524 node_mask);
1525 break;
1526 }
1528 do_wpaint_brush_smear(depsgraph, scene, ob, brush, vp, wpd, wpi, mesh, node_mask);
1529 break;
1531 do_wpaint_brush_blur(depsgraph, scene, ob, brush, vp, wpd, wpi, mesh, node_mask);
1532 break;
1535 scene,
1536 ob,
1537 brush,
1538 vp,
1539 wpd,
1540 wpi,
1541 mesh,
1542 BKE_brush_weight_get(&scene, &brush),
1543 node_mask);
1544 break;
1545 }
1546}
1549/* -------------------------------------------------------------------- */
1553void ED_object_wpaintmode_enter_ex(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
1554{
1556}
1558{
1559 Main &bmain = *CTX_data_main(C);
1560 Scene &scene = *CTX_data_scene(C);
1562 ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob);
1563}
1566/* -------------------------------------------------------------------- */
1581/* -------------------------------------------------------------------- */
1586{
1587 const Object *ob = CTX_data_active_object(C);
1588
1589 return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((const Mesh *)ob->data)->faces_num;
1590}
1591
1596
1597static bool weight_paint_poll_ex(bContext *C, bool check_tool)
1598{
1599 const Object *ob = CTX_data_active_object(C);
1600 const ScrArea *area;
1601
1602 if ((ob != nullptr) && (ob->mode & OB_MODE_WEIGHT_PAINT) &&
1603 (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != nullptr) &&
1604 (area = CTX_wm_area(C)) && (area->spacetype == SPACE_VIEW3D))
1605 {
1606 ARegion *region = CTX_wm_region(C);
1607 if (ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_HUD)) {
1608 if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) {
1609 return true;
1610 }
1611 }
1612 }
1613 return false;
1614}
1615
1617{
1618 return weight_paint_poll_ex(C, true);
1619}
1620
1622{
1623 return weight_paint_poll_ex(C, false);
1624}
1625
1630{
1631 Main &bmain = *CTX_data_main(C);
1632 wmMsgBus *mbus = CTX_wm_message_bus(C);
1634 const int mode_flag = OB_MODE_WEIGHT_PAINT;
1635 const bool is_mode_set = (ob.mode & mode_flag) != 0;
1636 Scene &scene = *CTX_data_scene(C);
1637 ToolSettings &ts = *scene.toolsettings;
1638
1639 if (!is_mode_set) {
1640 if (!blender::ed::object::mode_compat_set(C, &ob, (eObjectMode)mode_flag, op->reports)) {
1641 return OPERATOR_CANCELLED;
1642 }
1643 }
1644
1645 Mesh *mesh = BKE_mesh_from_object(&ob);
1646
1647 if (is_mode_set) {
1649 }
1650 else {
1651 Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C);
1652 if (depsgraph) {
1654 }
1655 ED_object_wpaintmode_enter_ex(bmain, *depsgraph, scene, ob);
1657 }
1658
1659 blender::ed::object::posemode_set_for_weight_paint(C, &bmain, &ob, is_mode_set);
1660
1661 /* Weight-paint works by overriding colors in mesh,
1662 * so need to make sure we recalculate on enter and
1663 * exit (exit needs doing regardless because we
1664 * should re-deform).
1665 */
1666 DEG_id_tag_update(&mesh->id, 0);
1667
1669
1670 WM_msg_publish_rna_prop(mbus, &ob.id, &ob, Object, mode);
1671
1673
1674 return OPERATOR_FINISHED;
1675}
1676
1678{
1679 ot->name = "Weight Paint Mode";
1680 ot->idname = "PAINT_OT_weight_paint_toggle";
1681 ot->description = "Toggle weight paint mode in 3D view";
1682
1685
1687}
1688
1695 Object &ob,
1696 VPaint &wp,
1697 WPaintData &wpd,
1698 WeightPaintInfo &wpi,
1699 Mesh &mesh,
1700 Brush &brush,
1701 const ePaintSymmetryFlags symm,
1702 const int axis,
1703 const int i,
1704 const float angle)
1705{
1706 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1707 SculptSession &ss = *ob.sculpt;
1709 SCULPT_cache_calc_brushdata_symm(*ss.cache, symm, axis, angle);
1710
1711 IndexMaskMemory memory;
1712 const IndexMask node_mask = vwpaint::pbvh_gather_generic(depsgraph, ob, wp, brush, memory);
1713
1714 wpaint_paint_leaves(C, ob, wp, wpd, wpi, mesh, node_mask);
1715}
1716
1718 Object &ob,
1719 VPaint &wp,
1720 WPaintData &wpd,
1721 WeightPaintInfo &wpi,
1722 Mesh &mesh,
1723 Brush &brush,
1724 const ePaintSymmetryFlags symm,
1725 const int axis)
1726{
1727 for (int i = 1; i < wp.radial_symm[axis - 'X']; i++) {
1728 const float angle = (2.0 * M_PI) * i / wp.radial_symm[axis - 'X'];
1729 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, axis, i, angle);
1730 }
1731}
1732
1733/* near duplicate of: sculpt.cc's,
1734 * 'do_symmetrical_brush_actions' and 'vpaint_do_symmetrical_brush_actions'. */
1736 bContext *C, Object &ob, VPaint &wp, WPaintData &wpd, WeightPaintInfo &wpi)
1737{
1738 Brush &brush = *BKE_paint_brush(&wp.paint);
1739 Mesh &mesh = *(Mesh *)ob.data;
1740 SculptSession &ss = *ob.sculpt;
1741 StrokeCache &cache = *ss.cache;
1742 const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
1743 int i = 0;
1744
1745 /* initial stroke */
1747 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'X', 0, 0);
1748 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'X');
1749 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'Y');
1750 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'Z');
1751
1752 cache.symmetry = symm;
1753
1754 if (mesh.editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) {
1755 /* We don't do any symmetry strokes when mirroring vertex groups. */
1756 copy_v3_v3(cache.last_location, cache.location);
1757 cache.is_last_valid = true;
1758 return;
1759 }
1760
1761 /* symm is a bit combination of XYZ - 1 is mirror
1762 * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
1763 for (i = 1; i <= symm; i++) {
1764 if (symm & i && (symm != 5 || i != 3) && (symm != 6 || !ELEM(i, 3, 5))) {
1766 cache.mirror_symmetry_pass = symm;
1767 cache.radial_symmetry_pass = 0;
1768 SCULPT_cache_calc_brushdata_symm(cache, symm, 0, 0);
1769
1770 if (i & (1 << 0)) {
1771 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X', 0, 0);
1772 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X');
1773 }
1774 if (i & (1 << 1)) {
1775 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y', 0, 0);
1776 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y');
1777 }
1778 if (i & (1 << 2)) {
1779 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z', 0, 0);
1780 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z');
1781 }
1782 }
1783 }
1784 copy_v3_v3(cache.last_location, cache.location);
1785 cache.is_last_valid = true;
1786}
1787
1789 wmOperator * /*op*/,
1790 PaintStroke *stroke,
1791 PointerRNA *itemptr)
1792{
1793 Scene &scene = *CTX_data_scene(C);
1795 VPaint &wp = *ts.wpaint;
1796 const Brush &brush = *BKE_paint_brush(&wp.paint);
1798 ViewContext *vc;
1800
1801 SculptSession &ss = *ob->sculpt;
1802
1803 vwpaint::update_cache_variants(C, wp, *ob, itemptr);
1804
1805 float mat[4][4];
1806
1807 const float brush_alpha_value = BKE_brush_alpha_get(&scene, &brush);
1808
1809 /* intentionally don't initialize as nullptr, make sure we initialize all members below */
1810 WeightPaintInfo wpi;
1811
1812 if (wpd == nullptr) {
1813 /* XXX: force a redraw here, since even though we can't paint,
1814 * at least view won't freeze until stroke ends */
1816 return;
1817 }
1818
1819 vc = &wpd->vc;
1820 ob = vc->obact;
1821
1824
1825 mul_m4_m4m4(mat, vc->rv3d->persmat, ob->object_to_world().ptr());
1826
1827 Mesh &mesh = *static_cast<Mesh *>(ob->data);
1828
1829 /* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */
1830 wpi.dvert = mesh.deform_verts_for_write();
1831
1832 wpi.defbase_tot = wpd->defbase_tot;
1833 wpi.defbase_sel = wpd->defbase_sel;
1835
1837 wpi.active = wpd->active;
1838 wpi.mirror = wpd->mirror;
1839 wpi.lock_flags = wpd->lock_flags;
1841 wpi.vgroup_locked = wpd->vgroup_locked;
1843 wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip") || ss.cache->invert;
1844 wpi.do_multipaint = wpd->do_multipaint;
1845 wpi.do_auto_normalize = ((ts.auto_normalize != 0) && (wpi.vgroup_validmap != nullptr) &&
1846 (wpi.do_multipaint || wpi.vgroup_validmap[wpi.active.index]));
1849 wpi.brush_alpha_value = brush_alpha_value;
1850
1851 if (wpd->precomputed_weight) {
1852 precompute_weight_values(*ob, brush, *wpd, wpi, mesh);
1853 }
1854
1855 wpaint_do_symmetrical_brush_actions(C, *ob, wp, *wpd, wpi);
1856
1857 swap_m4m4(vc->rv3d->persmat, mat);
1858
1859 /* Calculate pivot for rotation around selection if needed.
1860 * also needed for "Frame Selected" on last stroke. */
1861 float loc_world[3];
1862 mul_v3_m4v3(loc_world, ob->object_to_world().ptr(), ss.cache->location);
1863 vwpaint::last_stroke_update(scene, loc_world);
1864
1866
1867 DEG_id_tag_update(&mesh.id, 0);
1869 swap_m4m4(wpd->vc.rv3d->persmat, mat);
1870
1871 rcti r;
1872 if (SCULPT_get_redraw_rect(*vc->region, *CTX_wm_region_view3d(C), *ob, r)) {
1873 if (ss.cache) {
1874 ss.cache->current_r = r;
1875 }
1876
1877 /* previous is not set in the current cache else
1878 * the partial rect will always grow */
1879 if (ss.cache) {
1880 if (!BLI_rcti_is_empty(&ss.cache->previous_r)) {
1882 }
1883 }
1884
1885 r.xmin += vc->region->winrct.xmin - 2;
1886 r.xmax += vc->region->winrct.xmin + 2;
1887 r.ymin += vc->region->winrct.ymin - 2;
1888 r.ymax += vc->region->winrct.ymin + 2;
1889 }
1890 ED_region_tag_redraw_partial(vc->region, &r, true);
1891}
1892
1893static void wpaint_stroke_done(const bContext *C, PaintStroke * /*stroke*/)
1894{
1896
1897 SculptSession &ss = *ob.sculpt;
1898
1899 if (ss.cache->alt_smooth) {
1901 VPaint &vp = *ts.wpaint;
1903 }
1904
1905 if (ob.particlesystem.first) {
1907 for (int i = 0; i < PSYS_TOT_VG; i++) {
1908 if (psys->vgroup[i] == BKE_object_defgroup_active_index_get(&ob)) {
1909 psys->recalc |= ID_RECALC_PSYS_RESET;
1910 break;
1911 }
1912 }
1913 }
1914 }
1915
1916 DEG_id_tag_update((ID *)ob.data, 0);
1917
1919
1920 MEM_delete(ob.sculpt->cache);
1921 ob.sculpt->cache = nullptr;
1922}
1923
1924static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1925{
1926 int retval;
1927
1929 op,
1933 nullptr,
1935 event->type);
1936
1937 if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
1939 return OPERATOR_FINISHED;
1940 }
1942
1943 OPERATOR_RETVAL_CHECK(retval);
1945
1947}
1948
1950{
1952 op,
1956 nullptr,
1958 0);
1959
1961
1962 return OPERATOR_FINISHED;
1963}
1964
1966{
1968 MEM_delete(ob.sculpt->cache);
1969 ob.sculpt->cache = nullptr;
1970
1972}
1973
1974static int wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
1975{
1976 return paint_stroke_modal(C, op, event, (PaintStroke **)&op->customdata);
1977}
1978
1980{
1981 ot->name = "Weight Paint";
1982 ot->idname = "PAINT_OT_weight_paint";
1983 ot->description = "Paint a stroke in the current vertex group's weights";
1984
1987 ot->exec = wpaint_exec;
1990
1992
1994}
1995
float BKE_brush_weight_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1160
float BKE_brush_alpha_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1153
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(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)
Main * CTX_data_main(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Depsgraph * CTX_data_depsgraph_on_load(const bContext *C)
wmMsgBus * CTX_wm_message_bus(const bContext *C)
support for deformation groups and hooks.
int BKE_object_defgroup_active_index_get(const Object *ob)
Definition deform.cc:601
float BKE_defvert_lock_relative_weight(float weight, const MDeformVert *dv, int defbase_num, const bool *defbase_locked, const bool *defbase_unlocked)
Definition deform.cc:1008
float BKE_defvert_calc_lock_relative_weight(float weight, float locked_weight, float unlocked_weight)
Definition deform.cc:980
MDeformWeight * BKE_defvert_ensure_index(MDeformVert *dv, int defgroup)
Definition deform.cc:814
float BKE_defvert_total_selected_weight(const MDeformVert *dv, int defbase_num, const bool *defbase_sel)
Definition deform.cc:941
MDeformWeight * BKE_defvert_find_index(const MDeformVert *dv, int defgroup)
Definition deform.cc:795
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:770
void BKE_defvert_copy(MDeformVert *dvert_dst, const MDeformVert *dvert_src)
Definition deform.cc:130
#define VERTEX_WEIGHT_LOCK_EPSILON
float BKE_defvert_multipaint_collective_weight(const MDeformVert *dv, int defbase_num, const bool *defbase_sel, int defbase_sel_num, bool is_normalized)
Definition deform.cc:963
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
General operations, lookup, etc. for blender objects.
Functions for dealing with objects and deform verts, used by painting and tools.
bool BKE_object_defgroup_check_lock_relative(const bool *lock_flags, const bool *validmap, int index)
void BKE_object_defgroup_split_locked_validmap(int defbase_tot, const bool *locked, const bool *deform, bool *r_locked, bool *r_unlocked)
bool * BKE_object_defgroup_validmap_get(struct Object *ob, int defbase_tot)
bool * BKE_object_defgroup_lock_flags_get(struct Object *ob, int defbase_tot)
void BKE_object_defgroup_mirror_selection(struct Object *ob, int defbase_tot, const bool *selection, bool *dg_flags_sel, int *r_dg_flags_sel_tot)
bool BKE_object_defgroup_check_lock_relative_multi(int defbase_tot, const bool *lock_flags, const bool *selected, int sel_tot)
bool * BKE_object_defgroup_selected_get(struct Object *ob, int defbase_tot, int *r_dg_flags_sel_tot)
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:654
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
Definition paint.cc:1108
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
Generic array manipulation API.
#define BLI_array_binary_or(arr, arr_a, arr_b, arr_len)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define M_PI
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void swap_m4m4(float m1[4][4], float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float n[3])
void BLI_rcti_union(struct rcti *rct_a, const struct rcti *rct_b)
bool BLI_rcti_is_empty(const struct rcti *rect)
unsigned int uint
#define CLAMP(a, b, c)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_PSYS_RESET
Definition DNA_ID.h:1050
eBrushWeightPaintType
@ WPAINT_BRUSH_TYPE_BLUR
@ WPAINT_BRUSH_TYPE_AVERAGE
@ WPAINT_BRUSH_TYPE_DRAW
@ WPAINT_BRUSH_TYPE_SMEAR
@ BRUSH_ACCUMULATE
@ BRUSH_FRONTFACE_FALLOFF
eBrushFalloffShape
@ ME_EDIT_MIRROR_VERTEX_GROUPS
@ ME_EDIT_PAINT_VERT_SEL
@ ME_EDIT_PAINT_FACE_SEL
@ ME_EDIT_MIRROR_TOPO
#define ME_USING_MIRROR_X_VERTEX_GROUPS(_me)
eObjectMode
@ OB_MODE_WEIGHT_PAINT
Object is a sort of wrapper for general info.
@ DG_LOCK_WEIGHT
@ PSYS_TOT_VG
ePaintSymmetryFlags
@ VP_FLAG_VGROUP_RESTRICT
@ RGN_TYPE_WINDOW
@ RGN_TYPE_HUD
@ SPACE_VIEW3D
@ OPERATOR_RUNNING_MODAL
#define OPERATOR_RETVAL_CHECK(ret)
int mesh_get_x_mirror_vert(Object *ob, Mesh *mesh_eval, int index, bool use_topology)
Definition meshtools.cc:901
void ED_region_tag_redraw_partial(ARegion *region, const rcti *rct, bool rebuild)
Definition area.cc:681
bool ED_operator_region_view3d_active(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void view3d_operator_needs_opengl(const bContext *C)
IMB_BlendMode
Definition IMB_imbuf.hh:186
@ IMB_BLEND_DARKEN
Definition IMB_imbuf.hh:192
@ IMB_BLEND_LIGHTEN
Definition IMB_imbuf.hh:191
@ IMB_BLEND_MIX
Definition IMB_imbuf.hh:187
@ IMB_BLEND_ADD
Definition IMB_imbuf.hh:188
@ IMB_BLEND_SUB
Definition IMB_imbuf.hh:189
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_DRAW
Definition WM_types.hh:428
#define ND_MODE
Definition WM_types.hh:412
#define NC_SCENE
Definition WM_types.hh:345
#define NC_OBJECT
Definition WM_types.hh:346
static T sum(const btAlignedObjectArray< T > &items)
const T * data() const
Definition BLI_array.hh:301
bool is_empty() const
Definition BLI_array.hh:253
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr T * data() const
Definition BLI_span.hh:540
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
void resize(const int64_t new_size)
Span< NodeT > nodes() const
IndexMask slice(IndexRange range) const
local_group_size(16, 16) .push_constant(Type b
const Depsgraph * depsgraph
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2502
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2482
void posemode_set_for_weight_paint(bContext *C, Main *bmain, Object *ob, bool is_mode_set)
bool mode_compat_set(bContext *C, Object *ob, eObjectMode mode, ReportList *reports)
bool brush_use_accumulate_ex(const Brush &brush, eObjectMode ob_mode)
void view_angle_limits_init(NormalAnglePrecalc *a, float angle, bool do_mask_normal)
void get_brush_alpha_data(const Scene &scene, const SculptSession &ss, const Brush &brush, float *r_brush_size_pressure, float *r_brush_alpha_value, float *r_brush_alpha_pressure)
bool use_normal(const VPaint &vp)
void mode_exit_generic(Object &ob, eObjectMode mode_flag)
void init_session_data(const ToolSettings &ts, Object &ob)
bool test_brush_angle_falloff(const Brush &brush, const NormalAnglePrecalc &normal_angle_precalc, const float angle_cos, float *brush_strength)
void mode_enter_generic(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob, eObjectMode mode_flag)
void update_cache_invariants(bContext *C, VPaint &vp, SculptSession &ss, wmOperator *op, const float mval[2])
void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
bool brush_use_accumulate(const VPaint &vp)
void init_stroke(Depsgraph &depsgraph, Object &ob)
void update_cache_variants(bContext *C, VPaint &vp, Object &ob, PointerRNA *ptr)
IndexMask pbvh_gather_generic(const Depsgraph &depsgraph, const Object &ob, const VPaint &wp, const Brush &brush, IndexMaskMemory &memory)
void last_stroke_update(Scene &scene, const float location[3])
int paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke)
void calc_brush_strength_factors(const StrokeCache &cache, const Brush &brush, Span< float > distances, MutableSpan< float > factors)
Definition sculpt.cc:6889
void filter_distances_with_radius(float radius, Span< float > distances, MutableSpan< float > factors)
Definition sculpt.cc:6772
void calc_brush_distances(const SculptSession &ss, Span< float3 > vert_positions, Span< int > vert_indices, eBrushFalloffShape falloff_shape, MutableSpan< float > r_distances)
Definition sculpt.cc:6722
void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke)
int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
void * paint_stroke_mode_data(PaintStroke *stroke)
void paint_stroke_free(bContext *C, wmOperator *op, PaintStroke *stroke)
PaintStroke * paint_stroke_new(bContext *C, wmOperator *op, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, StrokeRedraw redraw, StrokeDone done, int event_type)
void fill_factor_from_hide(Span< bool > hide_vert, Span< int > verts, MutableSpan< float > r_factors)
Definition sculpt.cc:6442
void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr< PaintModeData > mode_data)
T safe_divide(const T &a, const T &b)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:153
float ED_wpaint_blend_tool(int tool, float weight, float paintval, float alpha)
@ WPAINT_ENSURE_MIRROR
bool ED_wpaint_ensure_data(bContext *C, ReportList *reports, enum eWPaintFlag flag, WPaintVGroupIndex *vgroup_index)
void paint_stroke_operator_properties(wmOperatorType *ot)
static int wpaint_exec(bContext *C, wmOperator *op)
static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
static void do_weight_paint_normalize_all(MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap)
void ED_object_wpaintmode_enter_ex(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
void PAINT_OT_weight_paint(wmOperatorType *ot)
static void wpaint_do_paint(bContext *C, Object &ob, VPaint &wp, WPaintData &wpd, WeightPaintInfo &wpi, Mesh &mesh, Brush &brush, const ePaintSymmetryFlags symm, const int axis, const int i, const float angle)
static bool weight_paint_poll_ex(bContext *C, bool check_tool)
static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
bool weight_paint_mode_poll(bContext *C)
static void precompute_weight_values(Object &ob, const Brush &brush, WPaintData &wpd, WeightPaintInfo &wpi, Mesh &mesh)
static void do_wpaint_brush_smear(const Depsgraph &depsgraph, const Scene &scene, Object &ob, const Brush &brush, VPaint &vp, WPaintData &wpd, const WeightPaintInfo &wpi, Mesh &mesh, const IndexMask &node_mask)
static float wpaint_get_active_weight(const MDeformVert &dv, const WeightPaintInfo &wpi)
bool weight_paint_poll(bContext *C)
static float wpaint_blend(const VPaint &wp, float weight, const float alpha, float paintval, const float, const bool do_flip)
static void filter_factors_with_selection(const Span< bool > select_vert, const Span< int > verts, const MutableSpan< float > factors)
bool weight_paint_mode_region_view3d_poll(bContext *C)
static void do_weight_paint_vertex(const VPaint &wp, Object &ob, const WeightPaintInfo &wpi, const uint index, float alpha, float paintweight)
void ED_object_wpaintmode_exit(bContext *C)
static void wpaint_paint_leaves(bContext *C, Object &ob, VPaint &vp, WPaintData &wpd, WeightPaintInfo &wpi, Mesh &mesh, const IndexMask &node_mask)
static void do_wpaint_brush_blur(const Depsgraph &depsgraph, const Scene &scene, Object &ob, const Brush &brush, VPaint &vp, WPaintData &wpd, const WeightPaintInfo &wpi, Mesh &mesh, const IndexMask &node_mask)
static bool multipaint_verify_change(MDeformVert *dvert, const int defbase_tot, float change, const bool *defbase_sel)
static void do_wpaint_brush_draw(const Depsgraph &depsgraph, const Scene &scene, Object &ob, const Brush &brush, VPaint &vp, WPaintData &wpd, const WeightPaintInfo &wpi, Mesh &mesh, const float strength, const IndexMask &node_mask)
static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void ED_object_wpaintmode_exit_ex(Object &ob)
static float wpaint_clamp_monotonic(float oldval, float curval, float newval)
static void wpaint_stroke_done(const bContext *C, PaintStroke *)
static float wpaint_undo_lock_relative(float weight, float old_weight, float locked_weight, float free_weight, bool auto_normalize)
static void wpaint_do_radial_symmetry(bContext *C, Object &ob, VPaint &wp, WPaintData &wpd, WeightPaintInfo &wpi, Mesh &mesh, Brush &brush, const ePaintSymmetryFlags symm, const int axis)
static MDeformVert * defweight_prev_init(MDeformVert *dvert_prev, MDeformVert *dvert_curr, int index)
bool weight_paint_poll_ignore_tool(bContext *C)
static void do_weight_paint_vertex_multi(const VPaint &wp, Object &ob, const WeightPaintInfo &wpi, const uint index, float alpha, float paintweight)
static void wpaint_cancel(bContext *C, wmOperator *op)
static int wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
void ED_object_wpaintmode_enter(bContext *C, Depsgraph &depsgraph)
void PAINT_OT_weight_paint_toggle(wmOperatorType *ot)
static float calculate_average_weight(const Depsgraph &depsgraph, Object &ob, const Mesh &mesh, const Brush &brush, const VPaint &vp, WeightPaintInfo &wpi, const IndexMask &node_mask)
static void wpaint_do_symmetrical_brush_actions(bContext *C, Object &ob, VPaint &wp, WPaintData &wpd, WeightPaintInfo &wpi)
static void multipaint_apply_change(MDeformVert *dvert, const int defbase_tot, float change, const bool *defbase_sel)
static void multipaint_clamp_change(MDeformVert *dvert, const int defbase_tot, const bool *defbase_sel, float *change_p)
static void wpaint_stroke_update_step(bContext *C, wmOperator *, PaintStroke *stroke, PointerRNA *itemptr)
static void do_weight_paint_vertex_single(const VPaint &wp, Object &ob, const WeightPaintInfo &wpi, const uint index, float alpha, float paintweight)
static void do_weight_paint_normalize_all_locked_try_active(MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap, const bool *lock_flags, const bool *lock_with_active)
static void parallel_nodes_loop_with_mirror_check(const Mesh &mesh, const IndexMask &node_mask, FunctionRef< void(IndexRange)> fn)
static bool do_weight_paint_normalize_all_locked(MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap, const bool *lock_flags)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
bool SCULPT_get_redraw_rect(const ARegion &region, const RegionView3D &rv3d, const Object &ob, rcti &rect)
Definition sculpt.cc:1191
ePaintSymmetryFlags SCULPT_mesh_symmetry_xyz_get(const Object &object)
Definition sculpt.cc:186
bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2], bool force_original)
Definition sculpt.cc:4732
const float * SCULPT_brush_frontface_normal_from_falloff_shape(const SculptSession &ss, char falloff_shape)
Definition sculpt.cc:1212
void SCULPT_cache_calc_brushdata_symm(blender::ed::sculpt_paint::StrokeCache &cache, const ePaintSymmetryFlags symm, const char axis, const float angle)
Definition sculpt.cc:3346
static float brush_strength(const Sculpt &sd, const blender::ed::sculpt_paint::StrokeCache &cache, const float feather, const UnifiedPaintSettings &ups, const PaintModeSettings &)
Definition sculpt.cc:2067
float falloff_angle
char falloff_shape
short blend
char weight_brush_type
Definition DNA_ID.h:413
void * first
struct MDeformWeight * dw
unsigned int def_nr
ListBase particlesystem
struct SculptSession * sculpt
float persmat[4][4]
blender::ed::sculpt_paint::StrokeCache * cache
Definition BKE_paint.hh:427
blender::Array< MDeformVert > dvert_prev
Definition BKE_paint.hh:481
float * alpha_weight
Definition BKE_paint.hh:477
struct SculptSession::@49 mode
struct SculptSession::@49::@50 wpaint
int radial_symm[3]
RegionView3D * rv3d
Definition ED_view3d.hh:76
ARegion * region
Definition ED_view3d.hh:73
Object * obact
Definition ED_view3d.hh:71
const bool * vgroup_unlocked
NormalAnglePrecalc normal_angle_precalc
const bool * lock_flags
ViewContext vc
WeightPaintGroupData active
bool do_lock_relative
const bool * vgroup_validmap
const bool * defbase_sel
const bool * vgroup_locked
bool precomputed_weight_ready
float * precomputed_weight
WeightPaintGroupData mirror
const bool * lock_flags
const bool * vgroup_locked
MutableSpan< MDeformVert > dvert
WeightPaintGroupData mirror
const bool * vgroup_unlocked
const bool * defbase_sel
const bool * vgroup_validmap
WeightPaintGroupData active
int ymin
int ymax
int xmin
int xmax
short type
Definition WM_types.hh:722
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(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct wmOperatorType * type
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)
bool WM_toolsystem_active_tool_is_brush(const bContext *C)
void WM_toolsystem_update_from_context_view3d(bContext *C)