Blender V5.0
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
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_array_utils.h"
17#include "BLI_color_mix.hh"
19#include "BLI_listbase.h"
20#include "BLI_math_base.hh"
21#include "BLI_task.hh"
22#include "BLI_vector.hh"
23
24#include "DNA_brush_types.h"
25#include "DNA_mesh_types.h"
26#include "DNA_meshdata_types.h"
27#include "DNA_object_types.h"
28#include "DNA_particle_types.h"
29#include "DNA_scene_types.h"
30
31#include "RNA_access.hh"
32
33#include "BKE_attribute.hh"
34#include "BKE_brush.hh"
35#include "BKE_context.hh"
36#include "BKE_deform.hh"
37#include "BKE_editmesh.hh"
38#include "BKE_mesh.hh"
39#include "BKE_object_deform.h"
40#include "BKE_paint.hh"
41#include "BKE_report.hh"
42
43#include "DEG_depsgraph.hh"
44
45#include "WM_api.hh"
46#include "WM_message.hh"
47#include "WM_toolsystem.hh"
48#include "WM_types.hh"
49
50#include "ED_mesh.hh"
51#include "ED_object.hh"
52#include "ED_paint.hh"
53#include "ED_screen.hh"
54#include "ED_view3d.hh"
55
56/* For IMB_BlendMode only. */
57#include "IMB_imbuf.hh"
58
59#include "bmesh.hh"
60
61#include "RNA_define.hh"
62
63#include "mesh_brush_common.hh"
64#include "paint_intern.hh" /* own include */
65#include "sculpt_automask.hh"
66#include "sculpt_intern.hh"
67
68using namespace blender;
69using namespace blender::ed::sculpt_paint;
71
74 double value;
75};
76
88 int index;
95 const bool *lock;
96};
97
98struct WPaintData : public PaintModeData {
101
103
104 /* variables for auto normalize */
105 const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */
106 const bool *lock_flags;
107 const bool *vgroup_locked; /* mask of locked defbones */
108 const bool *vgroup_unlocked; /* mask of unlocked defbones */
109
110 /* variables for multipaint */
111 const bool *defbase_sel; /* set of selected groups */
112 int defbase_tot_sel; /* number of selected groups */
113 bool do_multipaint; /* true if multipaint enabled and multiple groups selected */
115
117
118 /* original weight values for use in blur/smear */
121
133};
134
135/* struct to avoid passing many args each call to do_weight_paint_vertex()
136 * this _could_ be made a part of the operators 'WPaintData' struct, or at
137 * least a member, but for now keep its own struct, initialized on every
138 * paint stroke update - campbell */
140
142
144
145 /* both must add up to 'defbase_tot' */
148
150
151 /* boolean array for locked bones,
152 * length of defbase_tot */
153 const bool *lock_flags;
154 /* boolean array for selected bones,
155 * length of defbase_tot, can't be const because of how it's passed */
156 const bool *defbase_sel;
157 /* same as WeightPaintData.vgroup_validmap,
158 * only added here for convenience */
159 const bool *vgroup_validmap;
160 /* same as WeightPaintData.vgroup_locked/unlocked,
161 * only added here for convenience */
162 const bool *vgroup_locked;
163 const bool *vgroup_unlocked;
164
170
171 float brush_alpha_value; /* result of BKE_brush_alpha_get() */
172};
173
175 MDeformVert *dvert_curr,
176 int index)
177{
178 const MDeformVert *dv_curr = &dvert_curr[index];
179 MDeformVert *dv_prev = &dvert_prev[index];
180 if (dv_prev->flag == 1) {
181 dv_prev->flag = 0;
182 BKE_defvert_copy(dv_prev, dv_curr);
183 }
184 return dv_prev;
185}
186
187static float wpaint_blend(const VPaint &wp,
188 float weight,
189 const float alpha,
190 float paintval,
191 const float /*brush_alpha_value*/,
192 const bool do_flip)
193{
194 const Brush &brush = *BKE_paint_brush_for_read(&wp.paint);
196
197 if (do_flip) {
198 switch (blend) {
199 case IMB_BLEND_MIX:
200 paintval = 1.0f - paintval;
201 break;
202 case IMB_BLEND_ADD:
204 break;
205 case IMB_BLEND_SUB:
207 break;
210 break;
211 case IMB_BLEND_DARKEN:
213 break;
214 default:
215 break;
216 }
217 }
218
219 weight = ED_wpaint_blend_tool(blend, weight, paintval, alpha);
220
221 CLAMP(weight, 0.0f, 1.0f);
222 /* The following is a reasonable lower bound for values that a user may want for weight values,
223 * without this rounding, attempting to paint to an exact value of 0.0 becomes tedious. */
224 constexpr float threshold = 0.0001f;
225 return weight < threshold ? 0.0f : weight;
226}
227
228static float wpaint_clamp_monotonic(float oldval, float curval, float newval)
229{
230 if (newval < oldval) {
231 return std::min(newval, curval);
232 }
233 if (newval > oldval) {
234 return std::max(newval, curval);
235 }
236 return newval;
237}
238
240 float weight, float old_weight, float locked_weight, float free_weight, bool auto_normalize)
241{
242 /* In auto-normalize mode, or when there is no unlocked weight,
243 * compute based on locked weight. */
244 if (auto_normalize || free_weight <= 0.0f) {
245 if (locked_weight < 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
246 weight *= (1.0f - locked_weight);
247 }
248 else {
249 weight = 0;
250 }
251 }
252 else {
253 /* When dealing with full unlocked weight, don't paint, as it is always displayed as 1. */
254 if (old_weight >= free_weight) {
255 weight = old_weight;
256 }
257 /* Try to compute a weight value that would produce the desired effect if normalized. */
258 else if (weight < 1.0f) {
259 weight = weight * (free_weight - old_weight) / (1 - weight);
260 }
261 else {
262 weight = 1.0f;
263 }
264 }
265
266 return weight;
267}
268
269/* ----------------------------------------------------- */
270
272 const int defbase_tot,
273 const bool *vgroup_validmap)
274{
275 float sum = 0.0f, fac;
276 uint i, tot = 0;
277 MDeformWeight *dw;
278
279 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
280 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
281 tot++;
282 sum += dw->weight;
283 }
284 }
285
286 if ((tot == 0) || (sum == 1.0f)) {
287 return;
288 }
289
290 if (sum != 0.0f) {
291 fac = 1.0f / sum;
292
293 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
294 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
295 dw->weight *= fac;
296 }
297 }
298 }
299 else {
300 /* hrmf, not a factor in this case */
301 fac = 1.0f / tot;
302
303 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
304 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
305 dw->weight = fac;
306 }
307 }
308 }
309}
310
316 const int defbase_tot,
317 const bool *vgroup_validmap,
318 const bool *lock_flags)
319{
320 float sum = 0.0f, fac;
321 float sum_unlock = 0.0f;
322 float lock_weight = 0.0f;
323 uint i, tot = 0;
324 MDeformWeight *dw;
325
326 if (lock_flags == nullptr) {
327 do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap);
328 return true;
329 }
330
331 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
332 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
333 sum += dw->weight;
334
335 if (lock_flags[dw->def_nr]) {
336 lock_weight += dw->weight;
337 }
338 else {
339 tot++;
340 sum_unlock += dw->weight;
341 }
342 }
343 }
344
345 if (sum == 1.0f) {
346 return true;
347 }
348
349 if (tot == 0) {
350 return false;
351 }
352
353 if (lock_weight >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
354 /* locked groups make it impossible to fully normalize,
355 * zero out what we can and return false */
356 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
357 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
358 if (lock_flags[dw->def_nr] == false) {
359 dw->weight = 0.0f;
360 }
361 }
362 }
363
364 return (lock_weight == 1.0f);
365 }
366 if (sum_unlock != 0.0f) {
367 fac = (1.0f - lock_weight) / sum_unlock;
368
369 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
370 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
371 if (lock_flags[dw->def_nr] == false) {
372 dw->weight *= fac;
373 /* paranoid but possibly with float error */
374 CLAMP(dw->weight, 0.0f, 1.0f);
375 }
376 }
377 }
378 }
379 else {
380 /* hrmf, not a factor in this case */
381 fac = (1.0f - lock_weight) / tot;
382 /* paranoid but possibly with float error */
383 CLAMP(fac, 0.0f, 1.0f);
384
385 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
386 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
387 if (lock_flags[dw->def_nr] == false) {
388 dw->weight = fac;
389 }
390 }
391 }
392 }
393
394 return true;
395}
396
402 const int defbase_tot,
403 const bool *vgroup_validmap,
404 const bool *lock_flags,
405 const bool *lock_with_active)
406{
407 /* first pass with both active and explicitly locked groups restricted from change */
408
410 dvert, defbase_tot, vgroup_validmap, lock_with_active);
411
412 if (!success) {
422 do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_flags);
423 }
424}
425
426#if 0 /* UNUSED */
427static bool has_unselected_unlocked_bone_group(int defbase_tot,
428 bool *defbase_sel,
429 int selected,
430 const bool *lock_flags,
431 const bool *vgroup_validmap)
432{
433 int i;
434 if (defbase_tot == selected) {
435 return false;
436 }
437 for (i = 0; i < defbase_tot; i++) {
438 if (vgroup_validmap[i] && !defbase_sel[i] && !lock_flags[i]) {
439 return true;
440 }
441 }
442 return false;
443}
444#endif
446 const int defbase_tot,
447 const bool *defbase_sel,
448 float *change_p)
449{
450 int i;
451 MDeformWeight *dw;
452 float val;
453 float change = *change_p;
454
455 /* verify that the change does not cause values exceeding 1 and clamp it */
456 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
457 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
458 if (dw->weight) {
459 val = dw->weight * change;
460 if (val > 1) {
461 change = 1.0f / dw->weight;
462 }
463 }
464 }
465 }
466
467 *change_p = change;
468}
469
471 const int defbase_tot,
472 float change,
473 const bool *defbase_sel)
474{
475 int i;
476 MDeformWeight *dw;
477 float val;
478
479 /* in case the change is reduced, you need to recheck
480 * the earlier values to make sure they are not 0
481 * (precision error) */
482 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
483 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
484 if (dw->weight) {
485 val = dw->weight * change;
486 /* the value should never reach zero while multi-painting if it
487 * was nonzero beforehand */
488 if (val <= 0) {
489 return false;
490 }
491 }
492 }
493 }
494
495 return true;
496}
497
499 const int defbase_tot,
500 float change,
501 const bool *defbase_sel)
502{
503 int i;
504 MDeformWeight *dw;
505
506 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
507 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
508 if (dw->weight) {
509 dw->weight = dw->weight * change;
510 CLAMP(dw->weight, 0.0f, 1.0f);
511 }
512 }
513 }
514}
515
517 Object &ob,
518 const WeightPaintInfo &wpi,
519 const uint index,
520 float alpha,
521 float paintweight)
522{
523 Mesh *mesh = (Mesh *)ob.data;
524 MDeformVert *dv = &wpi.dvert[index];
525 bool topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
526
527 MDeformWeight *dw;
528 float weight_prev, weight_cur;
529 float dw_rel_locked = 0.0f, dw_rel_free = 1.0f;
530
531 int index_mirr;
532 int vgroup_mirr;
533
534 MDeformVert *dv_mirr;
535 MDeformWeight *dw_mirr;
536
537 /* Check if we should mirror vertex groups (X-axis). */
539 index_mirr = mesh_get_x_mirror_vert(&ob, nullptr, index, topology);
540 vgroup_mirr = wpi.mirror.index;
541
542 /* another possible error - mirror group _and_ active group are the same (which is fine),
543 * but we also are painting onto a center vertex - this would paint the same weight twice */
544 if (index_mirr == index && vgroup_mirr == wpi.active.index) {
545 index_mirr = vgroup_mirr = -1;
546 }
547 }
548 else {
549 index_mirr = vgroup_mirr = -1;
550 }
551
552 /* Check if painting should create new deform weight entries. */
553 bool restrict_to_existing = (wp.flag & VP_FLAG_VGROUP_RESTRICT) != 0;
554
555 if (wpi.do_lock_relative || wpi.do_auto_normalize) {
556 /* Without do_lock_relative only dw_rel_locked is reliable, while dw_rel_free may be fake 0. */
558 dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi.defbase_tot, wpi.vgroup_locked);
559 CLAMP(dw_rel_locked, 0.0f, 1.0f);
560
561 /* Do not create entries if there is not enough free weight to paint.
562 * This logic is the same as in wpaint_undo_lock_relative and auto-normalize. */
563 if (wpi.do_auto_normalize || dw_rel_free <= 0.0f) {
564 if (dw_rel_locked >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
565 restrict_to_existing = true;
566 }
567 }
568 }
569
570 if (restrict_to_existing) {
571 dw = BKE_defvert_find_index(dv, wpi.active.index);
572 }
573 else {
575 }
576
577 if (dw == nullptr) {
578 return;
579 }
580
581 if (index_mirr != -1) {
582 dv_mirr = &wpi.dvert[index_mirr];
583 if (wp.flag & VP_FLAG_VGROUP_RESTRICT) {
584 dw_mirr = BKE_defvert_find_index(dv_mirr, vgroup_mirr);
585
586 if (dw_mirr == nullptr) {
587 index_mirr = vgroup_mirr = -1;
588 dv_mirr = nullptr;
589 }
590 }
591 else {
592 if (index != index_mirr) {
593 dw_mirr = BKE_defvert_ensure_index(dv_mirr, vgroup_mirr);
594 }
595 else {
596 /* dv and dv_mirr are the same */
597 int totweight_prev = dv_mirr->totweight;
598 int dw_offset = int(dw - dv_mirr->dw);
599 dw_mirr = BKE_defvert_ensure_index(dv_mirr, vgroup_mirr);
600
601 /* if we added another, get our old one back */
602 if (totweight_prev != dv_mirr->totweight) {
603 dw = &dv_mirr->dw[dw_offset];
604 }
605 }
606 }
607 }
608 else {
609 dv_mirr = nullptr;
610 dw_mirr = nullptr;
611 }
612
613 weight_cur = dw->weight;
614
615 /* Handle weight caught up in locked defgroups for Lock Relative. */
616 if (wpi.do_lock_relative) {
617 weight_cur = BKE_defvert_calc_lock_relative_weight(weight_cur, dw_rel_locked, dw_rel_free);
618 }
619
621 MDeformVert *dvert_prev = ob.sculpt->mode.wpaint.dvert_prev.data();
622 MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi.dvert.data(), index);
623 if (index_mirr != -1) {
624 defweight_prev_init(dvert_prev, wpi.dvert.data(), index_mirr);
625 }
626
627 weight_prev = BKE_defvert_find_weight(dv_prev, wpi.active.index);
628
629 if (wpi.do_lock_relative) {
631 weight_prev, dv_prev, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
632 }
633 }
634 else {
635 weight_prev = weight_cur;
636 }
637
638 /* If there are no normalize-locks or multipaint,
639 * then there is no need to run the more complicated checks */
640
641 {
642 float new_weight = wpaint_blend(
643 wp, weight_prev, alpha, paintweight, wpi.brush_alpha_value, wpi.do_flip);
644
645 float weight = wpaint_clamp_monotonic(weight_prev, weight_cur, new_weight);
646
647 /* Undo the lock relative weight correction. */
648 if (wpi.do_lock_relative) {
649 if (index_mirr == index) {
650 /* When painting a center vertex with X Mirror and L/R pair,
651 * handle both groups together. This avoids weird fighting
652 * in the non-normalized weight mode. */
653 float orig_weight = dw->weight + dw_mirr->weight;
654 weight = 0.5f *
656 weight * 2, orig_weight, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
657 }
658 else {
660 weight, dw->weight, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
661 }
662
663 CLAMP(weight, 0.0f, 1.0f);
664 }
665
666 dw->weight = weight;
667
668 /* WATCH IT: take care of the ordering of applying mirror -> normalize,
669 * can give wrong results #26193, least confusing if normalize is done last */
670
671 if (index_mirr != -1) {
672 dw_mirr->weight = dw->weight;
673 }
674
675 if (wpi.do_auto_normalize) {
676 /* note on normalize - this used to be applied after painting and normalize all weights,
677 * in some ways this is good because there is feedback where the more weights involved would
678 * 'resist' so you couldn't instantly zero out other weights by painting 1.0 on the active.
679 *
680 * However this gave a problem since applying mirror, then normalize both verts
681 * the resulting weight won't match on both sides.
682 *
683 * If this 'resisting', slower normalize is nicer, we could call
684 * do_weight_paint_normalize_all() and only use...
685 * do_weight_paint_normalize_all_active() when normalizing the mirror vertex.
686 * - campbell
687 */
689 dv, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
690
691 if (index_mirr != -1) {
692 /* only normalize if this is not a center vertex,
693 * else we get a conflict, normalizing twice */
694 if (index != index_mirr) {
696 dv_mirr, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.mirror.lock);
697 }
698 else {
699 /* This case accounts for:
700 * - Painting onto a center vertex of a mesh.
701 * - X-mirror is enabled.
702 * - Auto normalize is enabled.
703 * - The group you are painting onto has a L / R version.
704 *
705 * We want L/R vgroups to have the same weight but this can't be if both are over 0.5,
706 * We _could_ have special check for that, but this would need its own
707 * normalize function which holds 2 groups from changing at once.
708 *
709 * So! just balance out the 2 weights, it keeps them equal and everything normalized.
710 *
711 * While it won't hit the desired weight immediately as the user waggles their mouse,
712 * constant painting and re-normalizing will get there. this is also just simpler logic.
713 * - campbell */
714 dw_mirr->weight = dw->weight = (dw_mirr->weight + dw->weight) * 0.5f;
715 }
716 }
717 }
718 }
719}
720
722 Object &ob,
723 const WeightPaintInfo &wpi,
724 const uint index,
725 float alpha,
726 float paintweight)
727{
728 Mesh *mesh = (Mesh *)ob.data;
729 MDeformVert *dv = &wpi.dvert[index];
730 bool topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
731
732 int index_mirr = -1;
733 MDeformVert *dv_mirr = nullptr;
734
735 float curw, curw_real, oldw, neww, change, curw_mirr, change_mirr;
736 float dw_rel_free, dw_rel_locked;
737
738 /* Check if we should mirror vertex groups (X-axis). */
740 index_mirr = mesh_get_x_mirror_vert(&ob, nullptr, index, topology);
741
742 if (!ELEM(index_mirr, -1, index)) {
743 dv_mirr = &wpi.dvert[index_mirr];
744 }
745 else {
746 index_mirr = -1;
747 }
748 }
749
750 /* compute weight change by applying the brush to average or sum of group weights */
753
754 if (curw == 0.0f) {
755 /* NOTE: no weight to assign to this vertex, could add all groups? */
756 return;
757 }
758
759 /* Handle weight caught up in locked defgroups for Lock Relative. */
760 if (wpi.do_lock_relative) {
762 dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi.defbase_tot, wpi.vgroup_locked);
763 CLAMP(dw_rel_locked, 0.0f, 1.0f);
764
765 curw = BKE_defvert_calc_lock_relative_weight(curw, dw_rel_locked, dw_rel_free);
766 }
767
769 MDeformVert *dvert_prev = ob.sculpt->mode.wpaint.dvert_prev.data();
770 MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi.dvert.data(), index);
771 if (index_mirr != -1) {
772 defweight_prev_init(dvert_prev, wpi.dvert.data(), index_mirr);
773 }
774
776 dv_prev, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
777
778 if (wpi.do_lock_relative) {
780 oldw, dv_prev, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
781 }
782 }
783 else {
784 oldw = curw;
785 }
786
787 neww = wpaint_blend(wp, oldw, alpha, paintweight, wpi.brush_alpha_value, wpi.do_flip);
788 neww = wpaint_clamp_monotonic(oldw, curw, neww);
789
790 if (wpi.do_lock_relative) {
792 neww, curw_real, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
793 }
794
795 change = neww / curw_real;
796
797 /* verify for all groups that 0 < result <= 1 */
798 multipaint_clamp_change(dv, wpi.defbase_tot, wpi.defbase_sel, &change);
799
800 if (dv_mirr != nullptr) {
802 dv_mirr, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
803
804 if (curw_mirr == 0.0f) {
805 /* can't mirror into a zero weight vertex */
806 dv_mirr = nullptr;
807 }
808 else {
809 /* mirror is changed to achieve the same collective weight value */
810 float orig = change_mirr = curw_real * change / curw_mirr;
811
812 multipaint_clamp_change(dv_mirr, wpi.defbase_tot, wpi.defbase_sel, &change_mirr);
813
814 if (!multipaint_verify_change(dv_mirr, wpi.defbase_tot, change_mirr, wpi.defbase_sel)) {
815 return;
816 }
817
818 change *= change_mirr / orig;
819 }
820 }
821
822 if (!multipaint_verify_change(dv, wpi.defbase_tot, change, wpi.defbase_sel)) {
823 return;
824 }
825
826 /* apply validated change to vertex and mirror */
827 multipaint_apply_change(dv, wpi.defbase_tot, change, wpi.defbase_sel);
828
829 if (dv_mirr != nullptr) {
830 multipaint_apply_change(dv_mirr, wpi.defbase_tot, change_mirr, wpi.defbase_sel);
831 }
832
833 if (wpi.do_auto_normalize) {
835 dv, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
836
837 if (dv_mirr != nullptr) {
839 dv_mirr, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
840 }
841 }
842}
843
844static void do_weight_paint_vertex(const VPaint &wp,
845 Object &ob,
846 const WeightPaintInfo &wpi,
847 const uint index,
848 float alpha,
849 float paintweight)
850{
851 if (wpi.do_multipaint) {
852 do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight);
853 }
854 else {
855 do_weight_paint_vertex_single(wp, ob, wpi, index, alpha, paintweight);
856 }
857}
858
859static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
860{
861 Scene &scene = *CTX_data_scene(C);
862 PaintStroke &stroke = *(PaintStroke *)op->customdata;
863 ToolSettings &ts = *scene.toolsettings;
866 WPaintVGroupIndex vgroup_index;
867 int defbase_tot, defbase_tot_sel;
868 bool *defbase_sel;
869 SculptSession &ss = *ob.sculpt;
872
873 if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) {
874 return false;
875 }
876
877 {
878 /* check if we are attempting to paint onto a locked vertex group,
879 * and other options disallow it from doing anything useful */
880 bDeformGroup *dg;
881 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, vgroup_index.active);
882 if (dg->flag & DG_LOCK_WEIGHT) {
883 BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting");
884 return false;
885 }
886 if (vgroup_index.mirror != -1) {
887 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, vgroup_index.mirror);
888 if (dg->flag & DG_LOCK_WEIGHT) {
889 BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting");
890 return false;
891 }
892 }
893 }
894
895 /* check that multipaint groups are unlocked */
896 defbase_tot = BLI_listbase_count(&mesh.vertex_group_names);
897 defbase_sel = BKE_object_defgroup_selected_get(&ob, defbase_tot, &defbase_tot_sel);
898
899 if (ts.multipaint && defbase_tot_sel > 1) {
900 int i;
901 bDeformGroup *dg;
902
905 &ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
906 }
907
908 for (i = 0; i < defbase_tot; i++) {
909 if (defbase_sel[i]) {
910 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, i);
911 if (dg->flag & DG_LOCK_WEIGHT) {
912 BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting");
913 MEM_freeN(defbase_sel);
914 return false;
915 }
916 }
917 }
918 }
919
920 std::unique_ptr<WPaintData> wpd = std::make_unique<WPaintData>();
922
923 const Brush *brush = BKE_paint_brush_for_read(&vp.paint);
924 vwpaint::view_angle_limits_init(&wpd->normal_angle_precalc,
925 brush->falloff_angle,
926 (brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0);
927
928 wpd->active.index = vgroup_index.active;
929 wpd->mirror.index = vgroup_index.mirror;
930
931 /* multipaint */
932 wpd->defbase_tot = defbase_tot;
933 wpd->defbase_sel = defbase_sel;
934 wpd->defbase_tot_sel = defbase_tot_sel > 1 ? defbase_tot_sel : 1;
935 wpd->do_multipaint = (ts.multipaint && defbase_tot_sel > 1);
936
937 /* set up auto-normalize, and generate map for detecting which
938 * vgroups affect deform bones */
939 wpd->lock_flags = BKE_object_defgroup_lock_flags_get(&ob, wpd->defbase_tot);
940 if (ts.auto_normalize || ts.multipaint || wpd->lock_flags != nullptr || ts.wpaint_lock_relative)
941 {
942 wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(&ob, wpd->defbase_tot);
943 }
944
945 /* Compute the set of all locked deform groups when Lock Relative is active. */
946 if (ts.wpaint_lock_relative &&
948 wpd->lock_flags, wpd->vgroup_validmap, wpd->active.index) &&
949 (!wpd->do_multipaint || BKE_object_defgroup_check_lock_relative_multi(
950 defbase_tot, wpd->lock_flags, defbase_sel, defbase_tot_sel)))
951 {
952 wpd->do_lock_relative = true;
953 }
954
955 if (wpd->do_lock_relative || (ts.auto_normalize && wpd->lock_flags && !wpd->do_multipaint)) {
956 bool *unlocked = (bool *)MEM_dupallocN(wpd->vgroup_validmap);
957
958 if (wpd->lock_flags) {
959 bool *locked = MEM_malloc_arrayN<bool>(wpd->defbase_tot, __func__);
961 wpd->defbase_tot, wpd->lock_flags, wpd->vgroup_validmap, locked, unlocked);
962 wpd->vgroup_locked = locked;
963 }
964
965 wpd->vgroup_unlocked = unlocked;
966 }
967
968 if (wpd->do_multipaint && ts.auto_normalize) {
969 bool *tmpflags;
970 tmpflags = MEM_malloc_arrayN<bool>(defbase_tot, __func__);
971 if (wpd->lock_flags) {
972 BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot);
973 }
974 else {
975 memcpy(tmpflags, wpd->defbase_sel, sizeof(*tmpflags) * wpd->defbase_tot);
976 }
977 wpd->active.lock = tmpflags;
978 }
979 else if (ts.auto_normalize) {
980 bool *tmpflags;
981
982 tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) :
983 MEM_calloc_arrayN<bool>(defbase_tot, __func__);
984 tmpflags[wpd->active.index] = true;
985 wpd->active.lock = tmpflags;
986
987 tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) :
988 MEM_calloc_arrayN<bool>(defbase_tot, __func__);
989 tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true;
990 wpd->mirror.lock = tmpflags;
991 }
992
993 /* If not previously created, create vertex/weight paint mode session data */
995 vwpaint::update_cache_invariants(C, vp, ss, op, mouse);
997
998 /* Brush may have changed after initialization. */
999 brush = BKE_paint_brush(&vp.paint);
1001 wpd->precomputed_weight = MEM_malloc_arrayN<float>(mesh.verts_num, __func__);
1002 }
1003
1004 if (!ob.sculpt->mode.wpaint.dvert_prev.is_empty()) {
1006 for (int i = 0; i < mesh.verts_num; i++, dv++) {
1007 /* Use to show this isn't initialized, never apply to the mesh data. */
1008 dv->flag = 1;
1009 }
1010 }
1011
1012 paint_stroke_set_mode_data(&stroke, std::move(wpd));
1013
1014 return true;
1015}
1016
1017static float wpaint_get_active_weight(const MDeformVert &dv, const WeightPaintInfo &wpi)
1018{
1019 float weight;
1020
1021 if (wpi.do_multipaint) {
1023 &dv, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
1024 }
1025 else {
1026 weight = BKE_defvert_find_weight(&dv, wpi.active.index);
1027 }
1028
1029 if (wpi.do_lock_relative) {
1031 weight, &dv, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
1032 }
1033
1034 CLAMP(weight, 0.0f, 1.0f);
1035 return weight;
1036}
1037
1039 Object &ob, const Brush &brush, WPaintData &wpd, WeightPaintInfo &wpi, Mesh &mesh)
1040{
1041 using namespace blender;
1042 if (wpd.precomputed_weight_ready &&
1044 {
1045 return;
1046 }
1047
1048 threading::parallel_for(IndexRange(mesh.verts_num), 512, [&](const IndexRange range) {
1049 for (const int i : range) {
1050 const MDeformVert &dv = wpi.dvert[i];
1051 wpd.precomputed_weight[i] = wpaint_get_active_weight(dv, wpi);
1052 }
1053 });
1054
1055 wpd.precomputed_weight_ready = true;
1056}
1057
1058/* -------------------------------------------------------------------- */
1061
1063 const IndexMask &node_mask,
1064 FunctionRef<void(IndexRange)> fn)
1065{
1066 /* NOTE: current mirroring code cannot be run in parallel */
1068 fn(node_mask.index_range());
1069 }
1070 else {
1072 node_mask.index_range(), 1, [&](const IndexRange range) { fn(range); });
1073 }
1074}
1075
1076static void filter_factors_with_selection(const Span<bool> select_vert,
1077 const Span<int> verts,
1078 const MutableSpan<float> factors)
1079{
1080 BLI_assert(verts.size() == factors.size());
1081
1082 for (const int i : verts.index_range()) {
1083 if (!select_vert[verts[i]]) {
1084 factors[i] = 0.0f;
1085 }
1086 }
1087}
1088
1089static void do_wpaint_brush_blur(const Depsgraph &depsgraph,
1090 Object &ob,
1091 const Brush &brush,
1092 VPaint &vp,
1093 WPaintData &wpd,
1094 const WeightPaintInfo &wpi,
1095 Mesh &mesh,
1096 const IndexMask &node_mask)
1097{
1098 using namespace blender;
1099 SculptSession &ss = *ob.sculpt;
1101 const StrokeCache &cache = *ss.cache;
1102 const GroupedSpan<int> vert_to_face = mesh.vert_to_face_map();
1103
1104 float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
1106 ss, vp.paint, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
1107 const bool use_normal = vwpaint::use_normal(vp);
1108 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1109 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1110
1111 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1112 ss, brush.falloff_shape);
1113
1114 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1115 const OffsetIndices faces = mesh.faces();
1116 const Span<int> corner_verts = mesh.corner_verts();
1117 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1118 const bke::AttributeAccessor attributes = mesh.attributes();
1119 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1120 VArraySpan<bool> select_vert;
1121 if (use_vert_sel || use_face_sel) {
1122 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1123 }
1124
1125 struct LocalData {
1126 Vector<float> factors;
1127 Vector<float> distances;
1128 };
1130 parallel_nodes_loop_with_mirror_check(mesh, node_mask, [&](const IndexRange range) {
1131 LocalData &tls = all_tls.local();
1132 node_mask.slice(range).foreach_index([&](const int i) {
1133 const Span<int> verts = nodes[i].verts();
1134 tls.factors.resize(verts.size());
1135 const MutableSpan<float> factors = tls.factors;
1136 fill_factor_from_hide(hide_vert, verts, factors);
1137 filter_region_clip_factors(ss, vert_positions, verts, factors);
1138 if (!select_vert.is_empty()) {
1139 filter_factors_with_selection(select_vert, verts, factors);
1140 }
1141
1142 tls.distances.resize(verts.size());
1143 const MutableSpan<float> distances = tls.distances;
1145 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1146 filter_distances_with_radius(cache.radius, distances, factors);
1147 calc_brush_strength_factors(cache, brush, distances, factors);
1148
1149 for (const int i : verts.index_range()) {
1150 const int vert = verts[i];
1151 if (factors[i] == 0.0f) {
1152 continue;
1153 }
1154
1155 /* Get the average face weight */
1156 int total_hit_loops = 0;
1157 float weight_final = 0.0f;
1158 for (const int face : vert_to_face[vert]) {
1159 total_hit_loops += faces[face].size();
1160 for (const int vert : corner_verts.slice(faces[face])) {
1161 weight_final += wpd.precomputed_weight[vert];
1162 }
1163 }
1164
1165 if (total_hit_loops == 0) {
1166 continue;
1167 }
1168
1169 float brush_strength = cache.bstrength;
1170 const float angle_cos = use_normal ?
1171 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1172 1.0f;
1174 brush, wpd.normal_angle_precalc, angle_cos, &brush_strength))
1175 {
1176 continue;
1177 }
1178
1179 const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure;
1180
1181 if ((brush.flag & BRUSH_ACCUMULATE) == 0) {
1182 if (ss.mode.wpaint.alpha_weight[vert] < final_alpha) {
1183 ss.mode.wpaint.alpha_weight[vert] = final_alpha;
1184 }
1185 else {
1186 continue;
1187 }
1188 }
1189
1190 weight_final /= total_hit_loops;
1191 do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, weight_final);
1192 }
1193 });
1194 });
1195}
1196
1197static void do_wpaint_brush_smear(const Depsgraph &depsgraph,
1198 Object &ob,
1199 const Brush &brush,
1200 VPaint &vp,
1201 WPaintData &wpd,
1202 const WeightPaintInfo &wpi,
1203 Mesh &mesh,
1204 const IndexMask &node_mask)
1205{
1206 using namespace blender;
1207 SculptSession &ss = *ob.sculpt;
1209 const GroupedSpan<int> vert_to_face = mesh.vert_to_face_map();
1210 const StrokeCache &cache = *ss.cache;
1211 if (!cache.is_last_valid) {
1212 return;
1213 }
1214
1215 float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
1217 ss, vp.paint, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
1218 const bool use_normal = vwpaint::use_normal(vp);
1219 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1220 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1221 float brush_dir[3];
1222
1223 sub_v3_v3v3(brush_dir, cache.location_symm, cache.last_location_symm);
1224 project_plane_v3_v3v3(brush_dir, brush_dir, cache.view_normal_symm);
1225 if (normalize_v3(brush_dir) == 0.0f) {
1226 return;
1227 }
1228
1229 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1230 const OffsetIndices faces = mesh.faces();
1231 const Span<int> corner_verts = mesh.corner_verts();
1232 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1233 const bke::AttributeAccessor attributes = mesh.attributes();
1234 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1235 VArraySpan<bool> select_vert;
1236 if (use_vert_sel || use_face_sel) {
1237 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1238 }
1239
1240 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1241 ss, brush.falloff_shape);
1242
1243 struct LocalData {
1244 Vector<float> factors;
1245 Vector<float> distances;
1246 };
1248 parallel_nodes_loop_with_mirror_check(mesh, node_mask, [&](const IndexRange range) {
1249 LocalData &tls = all_tls.local();
1250 node_mask.slice(range).foreach_index([&](const int i) {
1251 const Span<int> verts = nodes[i].verts();
1252 tls.factors.resize(verts.size());
1253 const MutableSpan<float> factors = tls.factors;
1254 fill_factor_from_hide(hide_vert, verts, factors);
1255 filter_region_clip_factors(ss, vert_positions, verts, factors);
1256 if (!select_vert.is_empty()) {
1257 filter_factors_with_selection(select_vert, verts, factors);
1258 }
1259
1260 tls.distances.resize(verts.size());
1261 const MutableSpan<float> distances = tls.distances;
1263 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1264 filter_distances_with_radius(cache.radius, distances, factors);
1265 calc_brush_strength_factors(cache, brush, distances, factors);
1266
1267 for (const int i : verts.index_range()) {
1268 const int vert = verts[i];
1269 if (factors[i] == 0.0f) {
1270 continue;
1271 }
1272
1273 float brush_strength = cache.bstrength;
1274 const float angle_cos = use_normal ?
1275 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1276 1.0f;
1278 brush, wpd.normal_angle_precalc, angle_cos, &brush_strength))
1279 {
1280 continue;
1281 }
1282
1283 bool do_color = false;
1284 /* Minimum dot product between brush direction and current
1285 * to neighbor direction is 0.0, meaning orthogonal. */
1286 float stroke_dot_max = 0.0f;
1287
1288 /* Get the color of the loop in the opposite direction of the brush movement
1289 * (this callback is specifically for smear.) */
1290 float weight_final = 0.0;
1291 for (const int face : vert_to_face[vert]) {
1292 for (const int vert_other : corner_verts.slice(faces[face])) {
1293 if (vert_other == vert) {
1294 continue;
1295 }
1296
1297 /* Get the direction from the selected vert to the neighbor. */
1298 float other_dir[3];
1299 sub_v3_v3v3(other_dir, vert_positions[vert], vert_positions[vert_other]);
1300 project_plane_v3_v3v3(other_dir, other_dir, cache.view_normal_symm);
1301
1302 normalize_v3(other_dir);
1303
1304 const float stroke_dot = dot_v3v3(other_dir, brush_dir);
1305
1306 if (stroke_dot > stroke_dot_max) {
1307 stroke_dot_max = stroke_dot;
1308 weight_final = wpd.precomputed_weight[vert_other];
1309 do_color = true;
1310 }
1311 }
1312 if (!do_color) {
1313 continue;
1314 }
1315 const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure;
1316 do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, weight_final);
1317 }
1318 }
1319 });
1320 });
1321}
1322
1323static void do_wpaint_brush_draw(const Depsgraph &depsgraph,
1324 Object &ob,
1325 const Brush &brush,
1326 VPaint &vp,
1327 WPaintData &wpd,
1328 const WeightPaintInfo &wpi,
1329 Mesh &mesh,
1330 const float strength,
1331 const IndexMask &node_mask)
1332{
1333 using namespace blender;
1334 SculptSession &ss = *ob.sculpt;
1336
1337 const StrokeCache &cache = *ss.cache;
1338 /* NOTE: normally `BKE_brush_weight_get(scene, brush)` is used,
1339 * however in this case we calculate a new weight each time. */
1340 const float paintweight = strength;
1341 float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
1343 ss, vp.paint, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
1344 const bool use_normal = vwpaint::use_normal(vp);
1345 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1346 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1347
1348 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1349 ss, brush.falloff_shape);
1350
1351 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1352 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1353 const bke::AttributeAccessor attributes = mesh.attributes();
1354 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1355 VArraySpan<bool> select_vert;
1356 if (use_vert_sel || use_face_sel) {
1357 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1358 }
1359
1360 struct LocalData {
1361 Vector<float> factors;
1362 Vector<float> distances;
1363 };
1365 parallel_nodes_loop_with_mirror_check(mesh, node_mask, [&](const IndexRange range) {
1366 LocalData &tls = all_tls.local();
1367 node_mask.slice(range).foreach_index([&](const int i) {
1368 const Span<int> verts = nodes[i].verts();
1369 tls.factors.resize(verts.size());
1370 const MutableSpan<float> factors = tls.factors;
1371 fill_factor_from_hide(hide_vert, verts, factors);
1372 filter_region_clip_factors(ss, vert_positions, verts, factors);
1373 if (!select_vert.is_empty()) {
1374 filter_factors_with_selection(select_vert, verts, factors);
1375 }
1376
1377 tls.distances.resize(verts.size());
1378 const MutableSpan<float> distances = tls.distances;
1380 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1381 filter_distances_with_radius(cache.radius, distances, factors);
1382 calc_brush_strength_factors(cache, brush, distances, factors);
1383
1384 for (const int i : verts.index_range()) {
1385 const int vert = verts[i];
1386 if (factors[i] == 0.0f) {
1387 continue;
1388 }
1389 float brush_strength = cache.bstrength;
1390 const float angle_cos = use_normal ?
1391 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1392 1.0f;
1394 brush, wpd.normal_angle_precalc, angle_cos, &brush_strength))
1395 {
1396 continue;
1397 }
1398 const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure;
1399
1400 if ((brush.flag & BRUSH_ACCUMULATE) == 0) {
1401 if (ss.mode.wpaint.alpha_weight[vert] < final_alpha) {
1402 ss.mode.wpaint.alpha_weight[vert] = final_alpha;
1403 }
1404 else {
1405 continue;
1406 }
1407 }
1408
1409 do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, paintweight);
1410 }
1411 });
1412 });
1413}
1414
1415static float calculate_average_weight(const Depsgraph &depsgraph,
1416 Object &ob,
1417 const Mesh &mesh,
1418 const Brush &brush,
1419 const VPaint &vp,
1420 WeightPaintInfo &wpi,
1421 const IndexMask &node_mask)
1422{
1423 using namespace blender;
1424 SculptSession &ss = *ob.sculpt;
1426 const StrokeCache &cache = *ss.cache;
1427
1428 const bool use_normal = vwpaint::use_normal(vp);
1429 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1430 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1431
1432 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1433 ss, brush.falloff_shape);
1434
1435 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1436 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1437 const bke::AttributeAccessor attributes = mesh.attributes();
1438 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1439 VArraySpan<bool> select_vert;
1440 if (use_vert_sel || use_face_sel) {
1441 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1442 }
1443
1444 struct LocalData {
1445 Vector<float> factors;
1446 Vector<float> distances;
1447 };
1450 node_mask.index_range(),
1451 1,
1453 [&](const IndexRange range, WPaintAverageAccum accum) {
1454 LocalData &tls = all_tls.local();
1455 node_mask.slice(range).foreach_index([&](const int i) {
1456 const Span<int> verts = nodes[i].verts();
1457 tls.factors.resize(verts.size());
1458 const MutableSpan<float> factors = tls.factors;
1459 fill_factor_from_hide(hide_vert, verts, factors);
1460 filter_region_clip_factors(ss, vert_positions, verts, factors);
1461 if (!select_vert.is_empty()) {
1462 filter_factors_with_selection(select_vert, verts, factors);
1463 }
1464
1465 tls.distances.resize(verts.size());
1466 const MutableSpan<float> distances = tls.distances;
1467 calc_brush_distances(
1468 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1469 filter_distances_with_radius(cache.radius, distances, factors);
1470 calc_brush_strength_factors(cache, brush, distances, factors);
1471
1472 for (const int i : verts.index_range()) {
1473 const int vert = verts[i];
1474 if (factors[i] == 0.0f) {
1475 continue;
1476 }
1477 const float angle_cos = use_normal ?
1478 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1479 1.0f;
1480 if (angle_cos <= 0.0f) {
1481 continue;
1482 }
1483 const MDeformVert &dv = wpi.dvert[vert];
1484 accum.len++;
1485 accum.value += wpaint_get_active_weight(dv, wpi);
1486 }
1487 });
1488 return accum;
1489 },
1490 [](const WPaintAverageAccum &a, const WPaintAverageAccum &b) {
1491 return WPaintAverageAccum{a.len + b.len, a.value + b.value};
1492 });
1493 return math::safe_divide(value.value, double(value.len));
1494}
1495
1497 Object &ob,
1498 VPaint &vp,
1499 WPaintData &wpd,
1500 WeightPaintInfo &wpi,
1501 Mesh &mesh,
1502 const IndexMask &node_mask)
1503{
1504 const Brush &brush = *ob.sculpt->cache->brush;
1505 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1506
1510 depsgraph,
1511 ob,
1512 brush,
1513 vp,
1514 wpd,
1515 wpi,
1516 mesh,
1517 calculate_average_weight(depsgraph, ob, mesh, brush, vp, wpi, node_mask),
1518 node_mask);
1519 break;
1520 }
1522 do_wpaint_brush_smear(depsgraph, ob, brush, vp, wpd, wpi, mesh, node_mask);
1523 break;
1525 do_wpaint_brush_blur(depsgraph, ob, brush, vp, wpd, wpi, mesh, node_mask);
1526 break;
1529 ob,
1530 brush,
1531 vp,
1532 wpd,
1533 wpi,
1534 mesh,
1535 BKE_brush_weight_get(&vp.paint, &brush),
1536 node_mask);
1537 break;
1538 }
1539}
1540
1541
1542/* -------------------------------------------------------------------- */
1545
1546void ED_object_wpaintmode_enter_ex(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
1547{
1549}
1551{
1552 Main &bmain = *CTX_data_main(C);
1553 Scene &scene = *CTX_data_scene(C);
1555 ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob);
1556}
1557
1558
1559/* -------------------------------------------------------------------- */
1562
1572
1573
1574/* -------------------------------------------------------------------- */
1577
1579{
1580 const Object *ob = CTX_data_active_object(C);
1581
1582 return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((const Mesh *)ob->data)->faces_num;
1583}
1584
1589
1590static bool weight_paint_poll_ex(bContext *C, bool check_tool)
1591{
1592 const Object *ob = CTX_data_active_object(C);
1593 const ScrArea *area;
1594
1595 if ((ob != nullptr) && (ob->mode & OB_MODE_WEIGHT_PAINT) &&
1596 (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != nullptr) &&
1597 (area = CTX_wm_area(C)) && (area->spacetype == SPACE_VIEW3D))
1598 {
1599 const ARegion *region = CTX_wm_region(C);
1600 if (region && ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_HUD)) {
1601 if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) {
1602 return true;
1603 }
1604 }
1605 }
1606 return false;
1607}
1608
1610{
1611 return weight_paint_poll_ex(C, true);
1612}
1613
1615{
1616 return weight_paint_poll_ex(C, false);
1617}
1618
1623{
1624 Main &bmain = *CTX_data_main(C);
1625 wmMsgBus *mbus = CTX_wm_message_bus(C);
1627 const int mode_flag = OB_MODE_WEIGHT_PAINT;
1628 const bool is_mode_set = (ob.mode & mode_flag) != 0;
1629 Scene &scene = *CTX_data_scene(C);
1630 ToolSettings &ts = *scene.toolsettings;
1631
1632 if (!is_mode_set) {
1633 if (!blender::ed::object::mode_compat_set(C, &ob, (eObjectMode)mode_flag, op->reports)) {
1634 return OPERATOR_CANCELLED;
1635 }
1636 }
1637
1639
1640 if (is_mode_set) {
1642 }
1643 else {
1645 if (depsgraph) {
1647 }
1648 ED_object_wpaintmode_enter_ex(bmain, *depsgraph, scene, ob);
1650 }
1651
1652 blender::ed::object::posemode_set_for_weight_paint(C, &bmain, &ob, is_mode_set);
1653
1654 /* Weight-paint works by overriding colors in mesh,
1655 * so need to make sure we recalculate on enter and
1656 * exit (exit needs doing regardless because we
1657 * should re-deform).
1658 */
1659 DEG_id_tag_update(&mesh->id, 0);
1660
1662
1663 WM_msg_publish_rna_prop(mbus, &ob.id, &ob, Object, mode);
1664
1666
1667 return OPERATOR_FINISHED;
1668}
1669
1671{
1672 ot->name = "Weight Paint Mode";
1673 ot->idname = "PAINT_OT_weight_paint_toggle";
1674 ot->description = "Toggle weight paint mode in 3D view";
1675
1678
1679 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1680}
1681
1683
1686
1688 Object &ob,
1689 VPaint &wp,
1690 WPaintData &wpd,
1691 WeightPaintInfo &wpi,
1692 Mesh &mesh,
1693 Brush &brush,
1694 const ePaintSymmetryFlags symm,
1695 const int axis,
1696 const int i,
1697 const float angle)
1698{
1699 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1700 SculptSession &ss = *ob.sculpt;
1703
1704 IndexMaskMemory memory;
1705 const IndexMask node_mask = vwpaint::pbvh_gather_generic(depsgraph, ob, wp, brush, memory);
1706
1707 wpaint_paint_leaves(C, ob, wp, wpd, wpi, mesh, node_mask);
1708}
1709
1711 Object &ob,
1712 VPaint &wp,
1713 WPaintData &wpd,
1714 WeightPaintInfo &wpi,
1715 Mesh &mesh,
1716 Brush &brush,
1717 const ePaintSymmetryFlags symm,
1718 const int axis)
1719{
1720 for (int i = 1; i < mesh.radial_symmetry[axis - 'X']; i++) {
1721 const float angle = (2.0 * M_PI) * i / mesh.radial_symmetry[axis - 'X'];
1722 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, axis, i, angle);
1723 }
1724}
1725
1726/* near duplicate of: sculpt.cc's,
1727 * 'do_symmetrical_brush_actions' and 'vpaint_do_symmetrical_brush_actions'. */
1729 bContext *C, Object &ob, VPaint &wp, WPaintData &wpd, WeightPaintInfo &wpi)
1730{
1731 Brush &brush = *BKE_paint_brush(&wp.paint);
1732 Mesh &mesh = *(Mesh *)ob.data;
1733 SculptSession &ss = *ob.sculpt;
1734 StrokeCache &cache = *ss.cache;
1735 const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
1736 int i = 0;
1737
1738 /* initial stroke */
1740 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'X', 0, 0);
1741 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'X');
1742 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'Y');
1743 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'Z');
1744
1745 cache.symmetry = symm;
1746
1747 if (mesh.editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) {
1748 /* We don't do any symmetry strokes when mirroring vertex groups. */
1749 copy_v3_v3(cache.last_location, cache.location);
1750 cache.is_last_valid = true;
1751 return;
1752 }
1753
1754 for (i = 1; i <= symm; i++) {
1755 if (is_symmetry_iteration_valid(i, symm)) {
1757 cache.mirror_symmetry_pass = symm;
1758 cache.radial_symmetry_pass = 0;
1759 SCULPT_cache_calc_brushdata_symm(cache, symm, 0, 0);
1760
1761 if (i & (1 << 0)) {
1762 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X', 0, 0);
1763 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X');
1764 }
1765 if (i & (1 << 1)) {
1766 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y', 0, 0);
1767 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y');
1768 }
1769 if (i & (1 << 2)) {
1770 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z', 0, 0);
1771 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z');
1772 }
1773 }
1774 }
1775 copy_v3_v3(cache.last_location, cache.location);
1776 cache.is_last_valid = true;
1777}
1778
1780 wmOperator *op,
1781 PaintStroke *stroke,
1782 PointerRNA *itemptr)
1783{
1785 VPaint &wp = *ts.wpaint;
1786 const Brush &brush = *BKE_paint_brush(&wp.paint);
1788 ViewContext *vc;
1790
1791 SculptSession &ss = *ob->sculpt;
1792
1793 vwpaint::update_cache_variants(C, wp, *ob, itemptr);
1794
1795 float mat[4][4];
1796
1797 const float brush_alpha_value = BKE_brush_alpha_get(&wp.paint, &brush);
1798
1799 /* intentionally don't initialize as nullptr, make sure we initialize all members below */
1800 WeightPaintInfo wpi;
1801
1802 if (wpd == nullptr) {
1803 /* XXX: force a redraw here, since even though we can't paint,
1804 * at least view won't freeze until stroke ends */
1806 return;
1807 }
1808
1809 vc = &wpd->vc;
1810 ob = vc->obact;
1811
1814
1815 mul_m4_m4m4(mat, vc->rv3d->persmat, ob->object_to_world().ptr());
1816
1817 Mesh &mesh = *static_cast<Mesh *>(ob->data);
1818
1819 /* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */
1820 wpi.dvert = mesh.deform_verts_for_write();
1821
1822 wpi.defbase_tot = wpd->defbase_tot;
1823 wpi.defbase_sel = wpd->defbase_sel;
1825
1827 wpi.active = wpd->active;
1828 wpi.mirror = wpd->mirror;
1829 wpi.lock_flags = wpd->lock_flags;
1831 wpi.vgroup_locked = wpd->vgroup_locked;
1833 wpi.do_flip = RNA_boolean_get(op->ptr, "pen_flip") || ss.cache->invert;
1834 wpi.do_multipaint = wpd->do_multipaint;
1835 wpi.do_auto_normalize = ((ts.auto_normalize != 0) && (wpi.vgroup_validmap != nullptr) &&
1836 (wpi.do_multipaint || wpi.vgroup_validmap[wpi.active.index]));
1839 wpi.brush_alpha_value = brush_alpha_value;
1840
1841 if (wpd->precomputed_weight) {
1842 precompute_weight_values(*ob, brush, *wpd, wpi, mesh);
1843 }
1844
1845 wpaint_do_symmetrical_brush_actions(C, *ob, wp, *wpd, wpi);
1846
1847 swap_m4m4(vc->rv3d->persmat, mat);
1848
1849 /* Calculate pivot for rotation around selection if needed.
1850 * also needed for "Frame Selected" on last stroke. */
1851 float loc_world[3];
1852 mul_v3_m4v3(loc_world, ob->object_to_world().ptr(), ss.cache->location);
1853 vwpaint::last_stroke_update(loc_world, wp.paint);
1854
1856
1857 DEG_id_tag_update(&mesh.id, 0);
1859 swap_m4m4(wpd->vc.rv3d->persmat, mat);
1860
1862}
1863
1864static void wpaint_stroke_done(const bContext *C, PaintStroke * /*stroke*/)
1865{
1867
1868 SculptSession &ss = *ob.sculpt;
1869
1870 if (ss.cache->alt_smooth) {
1872 VPaint &vp = *ts.wpaint;
1874 }
1875
1876 if (ob.particlesystem.first) {
1878 for (int i = 0; i < PSYS_TOT_VG; i++) {
1879 if (psys->vgroup[i] == BKE_object_defgroup_active_index_get(&ob)) {
1880 psys->recalc |= ID_RECALC_PSYS_RESET;
1881 break;
1882 }
1883 }
1884 }
1885 }
1886
1887 DEG_id_tag_update((ID *)ob.data, 0);
1888
1890
1891 MEM_delete(ob.sculpt->cache);
1892 ob.sculpt->cache = nullptr;
1893}
1894
1896{
1898 op,
1902 nullptr,
1904 event->type);
1905
1906 const wmOperatorStatus retval = op->type->modal(C, op, event);
1907 OPERATOR_RETVAL_CHECK(retval);
1908
1909 if (retval == OPERATOR_FINISHED) {
1911 return OPERATOR_FINISHED;
1912 }
1914
1916
1918}
1919
1921{
1923 op,
1927 nullptr,
1929 0);
1930
1932
1933 return OPERATOR_FINISHED;
1934}
1935
1937{
1939 MEM_delete(ob.sculpt->cache);
1940 ob.sculpt->cache = nullptr;
1941
1943}
1944
1946{
1947 return paint_stroke_modal(C, op, event, (PaintStroke **)&op->customdata);
1948}
1949
1951{
1952 ot->name = "Weight Paint";
1953 ot->idname = "PAINT_OT_weight_paint";
1954 ot->description = "Paint a stroke in the current vertex group's weights";
1955
1956 ot->invoke = wpaint_invoke;
1957 ot->modal = wpaint_modal;
1958 ot->exec = wpaint_exec;
1959 ot->poll = weight_paint_poll;
1960 ot->cancel = wpaint_cancel;
1961
1962 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
1963
1966 ot->srna,
1967 "override_location",
1968 false,
1969 "Override Location",
1970 "Override the given \"location\" array by recalculating object space positions from the "
1971 "provided \"mouse_event\" positions");
1973}
1974
float BKE_brush_alpha_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1357
float BKE_brush_weight_get(const Paint *paint, const Brush *brush)
Definition brush.cc:1364
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)
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:607
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:1017
float BKE_defvert_calc_lock_relative_weight(float weight, float locked_weight, float unlocked_weight)
Definition deform.cc:989
MDeformWeight * BKE_defvert_ensure_index(MDeformVert *dv, int defgroup)
Definition deform.cc:825
float BKE_defvert_total_selected_weight(const MDeformVert *dv, int defbase_num, const bool *defbase_sel)
Definition deform.cc:950
MDeformWeight * BKE_defvert_find_index(const MDeformVert *dv, int defgroup)
Definition deform.cc:806
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:774
void BKE_defvert_copy(MDeformVert *dvert_dst, const MDeformVert *dvert_src)
Definition deform.cc:127
#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:972
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
Mesh * BKE_mesh_from_object(Object *ob)
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:650
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
Definition paint.cc:1125
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
Generic array manipulation API.
#define BLI_array_binary_or(arr, arr_a, arr_b, arr_len)
#define BLI_assert(a)
Definition BLI_assert.h:46
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
#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])
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:1083
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_CANCELLED
@ OPERATOR_FINISHED
@ 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:302
bool ED_operator_region_view3d_active(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void view3d_operator_needs_gpu(const bContext *C)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
IMB_BlendMode
Definition IMB_imbuf.hh:178
@ IMB_BLEND_DARKEN
Definition IMB_imbuf.hh:184
@ IMB_BLEND_LIGHTEN
Definition IMB_imbuf.hh:183
@ IMB_BLEND_MIX
Definition IMB_imbuf.hh:179
@ IMB_BLEND_ADD
Definition IMB_imbuf.hh:180
@ IMB_BLEND_SUB
Definition IMB_imbuf.hh:181
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#define ND_DRAW
Definition WM_types.hh:461
#define ND_MODE
Definition WM_types.hh:445
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_OBJECT
Definition WM_types.hh:379
BPy_StructRNA * depsgraph
static T sum(const btAlignedObjectArray< T > &items)
const T * data() const
Definition BLI_array.hh:312
bool is_empty() const
Definition BLI_array.hh:264
constexpr T * data() const
Definition BLI_span.hh:539
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
GAttributeReader lookup(const StringRef attribute_id) const
Span< NodeT > nodes() const
IndexMask slice(IndexRange range) const
void foreach_index(Fn &&fn) const
static float verts[][3]
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:1059
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:1040
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)
bool use_normal(const VPaint &vp)
void mode_exit_generic(Object &ob, eObjectMode mode_flag)
bool test_brush_angle_falloff(const Brush &brush, const NormalAnglePrecalc &normal_angle_precalc, float angle_cos, float *brush_strength)
void init_session_data(const ToolSettings &ts, Object &ob)
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 get_brush_alpha_data(const SculptSession &ss, const Paint &paint, const Brush &brush, float *r_brush_size_pressure, float *r_brush_alpha_value, float *r_brush_alpha_pressure)
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)
void last_stroke_update(const float location[3], Paint &paint)
IndexMask pbvh_gather_generic(const Depsgraph &depsgraph, const Object &ob, const VPaint &wp, const Brush &brush, IndexMaskMemory &memory)
void smooth_brush_toggle_off(Paint *paint, StrokeCache *cache)
bool stroke_get_location_bvh(bContext *C, float out[3], const float mval[2], const bool force_original)
Definition sculpt.cc:4940
void calc_brush_strength_factors(const StrokeCache &cache, const Brush &brush, Span< float > distances, MutableSpan< float > factors)
Definition sculpt.cc:7183
wmOperatorStatus paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke)
void filter_distances_with_radius(float radius, Span< float > distances, MutableSpan< float > factors)
Definition sculpt.cc:7105
void filter_region_clip_factors(const SculptSession &ss, Span< float3 > vert_positions, Span< int > verts, MutableSpan< float > factors)
Definition sculpt.cc:6972
void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke)
wmOperatorStatus paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke **stroke_p)
void calc_brush_distances(const SculptSession &ss, Span< float3 > vert_positions, Span< int > vert, eBrushFalloffShape falloff_shape, MutableSpan< float > r_distances)
Definition sculpt.cc:7055
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:6775
void paint_stroke_set_mode_data(PaintStroke *stroke, std::unique_ptr< PaintModeData > mode_data)
bool is_symmetry_iteration_valid(const char i, const char symm)
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:93
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
float ED_wpaint_blend_tool(int tool, float weight, float paintval, float alpha)
bool ED_wpaint_ensure_data(bContext *C, ReportList *reports, eWPaintFlag flag, WPaintVGroupIndex *vgroup_index)
bool weight_paint_poll(bContext *C)
@ WPAINT_ENSURE_MIRROR
void paint_stroke_operator_properties(wmOperatorType *ot)
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)
bool weight_paint_mode_poll(bContext *C)
static void precompute_weight_values(Object &ob, const Brush &brush, WPaintData &wpd, WeightPaintInfo &wpi, Mesh &mesh)
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 bool multipaint_verify_change(MDeformVert *dvert, const int defbase_tot, float change, const bool *defbase_sel)
static wmOperatorStatus wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
static wmOperatorStatus wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus 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 void do_wpaint_brush_draw(const Depsgraph &depsgraph, Object &ob, const Brush &brush, VPaint &vp, WPaintData &wpd, const WeightPaintInfo &wpi, Mesh &mesh, const float strength, const IndexMask &node_mask)
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 do_wpaint_brush_smear(const Depsgraph &depsgraph, Object &ob, const Brush &brush, VPaint &vp, WPaintData &wpd, const WeightPaintInfo &wpi, Mesh &mesh, const IndexMask &node_mask)
static void wpaint_cancel(bContext *C, wmOperator *op)
static void wpaint_stroke_update_step(bContext *C, wmOperator *op, PaintStroke *stroke, PointerRNA *itemptr)
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 do_wpaint_brush_blur(const Depsgraph &depsgraph, Object &ob, const Brush &brush, VPaint &vp, WPaintData &wpd, const WeightPaintInfo &wpi, Mesh &mesh, const IndexMask &node_mask)
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 wmOperatorStatus wpaint_exec(bContext *C, wmOperator *op)
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)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
ePaintSymmetryFlags SCULPT_mesh_symmetry_xyz_get(const Object &object)
Definition sculpt.cc:184
const float * SCULPT_brush_frontface_normal_from_falloff_shape(const SculptSession &ss, char falloff_shape)
Definition sculpt.cc:1180
void SCULPT_cache_calc_brushdata_symm(blender::ed::sculpt_paint::StrokeCache &cache, const ePaintSymmetryFlags symm, const char axis, const float angle)
Definition sculpt.cc:3465
static float brush_strength(const Sculpt &sd, const blender::ed::sculpt_paint::StrokeCache &cache, const float feather, const PaintModeSettings &)
Definition sculpt.cc:2175
float falloff_angle
char falloff_shape
short blend
char weight_brush_type
Definition DNA_ID.h:414
void * first
struct MDeformWeight * dw
unsigned int def_nr
ListBase particlesystem
struct SculptSession * sculpt
float persmat[4][4]
struct ToolSettings * toolsettings
blender::ed::sculpt_paint::StrokeCache * cache
Definition BKE_paint.hh:417
blender::Array< MDeformVert > dvert_prev
Definition BKE_paint.hh:485
float * alpha_weight
Definition BKE_paint.hh:481
struct SculptSession::@362240011116215270333121305340144315201335267314::@377352350121204263020240366015362210376116055117 wpaint
struct SculptSession::@362240011116215270333121305340144315201335267314 mode
RegionView3D * rv3d
Definition ED_view3d.hh:80
ARegion * region
Definition ED_view3d.hh:77
Object * obact
Definition ED_view3d.hh:75
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
~WPaintData() override
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
wmEventType type
Definition WM_types.hh:757
wmOperatorStatus(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1081
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
static int blend(const Tex *tex, const float texvec[3], TexResult *texres)
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:4237
#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)