Blender V4.5
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_rect.h"
22#include "BLI_task.hh"
23#include "BLI_vector.hh"
24
25#include "DNA_brush_types.h"
26#include "DNA_mesh_types.h"
27#include "DNA_meshdata_types.h"
28#include "DNA_object_types.h"
29#include "DNA_particle_types.h"
30#include "DNA_scene_types.h"
31
32#include "RNA_access.hh"
33
34#include "BKE_attribute.hh"
35#include "BKE_brush.hh"
36#include "BKE_context.hh"
37#include "BKE_deform.hh"
38#include "BKE_editmesh.hh"
39#include "BKE_lib_id.hh"
40#include "BKE_mesh.hh"
41#include "BKE_object.hh"
42#include "BKE_object_deform.h"
43#include "BKE_paint.hh"
44#include "BKE_report.hh"
45
46#include "DEG_depsgraph.hh"
47
48#include "WM_api.hh"
49#include "WM_message.hh"
50#include "WM_toolsystem.hh"
51#include "WM_types.hh"
52
53#include "ED_image.hh"
54#include "ED_mesh.hh"
55#include "ED_object.hh"
56#include "ED_paint.hh"
57#include "ED_screen.hh"
58#include "ED_view3d.hh"
59
60/* For IMB_BlendMode only. */
61#include "IMB_imbuf.hh"
62
63#include "bmesh.hh"
64
65#include "RNA_define.hh"
66
67#include "mesh_brush_common.hh"
68#include "paint_intern.hh" /* own include */
69#include "sculpt_automask.hh"
70#include "sculpt_intern.hh"
71
72using namespace blender;
73using namespace blender::ed::sculpt_paint;
75
78 double value;
79};
80
92 int index;
99 const bool *lock;
100};
101
102struct WPaintData : public PaintModeData {
105
107
108 /* variables for auto normalize */
109 const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */
110 const bool *lock_flags;
111 const bool *vgroup_locked; /* mask of locked defbones */
112 const bool *vgroup_unlocked; /* mask of unlocked defbones */
113
114 /* variables for multipaint */
115 const bool *defbase_sel; /* set of selected groups */
116 int defbase_tot_sel; /* number of selected groups */
117 bool do_multipaint; /* true if multipaint enabled and multiple groups selected */
119
121
122 /* original weight values for use in blur/smear */
125
137};
138
139/* struct to avoid passing many args each call to do_weight_paint_vertex()
140 * this _could_ be made a part of the operators 'WPaintData' struct, or at
141 * least a member, but for now keep its own struct, initialized on every
142 * paint stroke update - campbell */
144
146
148
149 /* both must add up to 'defbase_tot' */
152
154
155 /* boolean array for locked bones,
156 * length of defbase_tot */
157 const bool *lock_flags;
158 /* boolean array for selected bones,
159 * length of defbase_tot, can't be const because of how it's passed */
160 const bool *defbase_sel;
161 /* same as WeightPaintData.vgroup_validmap,
162 * only added here for convenience */
163 const bool *vgroup_validmap;
164 /* same as WeightPaintData.vgroup_locked/unlocked,
165 * only added here for convenience */
166 const bool *vgroup_locked;
167 const bool *vgroup_unlocked;
168
174
175 float brush_alpha_value; /* result of BKE_brush_alpha_get() */
176};
177
179 MDeformVert *dvert_curr,
180 int index)
181{
182 const MDeformVert *dv_curr = &dvert_curr[index];
183 MDeformVert *dv_prev = &dvert_prev[index];
184 if (dv_prev->flag == 1) {
185 dv_prev->flag = 0;
186 BKE_defvert_copy(dv_prev, dv_curr);
187 }
188 return dv_prev;
189}
190
191static float wpaint_blend(const VPaint &wp,
192 float weight,
193 const float alpha,
194 float paintval,
195 const float /*brush_alpha_value*/,
196 const bool do_flip)
197{
198 const Brush &brush = *BKE_paint_brush_for_read(&wp.paint);
200
201 if (do_flip) {
202 switch (blend) {
203 case IMB_BLEND_MIX:
204 paintval = 1.0f - paintval;
205 break;
206 case IMB_BLEND_ADD:
208 break;
209 case IMB_BLEND_SUB:
211 break;
214 break;
215 case IMB_BLEND_DARKEN:
217 break;
218 default:
219 break;
220 }
221 }
222
223 weight = ED_wpaint_blend_tool(blend, weight, paintval, alpha);
224
225 CLAMP(weight, 0.0f, 1.0f);
226 /* The following is a reasonable lower bound for values that a user may want for weight values,
227 * without this rounding, attempting to paint to an exact value of 0.0 becomes tedious. */
228 constexpr float threshold = 0.0001f;
229 return weight < threshold ? 0.0f : weight;
230}
231
232static float wpaint_clamp_monotonic(float oldval, float curval, float newval)
233{
234 if (newval < oldval) {
235 return std::min(newval, curval);
236 }
237 if (newval > oldval) {
238 return std::max(newval, curval);
239 }
240 return newval;
241}
242
244 float weight, float old_weight, float locked_weight, float free_weight, bool auto_normalize)
245{
246 /* In auto-normalize mode, or when there is no unlocked weight,
247 * compute based on locked weight. */
248 if (auto_normalize || free_weight <= 0.0f) {
249 if (locked_weight < 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
250 weight *= (1.0f - locked_weight);
251 }
252 else {
253 weight = 0;
254 }
255 }
256 else {
257 /* When dealing with full unlocked weight, don't paint, as it is always displayed as 1. */
258 if (old_weight >= free_weight) {
259 weight = old_weight;
260 }
261 /* Try to compute a weight value that would produce the desired effect if normalized. */
262 else if (weight < 1.0f) {
263 weight = weight * (free_weight - old_weight) / (1 - weight);
264 }
265 else {
266 weight = 1.0f;
267 }
268 }
269
270 return weight;
271}
272
273/* ----------------------------------------------------- */
274
276 const int defbase_tot,
277 const bool *vgroup_validmap)
278{
279 float sum = 0.0f, fac;
280 uint i, tot = 0;
281 MDeformWeight *dw;
282
283 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
284 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
285 tot++;
286 sum += dw->weight;
287 }
288 }
289
290 if ((tot == 0) || (sum == 1.0f)) {
291 return;
292 }
293
294 if (sum != 0.0f) {
295 fac = 1.0f / sum;
296
297 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
298 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
299 dw->weight *= fac;
300 }
301 }
302 }
303 else {
304 /* hrmf, not a factor in this case */
305 fac = 1.0f / tot;
306
307 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
308 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
309 dw->weight = fac;
310 }
311 }
312 }
313}
314
320 const int defbase_tot,
321 const bool *vgroup_validmap,
322 const bool *lock_flags)
323{
324 float sum = 0.0f, fac;
325 float sum_unlock = 0.0f;
326 float lock_weight = 0.0f;
327 uint i, tot = 0;
328 MDeformWeight *dw;
329
330 if (lock_flags == nullptr) {
331 do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap);
332 return true;
333 }
334
335 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
336 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
337 sum += dw->weight;
338
339 if (lock_flags[dw->def_nr]) {
340 lock_weight += dw->weight;
341 }
342 else {
343 tot++;
344 sum_unlock += dw->weight;
345 }
346 }
347 }
348
349 if (sum == 1.0f) {
350 return true;
351 }
352
353 if (tot == 0) {
354 return false;
355 }
356
357 if (lock_weight >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
358 /* locked groups make it impossible to fully normalize,
359 * zero out what we can and return false */
360 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
361 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
362 if (lock_flags[dw->def_nr] == false) {
363 dw->weight = 0.0f;
364 }
365 }
366 }
367
368 return (lock_weight == 1.0f);
369 }
370 if (sum_unlock != 0.0f) {
371 fac = (1.0f - lock_weight) / sum_unlock;
372
373 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
374 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
375 if (lock_flags[dw->def_nr] == false) {
376 dw->weight *= fac;
377 /* paranoid but possibly with float error */
378 CLAMP(dw->weight, 0.0f, 1.0f);
379 }
380 }
381 }
382 }
383 else {
384 /* hrmf, not a factor in this case */
385 fac = (1.0f - lock_weight) / tot;
386 /* paranoid but possibly with float error */
387 CLAMP(fac, 0.0f, 1.0f);
388
389 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
390 if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) {
391 if (lock_flags[dw->def_nr] == false) {
392 dw->weight = fac;
393 }
394 }
395 }
396 }
397
398 return true;
399}
400
406 const int defbase_tot,
407 const bool *vgroup_validmap,
408 const bool *lock_flags,
409 const bool *lock_with_active)
410{
411 /* first pass with both active and explicitly locked groups restricted from change */
412
414 dvert, defbase_tot, vgroup_validmap, lock_with_active);
415
416 if (!success) {
426 do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_flags);
427 }
428}
429
430#if 0 /* UNUSED */
431static bool has_unselected_unlocked_bone_group(int defbase_tot,
432 bool *defbase_sel,
433 int selected,
434 const bool *lock_flags,
435 const bool *vgroup_validmap)
436{
437 int i;
438 if (defbase_tot == selected) {
439 return false;
440 }
441 for (i = 0; i < defbase_tot; i++) {
442 if (vgroup_validmap[i] && !defbase_sel[i] && !lock_flags[i]) {
443 return true;
444 }
445 }
446 return false;
447}
448#endif
450 const int defbase_tot,
451 const bool *defbase_sel,
452 float *change_p)
453{
454 int i;
455 MDeformWeight *dw;
456 float val;
457 float change = *change_p;
458
459 /* verify that the change does not cause values exceeding 1 and clamp it */
460 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
461 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
462 if (dw->weight) {
463 val = dw->weight * change;
464 if (val > 1) {
465 change = 1.0f / dw->weight;
466 }
467 }
468 }
469 }
470
471 *change_p = change;
472}
473
475 const int defbase_tot,
476 float change,
477 const bool *defbase_sel)
478{
479 int i;
480 MDeformWeight *dw;
481 float val;
482
483 /* in case the change is reduced, you need to recheck
484 * the earlier values to make sure they are not 0
485 * (precision error) */
486 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
487 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
488 if (dw->weight) {
489 val = dw->weight * change;
490 /* the value should never reach zero while multi-painting if it
491 * was nonzero beforehand */
492 if (val <= 0) {
493 return false;
494 }
495 }
496 }
497 }
498
499 return true;
500}
501
503 const int defbase_tot,
504 float change,
505 const bool *defbase_sel)
506{
507 int i;
508 MDeformWeight *dw;
509
510 for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
511 if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) {
512 if (dw->weight) {
513 dw->weight = dw->weight * change;
514 CLAMP(dw->weight, 0.0f, 1.0f);
515 }
516 }
517 }
518}
519
521 Object &ob,
522 const WeightPaintInfo &wpi,
523 const uint index,
524 float alpha,
525 float paintweight)
526{
527 Mesh *mesh = (Mesh *)ob.data;
528 MDeformVert *dv = &wpi.dvert[index];
529 bool topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
530
531 MDeformWeight *dw;
532 float weight_prev, weight_cur;
533 float dw_rel_locked = 0.0f, dw_rel_free = 1.0f;
534
535 int index_mirr;
536 int vgroup_mirr;
537
538 MDeformVert *dv_mirr;
539 MDeformWeight *dw_mirr;
540
541 /* Check if we should mirror vertex groups (X-axis). */
543 index_mirr = mesh_get_x_mirror_vert(&ob, nullptr, index, topology);
544 vgroup_mirr = wpi.mirror.index;
545
546 /* another possible error - mirror group _and_ active group are the same (which is fine),
547 * but we also are painting onto a center vertex - this would paint the same weight twice */
548 if (index_mirr == index && vgroup_mirr == wpi.active.index) {
549 index_mirr = vgroup_mirr = -1;
550 }
551 }
552 else {
553 index_mirr = vgroup_mirr = -1;
554 }
555
556 /* Check if painting should create new deform weight entries. */
557 bool restrict_to_existing = (wp.flag & VP_FLAG_VGROUP_RESTRICT) != 0;
558
559 if (wpi.do_lock_relative || wpi.do_auto_normalize) {
560 /* Without do_lock_relative only dw_rel_locked is reliable, while dw_rel_free may be fake 0. */
562 dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi.defbase_tot, wpi.vgroup_locked);
563 CLAMP(dw_rel_locked, 0.0f, 1.0f);
564
565 /* Do not create entries if there is not enough free weight to paint.
566 * This logic is the same as in wpaint_undo_lock_relative and auto-normalize. */
567 if (wpi.do_auto_normalize || dw_rel_free <= 0.0f) {
568 if (dw_rel_locked >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
569 restrict_to_existing = true;
570 }
571 }
572 }
573
574 if (restrict_to_existing) {
575 dw = BKE_defvert_find_index(dv, wpi.active.index);
576 }
577 else {
579 }
580
581 if (dw == nullptr) {
582 return;
583 }
584
585 if (index_mirr != -1) {
586 dv_mirr = &wpi.dvert[index_mirr];
587 if (wp.flag & VP_FLAG_VGROUP_RESTRICT) {
588 dw_mirr = BKE_defvert_find_index(dv_mirr, vgroup_mirr);
589
590 if (dw_mirr == nullptr) {
591 index_mirr = vgroup_mirr = -1;
592 dv_mirr = nullptr;
593 }
594 }
595 else {
596 if (index != index_mirr) {
597 dw_mirr = BKE_defvert_ensure_index(dv_mirr, vgroup_mirr);
598 }
599 else {
600 /* dv and dv_mirr are the same */
601 int totweight_prev = dv_mirr->totweight;
602 int dw_offset = int(dw - dv_mirr->dw);
603 dw_mirr = BKE_defvert_ensure_index(dv_mirr, vgroup_mirr);
604
605 /* if we added another, get our old one back */
606 if (totweight_prev != dv_mirr->totweight) {
607 dw = &dv_mirr->dw[dw_offset];
608 }
609 }
610 }
611 }
612 else {
613 dv_mirr = nullptr;
614 dw_mirr = nullptr;
615 }
616
617 weight_cur = dw->weight;
618
619 /* Handle weight caught up in locked defgroups for Lock Relative. */
620 if (wpi.do_lock_relative) {
621 weight_cur = BKE_defvert_calc_lock_relative_weight(weight_cur, dw_rel_locked, dw_rel_free);
622 }
623
625 MDeformVert *dvert_prev = ob.sculpt->mode.wpaint.dvert_prev.data();
626 MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi.dvert.data(), index);
627 if (index_mirr != -1) {
628 defweight_prev_init(dvert_prev, wpi.dvert.data(), index_mirr);
629 }
630
631 weight_prev = BKE_defvert_find_weight(dv_prev, wpi.active.index);
632
633 if (wpi.do_lock_relative) {
635 weight_prev, dv_prev, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
636 }
637 }
638 else {
639 weight_prev = weight_cur;
640 }
641
642 /* If there are no normalize-locks or multipaint,
643 * then there is no need to run the more complicated checks */
644
645 {
646 float new_weight = wpaint_blend(
647 wp, weight_prev, alpha, paintweight, wpi.brush_alpha_value, wpi.do_flip);
648
649 float weight = wpaint_clamp_monotonic(weight_prev, weight_cur, new_weight);
650
651 /* Undo the lock relative weight correction. */
652 if (wpi.do_lock_relative) {
653 if (index_mirr == index) {
654 /* When painting a center vertex with X Mirror and L/R pair,
655 * handle both groups together. This avoids weird fighting
656 * in the non-normalized weight mode. */
657 float orig_weight = dw->weight + dw_mirr->weight;
658 weight = 0.5f *
660 weight * 2, orig_weight, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
661 }
662 else {
664 weight, dw->weight, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
665 }
666
667 CLAMP(weight, 0.0f, 1.0f);
668 }
669
670 dw->weight = weight;
671
672 /* WATCH IT: take care of the ordering of applying mirror -> normalize,
673 * can give wrong results #26193, least confusing if normalize is done last */
674
675 if (index_mirr != -1) {
676 dw_mirr->weight = dw->weight;
677 }
678
679 if (wpi.do_auto_normalize) {
680 /* note on normalize - this used to be applied after painting and normalize all weights,
681 * in some ways this is good because there is feedback where the more weights involved would
682 * 'resist' so you couldn't instantly zero out other weights by painting 1.0 on the active.
683 *
684 * However this gave a problem since applying mirror, then normalize both verts
685 * the resulting weight won't match on both sides.
686 *
687 * If this 'resisting', slower normalize is nicer, we could call
688 * do_weight_paint_normalize_all() and only use...
689 * do_weight_paint_normalize_all_active() when normalizing the mirror vertex.
690 * - campbell
691 */
693 dv, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
694
695 if (index_mirr != -1) {
696 /* only normalize if this is not a center vertex,
697 * else we get a conflict, normalizing twice */
698 if (index != index_mirr) {
700 dv_mirr, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.mirror.lock);
701 }
702 else {
703 /* This case accounts for:
704 * - Painting onto a center vertex of a mesh.
705 * - X-mirror is enabled.
706 * - Auto normalize is enabled.
707 * - The group you are painting onto has a L / R version.
708 *
709 * We want L/R vgroups to have the same weight but this can't be if both are over 0.5,
710 * We _could_ have special check for that, but this would need its own
711 * normalize function which holds 2 groups from changing at once.
712 *
713 * So! just balance out the 2 weights, it keeps them equal and everything normalized.
714 *
715 * While it won't hit the desired weight immediately as the user waggles their mouse,
716 * constant painting and re-normalizing will get there. this is also just simpler logic.
717 * - campbell */
718 dw_mirr->weight = dw->weight = (dw_mirr->weight + dw->weight) * 0.5f;
719 }
720 }
721 }
722 }
723}
724
726 Object &ob,
727 const WeightPaintInfo &wpi,
728 const uint index,
729 float alpha,
730 float paintweight)
731{
732 Mesh *mesh = (Mesh *)ob.data;
733 MDeformVert *dv = &wpi.dvert[index];
734 bool topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
735
736 int index_mirr = -1;
737 MDeformVert *dv_mirr = nullptr;
738
739 float curw, curw_real, oldw, neww, change, curw_mirr, change_mirr;
740 float dw_rel_free, dw_rel_locked;
741
742 /* Check if we should mirror vertex groups (X-axis). */
744 index_mirr = mesh_get_x_mirror_vert(&ob, nullptr, index, topology);
745
746 if (!ELEM(index_mirr, -1, index)) {
747 dv_mirr = &wpi.dvert[index_mirr];
748 }
749 else {
750 index_mirr = -1;
751 }
752 }
753
754 /* compute weight change by applying the brush to average or sum of group weights */
757
758 if (curw == 0.0f) {
759 /* NOTE: no weight to assign to this vertex, could add all groups? */
760 return;
761 }
762
763 /* Handle weight caught up in locked defgroups for Lock Relative. */
764 if (wpi.do_lock_relative) {
766 dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi.defbase_tot, wpi.vgroup_locked);
767 CLAMP(dw_rel_locked, 0.0f, 1.0f);
768
769 curw = BKE_defvert_calc_lock_relative_weight(curw, dw_rel_locked, dw_rel_free);
770 }
771
773 MDeformVert *dvert_prev = ob.sculpt->mode.wpaint.dvert_prev.data();
774 MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi.dvert.data(), index);
775 if (index_mirr != -1) {
776 defweight_prev_init(dvert_prev, wpi.dvert.data(), index_mirr);
777 }
778
780 dv_prev, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
781
782 if (wpi.do_lock_relative) {
784 oldw, dv_prev, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
785 }
786 }
787 else {
788 oldw = curw;
789 }
790
791 neww = wpaint_blend(wp, oldw, alpha, paintweight, wpi.brush_alpha_value, wpi.do_flip);
792 neww = wpaint_clamp_monotonic(oldw, curw, neww);
793
794 if (wpi.do_lock_relative) {
796 neww, curw_real, dw_rel_locked, dw_rel_free, wpi.do_auto_normalize);
797 }
798
799 change = neww / curw_real;
800
801 /* verify for all groups that 0 < result <= 1 */
802 multipaint_clamp_change(dv, wpi.defbase_tot, wpi.defbase_sel, &change);
803
804 if (dv_mirr != nullptr) {
806 dv_mirr, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
807
808 if (curw_mirr == 0.0f) {
809 /* can't mirror into a zero weight vertex */
810 dv_mirr = nullptr;
811 }
812 else {
813 /* mirror is changed to achieve the same collective weight value */
814 float orig = change_mirr = curw_real * change / curw_mirr;
815
816 multipaint_clamp_change(dv_mirr, wpi.defbase_tot, wpi.defbase_sel, &change_mirr);
817
818 if (!multipaint_verify_change(dv_mirr, wpi.defbase_tot, change_mirr, wpi.defbase_sel)) {
819 return;
820 }
821
822 change *= change_mirr / orig;
823 }
824 }
825
826 if (!multipaint_verify_change(dv, wpi.defbase_tot, change, wpi.defbase_sel)) {
827 return;
828 }
829
830 /* apply validated change to vertex and mirror */
831 multipaint_apply_change(dv, wpi.defbase_tot, change, wpi.defbase_sel);
832
833 if (dv_mirr != nullptr) {
834 multipaint_apply_change(dv_mirr, wpi.defbase_tot, change_mirr, wpi.defbase_sel);
835 }
836
837 if (wpi.do_auto_normalize) {
839 dv, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
840
841 if (dv_mirr != nullptr) {
843 dv_mirr, wpi.defbase_tot, wpi.vgroup_validmap, wpi.lock_flags, wpi.active.lock);
844 }
845 }
846}
847
848static void do_weight_paint_vertex(const VPaint &wp,
849 Object &ob,
850 const WeightPaintInfo &wpi,
851 const uint index,
852 float alpha,
853 float paintweight)
854{
855 if (wpi.do_multipaint) {
856 do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight);
857 }
858 else {
859 do_weight_paint_vertex_single(wp, ob, wpi, index, alpha, paintweight);
860 }
861}
862
863static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
864{
865 Scene &scene = *CTX_data_scene(C);
866 PaintStroke &stroke = *(PaintStroke *)op->customdata;
867 ToolSettings &ts = *scene.toolsettings;
870 WPaintVGroupIndex vgroup_index;
871 int defbase_tot, defbase_tot_sel;
872 bool *defbase_sel;
873 SculptSession &ss = *ob.sculpt;
876
877 if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) {
878 return false;
879 }
880
881 {
882 /* check if we are attempting to paint onto a locked vertex group,
883 * and other options disallow it from doing anything useful */
884 bDeformGroup *dg;
885 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, vgroup_index.active);
886 if (dg->flag & DG_LOCK_WEIGHT) {
887 BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting");
888 return false;
889 }
890 if (vgroup_index.mirror != -1) {
891 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, vgroup_index.mirror);
892 if (dg->flag & DG_LOCK_WEIGHT) {
893 BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting");
894 return false;
895 }
896 }
897 }
898
899 /* check that multipaint groups are unlocked */
900 defbase_tot = BLI_listbase_count(&mesh.vertex_group_names);
901 defbase_sel = BKE_object_defgroup_selected_get(&ob, defbase_tot, &defbase_tot_sel);
902
903 if (ts.multipaint && defbase_tot_sel > 1) {
904 int i;
905 bDeformGroup *dg;
906
909 &ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
910 }
911
912 for (i = 0; i < defbase_tot; i++) {
913 if (defbase_sel[i]) {
914 dg = (bDeformGroup *)BLI_findlink(&mesh.vertex_group_names, i);
915 if (dg->flag & DG_LOCK_WEIGHT) {
916 BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting");
917 MEM_freeN(defbase_sel);
918 return false;
919 }
920 }
921 }
922 }
923
924 std::unique_ptr<WPaintData> wpd = std::make_unique<WPaintData>();
926
927 const Brush *brush = BKE_paint_brush_for_read(&vp.paint);
928 vwpaint::view_angle_limits_init(&wpd->normal_angle_precalc,
929 brush->falloff_angle,
930 (brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0);
931
932 wpd->active.index = vgroup_index.active;
933 wpd->mirror.index = vgroup_index.mirror;
934
935 /* multipaint */
936 wpd->defbase_tot = defbase_tot;
937 wpd->defbase_sel = defbase_sel;
938 wpd->defbase_tot_sel = defbase_tot_sel > 1 ? defbase_tot_sel : 1;
939 wpd->do_multipaint = (ts.multipaint && defbase_tot_sel > 1);
940
941 /* set up auto-normalize, and generate map for detecting which
942 * vgroups affect deform bones */
943 wpd->lock_flags = BKE_object_defgroup_lock_flags_get(&ob, wpd->defbase_tot);
944 if (ts.auto_normalize || ts.multipaint || wpd->lock_flags != nullptr || ts.wpaint_lock_relative)
945 {
946 wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(&ob, wpd->defbase_tot);
947 }
948
949 /* Compute the set of all locked deform groups when Lock Relative is active. */
950 if (ts.wpaint_lock_relative &&
952 wpd->lock_flags, wpd->vgroup_validmap, wpd->active.index) &&
953 (!wpd->do_multipaint || BKE_object_defgroup_check_lock_relative_multi(
954 defbase_tot, wpd->lock_flags, defbase_sel, defbase_tot_sel)))
955 {
956 wpd->do_lock_relative = true;
957 }
958
959 if (wpd->do_lock_relative || (ts.auto_normalize && wpd->lock_flags && !wpd->do_multipaint)) {
960 bool *unlocked = (bool *)MEM_dupallocN(wpd->vgroup_validmap);
961
962 if (wpd->lock_flags) {
963 bool *locked = MEM_malloc_arrayN<bool>(wpd->defbase_tot, __func__);
965 wpd->defbase_tot, wpd->lock_flags, wpd->vgroup_validmap, locked, unlocked);
966 wpd->vgroup_locked = locked;
967 }
968
969 wpd->vgroup_unlocked = unlocked;
970 }
971
972 if (wpd->do_multipaint && ts.auto_normalize) {
973 bool *tmpflags;
974 tmpflags = MEM_malloc_arrayN<bool>(defbase_tot, __func__);
975 if (wpd->lock_flags) {
976 BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot);
977 }
978 else {
979 memcpy(tmpflags, wpd->defbase_sel, sizeof(*tmpflags) * wpd->defbase_tot);
980 }
981 wpd->active.lock = tmpflags;
982 }
983 else if (ts.auto_normalize) {
984 bool *tmpflags;
985
986 tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) :
987 MEM_calloc_arrayN<bool>(defbase_tot, __func__);
988 tmpflags[wpd->active.index] = true;
989 wpd->active.lock = tmpflags;
990
991 tmpflags = wpd->lock_flags ? (bool *)MEM_dupallocN(wpd->lock_flags) :
992 MEM_calloc_arrayN<bool>(defbase_tot, __func__);
993 tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true;
994 wpd->mirror.lock = tmpflags;
995 }
996
997 /* If not previously created, create vertex/weight paint mode session data */
999 vwpaint::update_cache_invariants(C, vp, ss, op, mouse);
1001
1002 /* Brush may have changed after initialization. */
1003 brush = BKE_paint_brush(&vp.paint);
1005 wpd->precomputed_weight = MEM_malloc_arrayN<float>(mesh.verts_num, __func__);
1006 }
1007
1008 if (!ob.sculpt->mode.wpaint.dvert_prev.is_empty()) {
1010 for (int i = 0; i < mesh.verts_num; i++, dv++) {
1011 /* Use to show this isn't initialized, never apply to the mesh data. */
1012 dv->flag = 1;
1013 }
1014 }
1015
1016 paint_stroke_set_mode_data(&stroke, std::move(wpd));
1017
1018 return true;
1019}
1020
1021static float wpaint_get_active_weight(const MDeformVert &dv, const WeightPaintInfo &wpi)
1022{
1023 float weight;
1024
1025 if (wpi.do_multipaint) {
1027 &dv, wpi.defbase_tot, wpi.defbase_sel, wpi.defbase_tot_sel, wpi.is_normalized);
1028 }
1029 else {
1030 weight = BKE_defvert_find_weight(&dv, wpi.active.index);
1031 }
1032
1033 if (wpi.do_lock_relative) {
1035 weight, &dv, wpi.defbase_tot, wpi.vgroup_locked, wpi.vgroup_unlocked);
1036 }
1037
1038 CLAMP(weight, 0.0f, 1.0f);
1039 return weight;
1040}
1041
1043 Object &ob, const Brush &brush, WPaintData &wpd, WeightPaintInfo &wpi, Mesh &mesh)
1044{
1045 using namespace blender;
1046 if (wpd.precomputed_weight_ready &&
1048 {
1049 return;
1050 }
1051
1052 threading::parallel_for(IndexRange(mesh.verts_num), 512, [&](const IndexRange range) {
1053 for (const int i : range) {
1054 const MDeformVert &dv = wpi.dvert[i];
1055 wpd.precomputed_weight[i] = wpaint_get_active_weight(dv, wpi);
1056 }
1057 });
1058
1059 wpd.precomputed_weight_ready = true;
1060}
1061
1062/* -------------------------------------------------------------------- */
1065
1067 const IndexMask &node_mask,
1068 FunctionRef<void(IndexRange)> fn)
1069{
1070 /* NOTE: current mirroring code cannot be run in parallel */
1072 fn(node_mask.index_range());
1073 }
1074 else {
1076 node_mask.index_range(), 1, [&](const IndexRange range) { fn(range); });
1077 }
1078}
1079
1080static void filter_factors_with_selection(const Span<bool> select_vert,
1081 const Span<int> verts,
1082 const MutableSpan<float> factors)
1083{
1084 BLI_assert(verts.size() == factors.size());
1085
1086 for (const int i : verts.index_range()) {
1087 if (!select_vert[verts[i]]) {
1088 factors[i] = 0.0f;
1089 }
1090 }
1091}
1092
1093static void do_wpaint_brush_blur(const Depsgraph &depsgraph,
1094 const Scene &scene,
1095 Object &ob,
1096 const Brush &brush,
1097 VPaint &vp,
1098 WPaintData &wpd,
1099 const WeightPaintInfo &wpi,
1100 Mesh &mesh,
1101 const IndexMask &node_mask)
1102{
1103 using namespace blender;
1104 SculptSession &ss = *ob.sculpt;
1106 const StrokeCache &cache = *ss.cache;
1107 const GroupedSpan<int> vert_to_face = mesh.vert_to_face_map();
1108
1109 float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
1111 scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
1112 const bool use_normal = vwpaint::use_normal(vp);
1113 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1114 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1115
1116 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1117 ss, brush.falloff_shape);
1118
1119 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1120 const OffsetIndices faces = mesh.faces();
1121 const Span<int> corner_verts = mesh.corner_verts();
1122 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1123 const bke::AttributeAccessor attributes = mesh.attributes();
1124 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1125 VArraySpan<bool> select_vert;
1126 if (use_vert_sel || use_face_sel) {
1127 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1128 }
1129
1130 struct LocalData {
1131 Vector<float> factors;
1132 Vector<float> distances;
1133 };
1135 parallel_nodes_loop_with_mirror_check(mesh, node_mask, [&](const IndexRange range) {
1136 LocalData &tls = all_tls.local();
1137 node_mask.slice(range).foreach_index([&](const int i) {
1138 const Span<int> verts = nodes[i].verts();
1139 tls.factors.resize(verts.size());
1140 const MutableSpan<float> factors = tls.factors;
1141 fill_factor_from_hide(hide_vert, verts, factors);
1142 filter_region_clip_factors(ss, vert_positions, 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 {
1250 Vector<float> factors;
1251 Vector<float> distances;
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 filter_region_clip_factors(ss, vert_positions, verts, factors);
1262 if (!select_vert.is_empty()) {
1263 filter_factors_with_selection(select_vert, verts, factors);
1264 }
1265
1266 tls.distances.resize(verts.size());
1267 const MutableSpan<float> distances = tls.distances;
1269 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1270 filter_distances_with_radius(cache.radius, distances, factors);
1271 calc_brush_strength_factors(cache, brush, distances, factors);
1272
1273 for (const int i : verts.index_range()) {
1274 const int vert = verts[i];
1275 if (factors[i] == 0.0f) {
1276 continue;
1277 }
1278
1279 float brush_strength = cache.bstrength;
1280 const float angle_cos = use_normal ?
1281 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1282 1.0f;
1284 brush, wpd.normal_angle_precalc, angle_cos, &brush_strength))
1285 {
1286 continue;
1287 }
1288
1289 bool do_color = false;
1290 /* Minimum dot product between brush direction and current
1291 * to neighbor direction is 0.0, meaning orthogonal. */
1292 float stroke_dot_max = 0.0f;
1293
1294 /* Get the color of the loop in the opposite direction of the brush movement
1295 * (this callback is specifically for smear.) */
1296 float weight_final = 0.0;
1297 for (const int face : vert_to_face[vert]) {
1298 for (const int vert_other : corner_verts.slice(faces[face])) {
1299 if (vert_other == vert) {
1300 continue;
1301 }
1302
1303 /* Get the direction from the selected vert to the neighbor. */
1304 float other_dir[3];
1305 sub_v3_v3v3(other_dir, vert_positions[vert], vert_positions[vert_other]);
1306 project_plane_v3_v3v3(other_dir, other_dir, cache.view_normal_symm);
1307
1308 normalize_v3(other_dir);
1309
1310 const float stroke_dot = dot_v3v3(other_dir, brush_dir);
1311
1312 if (stroke_dot > stroke_dot_max) {
1313 stroke_dot_max = stroke_dot;
1314 weight_final = wpd.precomputed_weight[vert_other];
1315 do_color = true;
1316 }
1317 }
1318 if (!do_color) {
1319 continue;
1320 }
1321 const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure;
1322 do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, weight_final);
1323 }
1324 }
1325 });
1326 });
1327}
1328
1329static void do_wpaint_brush_draw(const Depsgraph &depsgraph,
1330 const Scene &scene,
1331 Object &ob,
1332 const Brush &brush,
1333 VPaint &vp,
1334 WPaintData &wpd,
1335 const WeightPaintInfo &wpi,
1336 Mesh &mesh,
1337 const float strength,
1338 const IndexMask &node_mask)
1339{
1340 using namespace blender;
1341 SculptSession &ss = *ob.sculpt;
1343
1344 const StrokeCache &cache = *ss.cache;
1345 /* NOTE: normally `BKE_brush_weight_get(scene, brush)` is used,
1346 * however in this case we calculate a new weight each time. */
1347 const float paintweight = strength;
1348 float brush_size_pressure, brush_alpha_value, brush_alpha_pressure;
1350 scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure);
1351 const bool use_normal = vwpaint::use_normal(vp);
1352 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1353 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1354
1355 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1356 ss, brush.falloff_shape);
1357
1358 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1359 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1360 const bke::AttributeAccessor attributes = mesh.attributes();
1361 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1362 VArraySpan<bool> select_vert;
1363 if (use_vert_sel || use_face_sel) {
1364 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1365 }
1366
1367 struct LocalData {
1368 Vector<float> factors;
1369 Vector<float> distances;
1370 };
1372 parallel_nodes_loop_with_mirror_check(mesh, node_mask, [&](const IndexRange range) {
1373 LocalData &tls = all_tls.local();
1374 node_mask.slice(range).foreach_index([&](const int i) {
1375 const Span<int> verts = nodes[i].verts();
1376 tls.factors.resize(verts.size());
1377 const MutableSpan<float> factors = tls.factors;
1378 fill_factor_from_hide(hide_vert, verts, factors);
1379 filter_region_clip_factors(ss, vert_positions, verts, factors);
1380 if (!select_vert.is_empty()) {
1381 filter_factors_with_selection(select_vert, verts, factors);
1382 }
1383
1384 tls.distances.resize(verts.size());
1385 const MutableSpan<float> distances = tls.distances;
1387 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1388 filter_distances_with_radius(cache.radius, distances, factors);
1389 calc_brush_strength_factors(cache, brush, distances, factors);
1390
1391 for (const int i : verts.index_range()) {
1392 const int vert = verts[i];
1393 if (factors[i] == 0.0f) {
1394 continue;
1395 }
1396 float brush_strength = cache.bstrength;
1397 const float angle_cos = use_normal ?
1398 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1399 1.0f;
1401 brush, wpd.normal_angle_precalc, angle_cos, &brush_strength))
1402 {
1403 continue;
1404 }
1405 const float final_alpha = factors[i] * brush_strength * brush_alpha_pressure;
1406
1407 if ((brush.flag & BRUSH_ACCUMULATE) == 0) {
1408 if (ss.mode.wpaint.alpha_weight[vert] < final_alpha) {
1409 ss.mode.wpaint.alpha_weight[vert] = final_alpha;
1410 }
1411 else {
1412 continue;
1413 }
1414 }
1415
1416 do_weight_paint_vertex(vp, ob, wpi, vert, final_alpha, paintweight);
1417 }
1418 });
1419 });
1420}
1421
1422static float calculate_average_weight(const Depsgraph &depsgraph,
1423 Object &ob,
1424 const Mesh &mesh,
1425 const Brush &brush,
1426 const VPaint &vp,
1427 WeightPaintInfo &wpi,
1428 const IndexMask &node_mask)
1429{
1430 using namespace blender;
1431 SculptSession &ss = *ob.sculpt;
1433 const StrokeCache &cache = *ss.cache;
1434
1435 const bool use_normal = vwpaint::use_normal(vp);
1436 const bool use_face_sel = (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
1437 const bool use_vert_sel = (mesh.editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
1438
1439 const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape(
1440 ss, brush.falloff_shape);
1441
1442 const Span<float3> vert_positions = bke::pbvh::vert_positions_eval(depsgraph, ob);
1443 const Span<float3> vert_normals = bke::pbvh::vert_normals_eval(depsgraph, ob);
1444 const bke::AttributeAccessor attributes = mesh.attributes();
1445 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
1446 VArraySpan<bool> select_vert;
1447 if (use_vert_sel || use_face_sel) {
1448 select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
1449 }
1450
1451 struct LocalData {
1452 Vector<float> factors;
1453 Vector<float> distances;
1454 };
1457 node_mask.index_range(),
1458 1,
1460 [&](const IndexRange range, WPaintAverageAccum accum) {
1461 LocalData &tls = all_tls.local();
1462 node_mask.slice(range).foreach_index([&](const int i) {
1463 const Span<int> verts = nodes[i].verts();
1464 tls.factors.resize(verts.size());
1465 const MutableSpan<float> factors = tls.factors;
1466 fill_factor_from_hide(hide_vert, verts, factors);
1467 filter_region_clip_factors(ss, vert_positions, verts, factors);
1468 if (!select_vert.is_empty()) {
1469 filter_factors_with_selection(select_vert, verts, factors);
1470 }
1471
1472 tls.distances.resize(verts.size());
1473 const MutableSpan<float> distances = tls.distances;
1474 calc_brush_distances(
1475 ss, vert_positions, verts, eBrushFalloffShape(brush.falloff_shape), distances);
1476 filter_distances_with_radius(cache.radius, distances, factors);
1477 calc_brush_strength_factors(cache, brush, distances, factors);
1478
1479 for (const int i : verts.index_range()) {
1480 const int vert = verts[i];
1481 if (factors[i] == 0.0f) {
1482 continue;
1483 }
1484 const float angle_cos = use_normal ?
1485 dot_v3v3(sculpt_normal_frontface, vert_normals[vert]) :
1486 1.0f;
1487 if (angle_cos <= 0.0f) {
1488 continue;
1489 }
1490 const MDeformVert &dv = wpi.dvert[vert];
1491 accum.len++;
1492 accum.value += wpaint_get_active_weight(dv, wpi);
1493 }
1494 });
1495 return accum;
1496 },
1497 [](const WPaintAverageAccum &a, const WPaintAverageAccum &b) {
1498 return WPaintAverageAccum{a.len + b.len, a.value + b.value};
1499 });
1500 return math::safe_divide(value.value, double(value.len));
1501}
1502
1504 Object &ob,
1505 VPaint &vp,
1506 WPaintData &wpd,
1507 WeightPaintInfo &wpi,
1508 Mesh &mesh,
1509 const IndexMask &node_mask)
1510{
1511 const Scene &scene = *CTX_data_scene(C);
1512 const Brush &brush = *ob.sculpt->cache->brush;
1513 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1514
1518 depsgraph,
1519 scene,
1520 ob,
1521 brush,
1522 vp,
1523 wpd,
1524 wpi,
1525 mesh,
1526 calculate_average_weight(depsgraph, ob, mesh, brush, vp, wpi, node_mask),
1527 node_mask);
1528 break;
1529 }
1531 do_wpaint_brush_smear(depsgraph, scene, ob, brush, vp, wpd, wpi, mesh, node_mask);
1532 break;
1534 do_wpaint_brush_blur(depsgraph, scene, ob, brush, vp, wpd, wpi, mesh, node_mask);
1535 break;
1538 scene,
1539 ob,
1540 brush,
1541 vp,
1542 wpd,
1543 wpi,
1544 mesh,
1545 BKE_brush_weight_get(&scene, &brush),
1546 node_mask);
1547 break;
1548 }
1549}
1550
1551
1552/* -------------------------------------------------------------------- */
1555
1556void ED_object_wpaintmode_enter_ex(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
1557{
1559}
1561{
1562 Main &bmain = *CTX_data_main(C);
1563 Scene &scene = *CTX_data_scene(C);
1565 ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob);
1566}
1567
1568
1569/* -------------------------------------------------------------------- */
1572
1582
1583
1584/* -------------------------------------------------------------------- */
1587
1589{
1590 const Object *ob = CTX_data_active_object(C);
1591
1592 return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((const Mesh *)ob->data)->faces_num;
1593}
1594
1599
1600static bool weight_paint_poll_ex(bContext *C, bool check_tool)
1601{
1602 const Object *ob = CTX_data_active_object(C);
1603 const ScrArea *area;
1604
1605 if ((ob != nullptr) && (ob->mode & OB_MODE_WEIGHT_PAINT) &&
1606 (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != nullptr) &&
1607 (area = CTX_wm_area(C)) && (area->spacetype == SPACE_VIEW3D))
1608 {
1609 const ARegion *region = CTX_wm_region(C);
1610 if (region && ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_HUD)) {
1611 if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) {
1612 return true;
1613 }
1614 }
1615 }
1616 return false;
1617}
1618
1620{
1621 return weight_paint_poll_ex(C, true);
1622}
1623
1625{
1626 return weight_paint_poll_ex(C, false);
1627}
1628
1633{
1634 Main &bmain = *CTX_data_main(C);
1635 wmMsgBus *mbus = CTX_wm_message_bus(C);
1637 const int mode_flag = OB_MODE_WEIGHT_PAINT;
1638 const bool is_mode_set = (ob.mode & mode_flag) != 0;
1639 Scene &scene = *CTX_data_scene(C);
1640 ToolSettings &ts = *scene.toolsettings;
1641
1642 if (!is_mode_set) {
1643 if (!blender::ed::object::mode_compat_set(C, &ob, (eObjectMode)mode_flag, op->reports)) {
1644 return OPERATOR_CANCELLED;
1645 }
1646 }
1647
1649
1650 if (is_mode_set) {
1652 }
1653 else {
1655 if (depsgraph) {
1657 }
1658 ED_object_wpaintmode_enter_ex(bmain, *depsgraph, scene, ob);
1660 }
1661
1662 blender::ed::object::posemode_set_for_weight_paint(C, &bmain, &ob, is_mode_set);
1663
1664 /* Weight-paint works by overriding colors in mesh,
1665 * so need to make sure we recalculate on enter and
1666 * exit (exit needs doing regardless because we
1667 * should re-deform).
1668 */
1669 DEG_id_tag_update(&mesh->id, 0);
1670
1672
1673 WM_msg_publish_rna_prop(mbus, &ob.id, &ob, Object, mode);
1674
1676
1677 return OPERATOR_FINISHED;
1678}
1679
1681{
1682 ot->name = "Weight Paint Mode";
1683 ot->idname = "PAINT_OT_weight_paint_toggle";
1684 ot->description = "Toggle weight paint mode in 3D view";
1685
1688
1689 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1690}
1691
1693
1696
1698 Object &ob,
1699 VPaint &wp,
1700 WPaintData &wpd,
1701 WeightPaintInfo &wpi,
1702 Mesh &mesh,
1703 Brush &brush,
1704 const ePaintSymmetryFlags symm,
1705 const int axis,
1706 const int i,
1707 const float angle)
1708{
1709 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1710 SculptSession &ss = *ob.sculpt;
1713
1714 IndexMaskMemory memory;
1715 const IndexMask node_mask = vwpaint::pbvh_gather_generic(depsgraph, ob, wp, brush, memory);
1716
1717 wpaint_paint_leaves(C, ob, wp, wpd, wpi, mesh, node_mask);
1718}
1719
1721 Object &ob,
1722 VPaint &wp,
1723 WPaintData &wpd,
1724 WeightPaintInfo &wpi,
1725 Mesh &mesh,
1726 Brush &brush,
1727 const ePaintSymmetryFlags symm,
1728 const int axis)
1729{
1730 for (int i = 1; i < wp.radial_symm[axis - 'X']; i++) {
1731 const float angle = (2.0 * M_PI) * i / wp.radial_symm[axis - 'X'];
1732 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, axis, i, angle);
1733 }
1734}
1735
1736/* near duplicate of: sculpt.cc's,
1737 * 'do_symmetrical_brush_actions' and 'vpaint_do_symmetrical_brush_actions'. */
1739 bContext *C, Object &ob, VPaint &wp, WPaintData &wpd, WeightPaintInfo &wpi)
1740{
1741 Brush &brush = *BKE_paint_brush(&wp.paint);
1742 Mesh &mesh = *(Mesh *)ob.data;
1743 SculptSession &ss = *ob.sculpt;
1744 StrokeCache &cache = *ss.cache;
1745 const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
1746 int i = 0;
1747
1748 /* initial stroke */
1750 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'X', 0, 0);
1751 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'X');
1752 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'Y');
1753 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'Z');
1754
1755 cache.symmetry = symm;
1756
1757 if (mesh.editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) {
1758 /* We don't do any symmetry strokes when mirroring vertex groups. */
1759 copy_v3_v3(cache.last_location, cache.location);
1760 cache.is_last_valid = true;
1761 return;
1762 }
1763
1764 for (i = 1; i <= symm; i++) {
1765 if (is_symmetry_iteration_valid(i, symm)) {
1767 cache.mirror_symmetry_pass = symm;
1768 cache.radial_symmetry_pass = 0;
1769 SCULPT_cache_calc_brushdata_symm(cache, symm, 0, 0);
1770
1771 if (i & (1 << 0)) {
1772 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X', 0, 0);
1773 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X');
1774 }
1775 if (i & (1 << 1)) {
1776 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y', 0, 0);
1777 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y');
1778 }
1779 if (i & (1 << 2)) {
1780 wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z', 0, 0);
1781 wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z');
1782 }
1783 }
1784 }
1785 copy_v3_v3(cache.last_location, cache.location);
1786 cache.is_last_valid = true;
1787}
1788
1790 wmOperator *op,
1791 PaintStroke *stroke,
1792 PointerRNA *itemptr)
1793{
1794 Scene &scene = *CTX_data_scene(C);
1796 VPaint &wp = *ts.wpaint;
1797 const Brush &brush = *BKE_paint_brush(&wp.paint);
1799 ViewContext *vc;
1801
1802 SculptSession &ss = *ob->sculpt;
1803
1804 vwpaint::update_cache_variants(C, wp, *ob, itemptr);
1805
1806 float mat[4][4];
1807
1808 const float brush_alpha_value = BKE_brush_alpha_get(&scene, &brush);
1809
1810 /* intentionally don't initialize as nullptr, make sure we initialize all members below */
1811 WeightPaintInfo wpi;
1812
1813 if (wpd == nullptr) {
1814 /* XXX: force a redraw here, since even though we can't paint,
1815 * at least view won't freeze until stroke ends */
1817 return;
1818 }
1819
1820 vc = &wpd->vc;
1821 ob = vc->obact;
1822
1825
1826 mul_m4_m4m4(mat, vc->rv3d->persmat, ob->object_to_world().ptr());
1827
1828 Mesh &mesh = *static_cast<Mesh *>(ob->data);
1829
1830 /* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */
1831 wpi.dvert = mesh.deform_verts_for_write();
1832
1833 wpi.defbase_tot = wpd->defbase_tot;
1834 wpi.defbase_sel = wpd->defbase_sel;
1836
1838 wpi.active = wpd->active;
1839 wpi.mirror = wpd->mirror;
1840 wpi.lock_flags = wpd->lock_flags;
1842 wpi.vgroup_locked = wpd->vgroup_locked;
1844 wpi.do_flip = RNA_boolean_get(op->ptr, "pen_flip") || ss.cache->invert;
1845 wpi.do_multipaint = wpd->do_multipaint;
1846 wpi.do_auto_normalize = ((ts.auto_normalize != 0) && (wpi.vgroup_validmap != nullptr) &&
1847 (wpi.do_multipaint || wpi.vgroup_validmap[wpi.active.index]));
1850 wpi.brush_alpha_value = brush_alpha_value;
1851
1852 if (wpd->precomputed_weight) {
1853 precompute_weight_values(*ob, brush, *wpd, wpi, mesh);
1854 }
1855
1856 wpaint_do_symmetrical_brush_actions(C, *ob, wp, *wpd, wpi);
1857
1858 swap_m4m4(vc->rv3d->persmat, mat);
1859
1860 /* Calculate pivot for rotation around selection if needed.
1861 * also needed for "Frame Selected" on last stroke. */
1862 float loc_world[3];
1863 mul_v3_m4v3(loc_world, ob->object_to_world().ptr(), ss.cache->location);
1864 vwpaint::last_stroke_update(scene, loc_world);
1865
1867
1868 DEG_id_tag_update(&mesh.id, 0);
1870 swap_m4m4(wpd->vc.rv3d->persmat, mat);
1871
1873}
1874
1875static void wpaint_stroke_done(const bContext *C, PaintStroke * /*stroke*/)
1876{
1878
1879 SculptSession &ss = *ob.sculpt;
1880
1881 if (ss.cache->alt_smooth) {
1883 VPaint &vp = *ts.wpaint;
1885 }
1886
1887 if (ob.particlesystem.first) {
1889 for (int i = 0; i < PSYS_TOT_VG; i++) {
1890 if (psys->vgroup[i] == BKE_object_defgroup_active_index_get(&ob)) {
1891 psys->recalc |= ID_RECALC_PSYS_RESET;
1892 break;
1893 }
1894 }
1895 }
1896 }
1897
1898 DEG_id_tag_update((ID *)ob.data, 0);
1899
1901
1902 MEM_delete(ob.sculpt->cache);
1903 ob.sculpt->cache = nullptr;
1904}
1905
1907{
1909 op,
1913 nullptr,
1915 event->type);
1916
1917 const wmOperatorStatus retval = op->type->modal(C, op, event);
1918 OPERATOR_RETVAL_CHECK(retval);
1919
1920 if (retval == OPERATOR_FINISHED) {
1922 return OPERATOR_FINISHED;
1923 }
1925
1927
1929}
1930
1932{
1934 op,
1938 nullptr,
1940 0);
1941
1943
1944 return OPERATOR_FINISHED;
1945}
1946
1948{
1950 MEM_delete(ob.sculpt->cache);
1951 ob.sculpt->cache = nullptr;
1952
1954}
1955
1957{
1958 return paint_stroke_modal(C, op, event, (PaintStroke **)&op->customdata);
1959}
1960
1962{
1963 ot->name = "Weight Paint";
1964 ot->idname = "PAINT_OT_weight_paint";
1965 ot->description = "Paint a stroke in the current vertex group's weights";
1966
1967 ot->invoke = wpaint_invoke;
1968 ot->modal = wpaint_modal;
1969 ot->exec = wpaint_exec;
1970 ot->poll = weight_paint_poll;
1971 ot->cancel = wpaint_cancel;
1972
1973 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
1974
1977 ot->srna,
1978 "override_location",
1979 false,
1980 "Override Location",
1981 "Override the given `location` array by recalculating object space positions from the "
1982 "provided `mouse_event` positions");
1984}
1985
float BKE_brush_weight_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1276
float BKE_brush_alpha_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1269
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:596
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:1006
float BKE_defvert_calc_lock_relative_weight(float weight, float locked_weight, float unlocked_weight)
Definition deform.cc:978
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:939
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:763
void BKE_defvert_copy(MDeformVert *dvert_dst, const MDeformVert *dvert_src)
Definition deform.cc:125
#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:961
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)
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:641
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
Definition paint.cc:1109
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
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:991
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:911
bool ED_operator_region_view3d_active(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
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:185
@ IMB_BLEND_DARKEN
Definition IMB_imbuf.hh:191
@ IMB_BLEND_LIGHTEN
Definition IMB_imbuf.hh:190
@ IMB_BLEND_MIX
Definition IMB_imbuf.hh:186
@ IMB_BLEND_ADD
Definition IMB_imbuf.hh:187
@ IMB_BLEND_SUB
Definition IMB_imbuf.hh:188
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
#define ND_DRAW
Definition WM_types.hh:458
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_MODE
Definition WM_types.hh:442
#define NC_SCENE
Definition WM_types.hh:375
#define NC_OBJECT
Definition WM_types.hh:376
BPy_StructRNA * depsgraph
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 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]
#define MEM_SAFE_FREE(v)
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:2912
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2435
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:2416
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])
bool stroke_get_location_bvh(bContext *C, float out[3], const float mval[2], const bool force_original)
Definition sculpt.cc:4919
void calc_brush_strength_factors(const StrokeCache &cache, const Brush &brush, Span< float > distances, MutableSpan< float > factors)
Definition sculpt.cc:7167
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:7089
void filter_region_clip_factors(const SculptSession &ss, Span< float3 > vert_positions, Span< int > verts, MutableSpan< float > factors)
Definition sculpt.cc:6956
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:7039
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:6759
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 weight_paint_poll(bContext *C)
@ 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 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 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 wmOperatorStatus wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
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 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 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 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_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:185
const float * SCULPT_brush_frontface_normal_from_falloff_shape(const SculptSession &ss, char falloff_shape)
Definition sculpt.cc:1181
void SCULPT_cache_calc_brushdata_symm(blender::ed::sculpt_paint::StrokeCache &cache, const ePaintSymmetryFlags symm, const char axis, const float angle)
Definition sculpt.cc:3446
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:2157
float falloff_angle
char falloff_shape
short blend
char weight_brush_type
Definition DNA_ID.h:404
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:437
blender::Array< MDeformVert > dvert_prev
Definition BKE_paint.hh:505
struct SculptSession::@277147341176372332050050347244236340077143206264 mode
float * alpha_weight
Definition BKE_paint.hh:501
struct SculptSession::@277147341176372332050050347244236340077143206264::@267002172247257132177045334232116313020165177372 wpaint
int radial_symm[3]
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:754
wmOperatorStatus(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1078
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:4225
#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)