Blender V5.0
grease_pencil_weight_paint.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_armature.hh"
10#include "BKE_brush.hh"
11#include "BKE_context.hh"
12#include "BKE_crazyspace.hh"
13#include "BKE_deform.hh"
14#include "BKE_grease_pencil.hh"
15#include "BKE_modifier.hh"
16#include "BKE_object_deform.h"
17#include "BKE_paint.hh"
18#include "BKE_report.hh"
19
20#include "BLI_listbase.h"
21#include "BLI_math_geom.h"
22#include "BLI_math_matrix.h"
23
24#include "DNA_brush_types.h"
25#include "DNA_meshdata_types.h"
26
27#include "RNA_access.hh"
28#include "RNA_define.hh"
29
30#include "ED_curves.hh"
31#include "ED_grease_pencil.hh"
32#include "ED_view3d.hh"
33
35
36#include "GEO_smooth_curves.hh"
37
39
41{
42 /* Get all vertex group names in the object. */
43 const ListBase *defbase = BKE_object_defgroup_list(&object);
44 Set<std::string> defgroups;
45 LISTBASE_FOREACH (bDeformGroup *, dg, defbase) {
46 defgroups.add(dg->name);
47 }
48
49 /* Inspect all armature modifiers in the object. */
50 Set<std::string> bone_deformed_vgroups;
51 VirtualModifierData virtual_modifier_data;
52 ModifierData *md = BKE_modifiers_get_virtual_modifierlist(&object, &virtual_modifier_data);
53 for (; md; md = md->next) {
56 {
57 continue;
58 }
60 md);
61 if (!gamd->object || !gamd->object->pose) {
62 continue;
63 }
64
65 bPose *pose = gamd->object->pose;
66 LISTBASE_FOREACH (bPoseChannel *, channel, &pose->chanbase) {
67 if (channel->bone->flag & BONE_NO_DEFORM) {
68 continue;
69 }
70 /* When a vertex group name matches the bone name, it is bone-deformed. */
71 if (defgroups.contains(channel->name)) {
72 bone_deformed_vgroups.add(channel->name);
73 }
74 }
75 }
76
77 return bone_deformed_vgroups;
78}
79
80/* Normalize the weights of vertex groups deformed by bones so that the sum is 1.0f.
81 * Returns false when the normalization failed due to too many locked vertex groups. In that case a
82 * second pass can be done with the active vertex group unlocked.
83 */
85 const int vertex_groups_num,
86 const Span<bool> vertex_group_is_bone_deformed,
87 const FunctionRef<bool(int)> vertex_group_is_locked)
88{
89 /* Nothing to normalize when there are less than two vertex group weights. */
90 if (dvert.totweight <= 1) {
91 return true;
92 }
93
94 /* Get the sum of weights of bone-deformed vertex groups. */
95 float sum_weights_total = 0.0f;
96 float sum_weights_locked = 0.0f;
97 float sum_weights_unlocked = 0.0f;
98 int locked_num = 0;
99 int unlocked_num = 0;
100 for (const int i : IndexRange(dvert.totweight)) {
101 MDeformWeight &dw = dvert.dw[i];
102
103 /* Auto-normalize is only applied on bone-deformed vertex groups that have weight already. */
104 if (dw.def_nr >= vertex_groups_num || !vertex_group_is_bone_deformed[dw.def_nr] ||
105 dw.weight <= FLT_EPSILON)
106 {
107 continue;
108 }
109
110 sum_weights_total += dw.weight;
111
112 if (vertex_group_is_locked(dw.def_nr)) {
113 locked_num++;
114 sum_weights_locked += dw.weight;
115 }
116 else {
117 unlocked_num++;
118 sum_weights_unlocked += dw.weight;
119 }
120 }
121
122 /* Already normalized? */
123 if (sum_weights_total == 1.0f) {
124 return true;
125 }
126
127 /* Any unlocked vertex group to normalize? */
128 if (unlocked_num == 0) {
129 /* We don't need a second pass when there is only one locked group (the active group). */
130 return (locked_num == 1);
131 }
132
133 /* Locked groups can make it impossible to fully normalize. */
134 if (sum_weights_locked >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
135 /* Zero out the weights we are allowed to touch and return false, indicating a second pass is
136 * needed. */
137 for (const int i : IndexRange(dvert.totweight)) {
138 MDeformWeight &dw = dvert.dw[i];
139 if (dw.def_nr < vertex_groups_num && vertex_group_is_bone_deformed[dw.def_nr] &&
140 !vertex_group_is_locked(dw.def_nr))
141 {
142 dw.weight = 0.0f;
143 }
144 }
145
146 return (sum_weights_locked == 1.0f);
147 }
148
149 /* When the sum of the unlocked weights isn't zero, we can use a multiplier to normalize them
150 * to 1.0f. */
151 if (sum_weights_unlocked != 0.0f) {
152 const float normalize_factor = (1.0f - sum_weights_locked) / sum_weights_unlocked;
153
154 for (const int i : IndexRange(dvert.totweight)) {
155 MDeformWeight &dw = dvert.dw[i];
156 if (dw.def_nr < vertex_groups_num && vertex_group_is_bone_deformed[dw.def_nr] &&
157 dw.weight > FLT_EPSILON && !vertex_group_is_locked(dw.def_nr))
158 {
159 dw.weight = math::clamp(dw.weight * normalize_factor, 0.0f, 1.0f);
160 }
161 }
162
163 return true;
164 }
165
166 /* Spread out the remainder of the locked weights over the unlocked weights. */
167 const float weight_remainder = math::clamp(
168 (1.0f - sum_weights_locked) / unlocked_num, 0.0f, 1.0f);
169
170 for (const int i : IndexRange(dvert.totweight)) {
171 MDeformWeight &dw = dvert.dw[i];
172 if (dw.def_nr < vertex_groups_num && vertex_group_is_bone_deformed[dw.def_nr] &&
173 dw.weight > FLT_EPSILON && !vertex_group_is_locked(dw.def_nr))
174 {
175 dw.weight = weight_remainder;
176 }
177 }
178
179 return true;
180}
181
183 const int active_vertex_group,
184 const Span<bool> vertex_group_is_locked,
185 const Span<bool> vertex_group_is_bone_deformed)
186{
187 /* Try to normalize the weights with both active and explicitly locked vertex groups restricted
188 * from change. */
189 const auto active_vertex_group_is_locked = [&](const int vertex_group_index) {
190 return vertex_group_is_locked[vertex_group_index] || vertex_group_index == active_vertex_group;
191 };
192 const bool success = normalize_vertex_weights_try(dvert,
193 vertex_group_is_locked.size(),
194 vertex_group_is_bone_deformed,
195 active_vertex_group_is_locked);
196
197 if (success) {
198 return;
199 }
200
201 /* Do a second pass with the active vertex group unlocked. */
202 const auto active_vertex_group_is_unlocked = [&](const int vertex_group_index) {
203 return vertex_group_is_locked[vertex_group_index];
204 };
206 vertex_group_is_locked.size(),
207 vertex_group_is_bone_deformed,
208 active_vertex_group_is_unlocked);
209}
210
212 Object &ob, const Bone *bone, const FunctionRef<bool(Object &, const Bone *)> bone_callback)
213{
214 int count = 0;
215
216 if (bone != nullptr) {
217 /* Only call `bone_callback` if the bone is non null */
218 count += bone_callback(ob, bone) ? 1 : 0;
219 /* Try to execute `bone_callback` for the first child. */
221 ob, static_cast<Bone *>(bone->childbase.first), bone_callback);
222 /* Try to execute `bone_callback` for the next bone at this depth of the recursion. */
223 count += foreach_bone_in_armature_ex(ob, bone->next, bone_callback);
224 }
225
226 return count;
227}
228
230 const bArmature &armature,
231 const FunctionRef<bool(Object &, const Bone *)> bone_callback)
232{
234 ob, static_cast<const Bone *>(armature.bonebase.first), bone_callback);
235}
236
237bool add_armature_vertex_groups(Object &object, const Object &ob_armature)
238{
239 const bArmature &armature = *static_cast<const bArmature *>(ob_armature.data);
240
241 const int added_vertex_groups = foreach_bone_in_armature(
242 object, armature, [&](Object &object, const Bone *bone) {
243 if ((bone->flag & BONE_NO_DEFORM) == 0) {
244 /* Check if the name of the bone matches a vertex group name. */
245 if (!BKE_object_defgroup_find_name(&object, bone->name)) {
246 /* Add a new vertex group with the name of the bone. */
247 BKE_object_defgroup_add_name(&object, bone->name);
248 return true;
249 }
250 }
251 return false;
252 });
253
254 return added_vertex_groups > 0;
255}
256
258 Object &object,
259 Vector<const Bone *> &r_skinnable_bones,
260 Vector<std::string> &r_deform_group_names)
261{
262 const int added_vertex_groups = foreach_bone_in_armature(
263 object, armature, [&](Object &object, const Bone *bone) {
264 if ((bone->flag & BONE_NO_DEFORM) == 0) {
265 /* Check if the name of the bone matches a vertex group name. */
266 bDeformGroup *dg = BKE_object_defgroup_find_name(&object, bone->name);
267 if (dg == nullptr) {
268 /* Add a new vertex group with the name of the bone. */
269 dg = BKE_object_defgroup_add_name(&object, bone->name);
270 }
271 r_deform_group_names.append(dg->name);
272 r_skinnable_bones.append(bone);
273 return true;
274 }
275 return false;
276 });
277
278 if (added_vertex_groups <= 0) {
279 return false;
280 }
281 return true;
282}
283
285 const float4x4 &transform,
288{
289 threading::parallel_for(bones.index_range(), 4096, [&](const IndexRange range) {
290 for (const int i : range) {
291 const Bone *bone = bones[i];
292 roots[i] = math::transform_point(transform, float3(bone->arm_head));
293 tips[i] = math::transform_point(transform, float3(bone->arm_tail));
294 }
295 });
296}
297
299{
300 int def_nr = BKE_defgroup_name_index(&curves.vertex_group_names, name);
301
302 /* Lazily add the vertex group. */
303 if (def_nr == -1) {
304 bDeformGroup *defgroup = MEM_callocN<bDeformGroup>(__func__);
305 name.copy_utf8_truncated(defgroup->name);
306 BLI_addtail(&curves.vertex_group_names, defgroup);
307 def_nr = BLI_listbase_count(&curves.vertex_group_names) - 1;
308 BLI_assert(def_nr >= 0);
309 }
310
311 return def_nr;
312}
313
314void add_armature_envelope_weights(Scene &scene, Object &object, const Object &ob_armature)
315{
316 using namespace bke;
317 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
318 const bArmature &armature = *static_cast<const bArmature *>(ob_armature.data);
319 const float4x4 armature_to_world = ob_armature.object_to_world();
320 const float scale = mat4_to_scale(armature_to_world.ptr());
321
322 Vector<const Bone *> skinnable_bones;
323 Vector<std::string> deform_group_names;
325 armature, object, skinnable_bones, deform_group_names))
326 {
327 return;
328 }
329
330 /* Get the roots and tips of the bones in world space. */
331 Array<float3> roots(skinnable_bones.size());
332 Array<float3> tips(skinnable_bones.size());
334 skinnable_bones, armature_to_world, roots.as_mutable_span(), tips.as_mutable_span());
335
336 Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
337 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
339 const bke::greasepencil::Layer &layer = *layers[info.layer_index];
340 const float4x4 layer_to_world = layer.to_world_space(object);
341
343
344 /* Get all the positions in world space. */
345 Array<float3> positions(curves.points_num());
346 math::transform_points(curves.positions(), layer_to_world, positions);
347
348 for (const int bone_i : skinnable_bones.index_range()) {
349 const Bone *bone = skinnable_bones[bone_i];
350 const char *deform_group_name = deform_group_names[bone_i].c_str();
351 const float3 bone_root = roots[bone_i];
352 const float3 bone_tip = tips[bone_i];
353
354 const int def_nr = lookup_or_add_deform_group_index(curves, deform_group_name);
355
356 MutableSpan<MDeformVert> dverts = curves.deform_verts_for_write();
358 for (const int point_i : curves.points_range()) {
359 const float weight = distfactor_to_bone(positions[point_i],
360 bone_root,
361 bone_tip,
362 bone->rad_head * scale,
363 bone->rad_tail * scale,
364 bone->dist * scale);
365 if (weight != 0.0f) {
366 weights.set(point_i, weight);
367 }
368 }
369 }
370 });
371}
372
373void add_armature_automatic_weights(Scene &scene, Object &object, const Object &ob_armature)
374{
375 using namespace bke;
376 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
377 const bArmature &armature = *static_cast<const bArmature *>(ob_armature.data);
378 const float4x4 armature_to_world = ob_armature.object_to_world();
379 /* Note: These constant values are taken from the legacy grease pencil code. */
380 const float default_ratio = 0.1f;
381 const float default_decay = 0.8f;
382
383 Vector<const Bone *> skinnable_bones;
384 Vector<std::string> deform_group_names;
386 armature, object, skinnable_bones, deform_group_names))
387 {
388 return;
389 }
390
391 /* Get the roots and tips of the bones in world space. */
392 Array<float3> roots(skinnable_bones.size());
393 Array<float3> tips(skinnable_bones.size());
395 skinnable_bones, armature_to_world, roots.as_mutable_span(), tips.as_mutable_span());
396
397 /* Note: This is taken from the legacy grease pencil code. */
398 const auto get_weight = [](const float dist, const float decay_rad, const float diff_rad) {
399 return (dist < decay_rad) ? 1.0f :
400 math::interpolate(0.9f, 0.0f, (dist - decay_rad) / diff_rad);
401 };
402
403 Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
404 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
406 const bke::greasepencil::Layer &layer = *layers[info.layer_index];
407 const float4x4 layer_to_world = layer.to_world_space(object);
408
410
411 /* Get all the positions in world space. */
412 Array<float3> positions(curves.points_num());
413 math::transform_points(curves.positions(), layer_to_world, positions);
414
415 for (const int bone_i : skinnable_bones.index_range()) {
416 const char *deform_group_name = deform_group_names[bone_i].c_str();
417 const float3 bone_root = roots[bone_i];
418 const float3 bone_tip = tips[bone_i];
419
420 const float radius_squared = math::distance_squared(bone_root, bone_tip) * default_ratio;
421 const float decay_rad = radius_squared - (radius_squared * default_decay);
422 const float diff_rad = radius_squared - decay_rad;
423
424 const int def_nr = lookup_or_add_deform_group_index(curves, deform_group_name);
425
426 MutableSpan<MDeformVert> dverts = curves.deform_verts_for_write();
428 for (const int point_i : curves.points_range()) {
429 const float3 position = positions[point_i];
430 const float dist_to_bone = dist_squared_to_line_segment_v3(position, bone_root, bone_tip);
431 const float weight = (dist_to_bone > radius_squared) ?
432 0.0f :
433 get_weight(dist_to_bone, decay_rad, diff_rad);
434 if (weight != 0.0f) {
435 weights.set(point_i, weight);
436 }
437 }
438 }
439 });
440}
441
447
449 wmOperator * /*op*/,
450 const wmEvent *event)
451{
454
455 /* Get the active vertex group. */
456 const int object_defgroup_nr = BKE_object_defgroup_active_index_get(vc.obact) - 1;
457 if (object_defgroup_nr == -1) {
458 return OPERATOR_CANCELLED;
459 }
460 const bDeformGroup *object_defgroup = static_cast<const bDeformGroup *>(
461 BLI_findlink(BKE_object_defgroup_list(vc.obact), object_defgroup_nr));
462
463 /* Collect visible drawings. */
464 const Object *ob_eval = DEG_get_evaluated(vc.depsgraph, vc.obact);
465 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(vc.obact->data);
466 const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*vc.scene, grease_pencil, false);
467
468 /* Find stroke points closest to mouse cursor position. */
470 drawings.index_range(),
471 1L,
473 [&](const IndexRange range, const ClosestGreasePencilDrawing &init) {
474 ClosestGreasePencilDrawing new_closest = init;
475 for (const int i : range) {
476 DrawingInfo info = drawings[i];
477 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
478
479 /* Skip drawing when it doesn't use the active vertex group. */
480 const int drawing_defgroup_nr = BKE_defgroup_name_index(
481 &info.drawing.strokes().vertex_group_names, object_defgroup->name);
482 if (drawing_defgroup_nr == -1) {
483 continue;
484 }
485
486 /* Get deformation by modifiers. */
489 ob_eval, *vc.obact, info.drawing);
490
491 IndexMaskMemory memory;
492 const IndexMask points = retrieve_visible_points(*vc.obact, info.drawing, memory);
493 if (points.is_empty()) {
494 continue;
495 }
496 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
498 layer_to_world);
500 std::optional<ed::curves::FindClosestData> new_closest_elem =
502 curves.points_by_curve(),
503 deformation.positions,
504 curves.cyclic(),
505 projection,
506 points,
508 event->mval,
509 new_closest.elem);
510 if (new_closest_elem) {
511 new_closest.elem = *new_closest_elem;
512 new_closest.drawing = &info.drawing;
513 new_closest.active_defgroup_index = drawing_defgroup_nr;
514 }
515 }
516 return new_closest;
517 },
519 return (a.elem.distance_sq < b.elem.distance_sq) ? a : b;
520 });
521
522 if (!closest.drawing) {
523 return OPERATOR_CANCELLED;
524 }
525
526 /* From the closest point found, get the vertex weight in the active vertex group. */
527 const VArray<float> point_weights = bke::varray_for_deform_verts(
528 closest.drawing->strokes().deform_verts(), closest.active_defgroup_index);
529 const float new_weight = math::clamp(point_weights[closest.elem.index], 0.0f, 1.0f);
530
531 /* Set the new brush weight. */
532 const ToolSettings *ts = vc.scene->toolsettings;
534 BKE_brush_weight_set(&ts->gp_weightpaint->paint, brush, new_weight);
535
536 /* Update brush settings in UI. */
538
539 return OPERATOR_FINISHED;
540}
541
543{
544 /* Identifiers. */
545 ot->name = "Weight Paint Sample Weight";
546 ot->idname = "GREASE_PENCIL_OT_weight_sample";
547 ot->description =
548 "Set the weight of the Draw tool to the weight of the vertex under the mouse cursor";
549
550 /* Callbacks. */
552 ot->invoke = weight_sample_invoke;
553
554 /* Flags. */
556}
557
559{
561 Brush *brush = BKE_paint_brush(paint);
562
563 /* Toggle direction flag. */
564 brush->flag ^= BRUSH_DIR_IN;
565
567 /* Update brush settings in UI. */
569
570 return OPERATOR_FINISHED;
571}
572
574{
576 return false;
577 }
578
580 if (paint == nullptr) {
581 return false;
582 }
583 Brush *brush = BKE_paint_brush(paint);
584 if (brush == nullptr) {
585 return false;
586 }
588}
589
591{
592 /* Identifiers. */
593 ot->name = "Weight Paint Toggle Direction";
594 ot->idname = "GREASE_PENCIL_OT_weight_toggle_direction";
595 ot->description = "Toggle Add/Subtract for the weight paint draw tool";
596
597 /* Callbacks. */
600
601 /* Flags. */
603}
604
606{
607 const Scene &scene = *CTX_data_scene(C);
609 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
610
611 /* Object vgroup index. */
612 const int active_index = BKE_object_defgroup_active_index_get(object) - 1;
613 if (active_index == -1) {
614 return OPERATOR_CANCELLED;
615 }
616
617 const bDeformGroup *active_defgroup = static_cast<const bDeformGroup *>(
618 BLI_findlink(BKE_object_defgroup_list(object), active_index));
619
620 if (active_defgroup->flag & DG_LOCK_WEIGHT) {
621 BKE_report(op->reports, RPT_WARNING, "Active Vertex Group is locked");
622 return OPERATOR_CANCELLED;
623 }
624
625 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
626
629 /* Active vgroup index of drawing. */
630 const int drawing_vgroup_index = BKE_defgroup_name_index(&curves.vertex_group_names,
631 active_defgroup->name);
632 if (drawing_vgroup_index == -1) {
633 return;
634 }
635
637 curves.deform_verts_for_write(), drawing_vgroup_index);
638 if (weights.size() == 0) {
639 return;
640 }
641
642 for (const int i : weights.index_range()) {
643 const float invert_weight = 1.0f - weights[i];
644 weights.set(i, invert_weight);
645 }
646 });
647
648 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
649 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
650 return OPERATOR_FINISHED;
651}
652
654{
656 return false;
657 }
658
659 const Object *ob = CTX_data_active_object(C);
660 if (ob == nullptr || BLI_listbase_is_empty(BKE_object_defgroup_list(ob))) {
661 return false;
662 }
663
664 return true;
665}
666
668{
669 /* identifiers */
670 ot->name = "Invert Weight";
671 ot->idname = "GREASE_PENCIL_OT_weight_invert";
672 ot->description = "Invert the weight of active vertex group";
673
674 /* API callbacks. */
677
678 /* flags */
680}
681
683{
684 /* Get the active vertex group in the Grease Pencil object. */
686 const int object_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
687 if (object_defgroup_nr == -1) {
688 return OPERATOR_CANCELLED;
689 }
690 const bDeformGroup *object_defgroup = static_cast<const bDeformGroup *>(
691 BLI_findlink(BKE_object_defgroup_list(object), object_defgroup_nr));
692 if (object_defgroup->flag & DG_LOCK_WEIGHT) {
693 BKE_report(op->reports, RPT_WARNING, "Active vertex group is locked");
694 return OPERATOR_CANCELLED;
695 }
696
697 const float smooth_factor = RNA_float_get(op->ptr, "factor");
698 const int repeat = RNA_int_get(op->ptr, "repeat");
699
700 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
701 const Scene &scene = *CTX_data_scene(C);
702 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
703
704 /* Smooth weights in all editable drawings. */
705 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange drawing_range) {
706 for (const int drawing : drawing_range) {
707 bke::CurvesGeometry &curves = drawings[drawing].drawing.strokes_for_write();
708 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
709
710 /* Skip the drawing when it doesn't use the active vertex group. */
711 if (!attributes.contains(object_defgroup->name)) {
712 continue;
713 }
714
715 bke::SpanAttributeWriter<float> weights = attributes.lookup_for_write_span<float>(
716 object_defgroup->name);
717 geometry::smooth_curve_attribute(curves.curves_range(),
718 curves.points_by_curve(),
719 VArray<bool>::from_single(true, curves.points_num()),
720 curves.cyclic(),
721 repeat,
722 smooth_factor,
723 true,
724 false,
725 weights.span);
726 weights.finish();
727 }
728 });
729
730 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
731 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
732
733 return OPERATOR_FINISHED;
734}
735
737{
738 /* Identifiers. */
739 ot->name = "Smooth Vertex Group";
740 ot->idname = "GREASE_PENCIL_OT_vertex_group_smooth";
741 ot->description = "Smooth the weights of the active vertex group";
742
743 /* Callbacks. */
746
747 /* Flags. */
749
750 /* Operator properties. */
751 RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f);
752 RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200);
753}
754
756{
757 /* Get the active vertex group in the Grease Pencil object. */
759 const int object_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
760 if (object_defgroup_nr == -1) {
761 return OPERATOR_CANCELLED;
762 }
763 const bDeformGroup *object_defgroup = static_cast<const bDeformGroup *>(
764 BLI_findlink(BKE_object_defgroup_list(object), object_defgroup_nr));
765 if (object_defgroup->flag & DG_LOCK_WEIGHT) {
766 BKE_report(op->reports, RPT_WARNING, "Active vertex group is locked");
767 return OPERATOR_CANCELLED;
768 }
769
770 /* Get all editable drawings, grouped per frame. */
771 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
772 const Scene &scene = *CTX_data_scene(C);
773 Array<Vector<MutableDrawingInfo>> drawings_per_frame =
775
776 /* Per frame, normalize the weights in the active vertex group. */
777 bool changed = false;
778 for (const int frame_i : drawings_per_frame.index_range()) {
779 /* Get the maximum weight in the active vertex group for this frame. */
780 const Vector<MutableDrawingInfo> drawings = drawings_per_frame[frame_i];
781 const float max_weight_in_frame = threading::parallel_reduce(
782 drawings.index_range(),
783 1,
784 0.0f,
785 [&](const IndexRange drawing_range, const float &drawing_weight_init) {
786 float max_weight_in_drawing = drawing_weight_init;
787 for (const int drawing_i : drawing_range) {
788 const bke::CurvesGeometry &curves = drawings[drawing_i].drawing.strokes();
789 const bke::AttributeAccessor attributes = curves.attributes();
790
791 /* Skip the drawing when it doesn't use the active vertex group. */
792 if (!attributes.contains(object_defgroup->name)) {
793 continue;
794 }
795
796 /* Get the maximum weight in this drawing. */
797 const VArray<float> weights = *curves.attributes().lookup_or_default<float>(
798 object_defgroup->name, bke::AttrDomain::Point, 0.0f);
799 const float max_weight_in_points = threading::parallel_reduce(
800 weights.index_range(),
801 1024,
802 max_weight_in_drawing,
803 [&](const IndexRange point_range, const float &init) {
804 float max_weight = init;
805 for (const int point_i : point_range) {
806 max_weight = math::max(max_weight, weights[point_i]);
807 }
808 return max_weight;
809 },
810 [](const float a, const float b) { return math::max(a, b); });
811 max_weight_in_drawing = math::max(max_weight_in_drawing, max_weight_in_points);
812 }
813 return max_weight_in_drawing;
814 },
815 [](const float a, const float b) { return math::max(a, b); });
816
817 if (ELEM(max_weight_in_frame, 0.0f, 1.0f)) {
818 continue;
819 }
820
821 /* Normalize weights from 0.0 to 1.0, by dividing the weights in the active vertex group by the
822 * maximum weight in the frame. */
823 changed = true;
824 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange drawing_range) {
825 for (const int drawing_i : drawing_range) {
826 bke::CurvesGeometry &curves = drawings[drawing_i].drawing.strokes_for_write();
827 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
828
829 /* Skip the drawing when it doesn't use the active vertex group. */
830 if (!attributes.contains(object_defgroup->name)) {
831 continue;
832 }
833
834 bke::SpanAttributeWriter<float> weights = attributes.lookup_for_write_span<float>(
835 object_defgroup->name);
836 threading::parallel_for(
837 weights.span.index_range(), 1024, [&](const IndexRange point_range) {
838 for (const int point_i : point_range) {
839 weights.span[point_i] /= max_weight_in_frame;
840 }
841 });
842 weights.finish();
843 }
844 });
845 }
846
847 if (changed) {
848 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
849 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
850 }
851
852 return OPERATOR_FINISHED;
853}
854
856{
857 /* Identifiers. */
858 ot->name = "Normalize Vertex Group";
859 ot->idname = "GREASE_PENCIL_OT_vertex_group_normalize";
860 ot->description = "Normalize weights of the active vertex group";
861
862 /* Callbacks. */
865
866 /* Flags. */
868}
869
871{
872 /* Get the active vertex group in the Grease Pencil object. */
874 const int object_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
875 const bDeformGroup *object_defgroup = static_cast<const bDeformGroup *>(
876 BLI_findlink(BKE_object_defgroup_list(object), object_defgroup_nr));
877
878 /* Get the locked vertex groups in the object. */
879 Set<std::string> object_locked_defgroups;
880 const ListBase *defgroups = BKE_object_defgroup_list(object);
881 LISTBASE_FOREACH (bDeformGroup *, dg, defgroups) {
882 if ((dg->flag & DG_LOCK_WEIGHT) != 0) {
883 object_locked_defgroups.add(dg->name);
884 }
885 }
886 const bool lock_active_group = RNA_boolean_get(op->ptr, "lock_active");
887
888 /* Get all editable drawings. */
889 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
890 const Scene &scene = *CTX_data_scene(C);
891 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
892
893 /* Normalize weights in all drawings. */
894 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange drawing_range) {
895 for (const int drawing_i : drawing_range) {
896 bke::CurvesGeometry &curves = drawings[drawing_i].drawing.strokes_for_write();
897
898 /* Get the active vertex group in the drawing when it needs to be locked. */
899 int active_vertex_group = -1;
900 if (object_defgroup && lock_active_group) {
901 active_vertex_group = BKE_defgroup_name_index(&curves.vertex_group_names,
902 object_defgroup->name);
903 }
904
905 /* Put the lock state of every vertex group in a boolean array. */
906 Vector<bool> vertex_group_is_locked;
907 Vector<bool> vertex_group_is_included;
908 LISTBASE_FOREACH (bDeformGroup *, dg, &curves.vertex_group_names) {
909 vertex_group_is_locked.append(object_locked_defgroups.contains(dg->name));
910 /* Dummy, needed for the #normalize_vertex_weights() call. */
911 vertex_group_is_included.append(true);
912 }
913
914 /* For all points in the drawing, normalize the weights of all vertex groups to the sum
915 * of 1.0. */
916 MutableSpan<MDeformVert> deform_verts = curves.deform_verts_for_write();
917 threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange point_range) {
918 for (const int point_i : point_range) {
919 normalize_vertex_weights(deform_verts[point_i],
920 active_vertex_group,
921 vertex_group_is_locked,
922 vertex_group_is_included);
923 }
924 });
925 }
926 });
927
928 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
929 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
930
931 return OPERATOR_FINISHED;
932}
933
935{
936 /* Identifiers. */
937 ot->name = "Normalize All Vertex Groups";
938 ot->idname = "GREASE_PENCIL_OT_vertex_group_normalize_all";
939 ot->description =
940 "Normalize the weights of all vertex groups, so that for each vertex, the sum of all "
941 "weights is 1.0";
942
943 /* Callbacks. */
946
947 /* Flags. */
949
950 /* Operator properties. */
951 RNA_def_boolean(ot->srna,
952 "lock_active",
953 true,
954 "Lock Active",
955 "Keep the values of the active group while normalizing others");
956}
957
958} // namespace blender::ed::greasepencil
959
float distfactor_to_bone(const blender::float3 &position, const blender::float3 &head, const blender::float3 &tail, float radius_head, float radius_tail, float falloff_distance)
void BKE_brush_weight_set(Paint *paint, Brush *brush, float value)
Definition brush.cc:1371
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:764
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
support for deformation groups and hooks.
int BKE_object_defgroup_active_index_get(const Object *ob)
Definition deform.cc:607
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:540
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:585
bDeformGroup * BKE_object_defgroup_find_name(const Object *ob, blender::StringRef name)
Definition deform.cc:526
#define VERTEX_WEIGHT_LOCK_EPSILON
Low-level operations for grease pencil.
ModifierData * BKE_modifiers_get_virtual_modifierlist(const Object *ob, VirtualModifierData *data)
Functions for dealing with objects and deform verts, used by painting and tools.
struct bDeformGroup * BKE_object_defgroup_add_name(struct Object *ob, const char *name)
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:645
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#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)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3])
Definition math_geom.cc:519
float mat4_to_scale(const float mat[4][4])
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ BONE_NO_DEFORM
@ BRUSH_DIR_IN
@ GPWEIGHT_BRUSH_TYPE_DRAW
@ eModifierMode_Virtual
@ eModifierMode_Realtime
@ eModifierType_GreasePencilArmature
@ DG_LOCK_WEIGHT
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
blender::float4x4 ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, const blender::float4x4 &obmat)
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define NC_BRUSH
Definition WM_types.hh:385
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
BPy_StructRNA * depsgraph
bool closest(btVector3 &v)
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
IndexRange index_range() const
Definition BLI_array.hh:360
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
IndexRange index_range() const
void set(const int64_t index, T value)
int64_t size() const
void append(const T &value)
IndexRange index_range() const
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
float4x4 to_world_space(const Object &object) const
void ED_operatortypes_grease_pencil_weight_paint()
int count
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval, const Object &ob_orig, const bke::greasepencil::Drawing &drawing_orig)
VMutableArray< float > varray_for_mutable_deform_verts(MutableSpan< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1747
VArray< float > varray_for_deform_verts(Span< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1743
std::optional< FindClosestData > closest_elem_find_screen_space(const ViewContext &vc, const OffsetIndices< int > points_by_curve, const Span< float3 > positions, const VArray< bool > &cyclic, const float4x4 &projection, const IndexMask &mask, const bke::AttrDomain domain, const int2 coord, const FindClosestData &initial_closest)
static bool normalize_vertex_weights_try(MDeformVert &dvert, const int vertex_groups_num, const Span< bool > vertex_group_is_bone_deformed, const FunctionRef< bool(int)> vertex_group_is_locked)
static wmOperatorStatus grease_pencil_weight_invert_exec(bContext *C, wmOperator *op)
Set< std::string > get_bone_deformed_vertex_group_names(const Object &object)
static wmOperatorStatus vertex_group_smooth_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_vertex_group_smooth(wmOperatorType *ot)
static int foreach_bone_in_armature(Object &ob, const bArmature &armature, const FunctionRef< bool(Object &, const Bone *)> bone_callback)
static void GREASE_PENCIL_OT_vertex_group_normalize(wmOperatorType *ot)
void add_armature_automatic_weights(Scene &scene, Object &object, const Object &ob_armature)
IndexMask retrieve_visible_points(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
Vector< DrawingInfo > retrieve_visible_drawings(const Scene &scene, const GreasePencil &grease_pencil, const bool do_onion_skinning)
static wmOperatorStatus vertex_group_normalize_all_exec(bContext *C, wmOperator *op)
static bool toggle_weight_tool_direction_poll(bContext *C)
static wmOperatorStatus vertex_group_normalize_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_weight_sample(wmOperatorType *ot)
void normalize_vertex_weights(MDeformVert &dvert, const int active_vertex_group, const Span< bool > vertex_group_is_locked, const Span< bool > vertex_group_is_bone_deformed)
static void GREASE_PENCIL_OT_vertex_group_normalize_all(wmOperatorType *ot)
static void GREASE_PENCIL_OT_weight_invert(wmOperatorType *ot)
static wmOperatorStatus toggle_weight_tool_direction_exec(bContext *C, wmOperator *)
static void GREASE_PENCIL_OT_weight_toggle_direction(wmOperatorType *ot)
static wmOperatorStatus weight_sample_invoke(bContext *C, wmOperator *, const wmEvent *event)
void add_armature_envelope_weights(Scene &scene, Object &object, const Object &ob_armature)
bool grease_pencil_weight_painting_poll(bContext *C)
bool add_armature_vertex_groups(Object &object, const Object &ob_armature)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
static int foreach_bone_in_armature_ex(Object &ob, const Bone *bone, const FunctionRef< bool(Object &, const Bone *)> bone_callback)
static bool get_skinnable_bones_and_deform_group_names(const bArmature &armature, Object &object, Vector< const Bone * > &r_skinnable_bones, Vector< std::string > &r_deform_group_names)
Array< Vector< MutableDrawingInfo > > retrieve_editable_drawings_grouped_per_frame(const Scene &scene, GreasePencil &grease_pencil)
static void get_root_and_tips_of_bones(Span< const Bone * > bones, const float4x4 &transform, MutableSpan< float3 > roots, MutableSpan< float3 > tips)
static bool grease_pencil_vertex_group_weight_poll(bContext *C)
static int lookup_or_add_deform_group_index(CurvesGeometry &curves, const StringRef name)
T clamp(const T &a, const T &min, const T &max)
T interpolate(const T &a, const T &b, const FactorT &t)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
T max(const T &a, const T &b)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
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
MatBase< float, 4, 4 > float4x4
VecBase< float, 3 > float3
static void init(bNodeTree *, bNode *node)
static wmOperatorStatus weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const char * name
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
char name[64]
struct Bone * next
ListBase childbase
char gpencil_weight_brush_type
ListBase vertex_group_names
void * first
struct MDeformWeight * dw
unsigned int def_nr
struct ModifierData * next
struct bPose * pose
struct ToolSettings * toolsettings
GpWeightPaint * gp_weightpaint
RegionView3D * rv3d
Definition ED_view3d.hh:80
Scene * scene
Definition ED_view3d.hh:73
Object * obact
Definition ED_view3d.hh:75
Depsgraph * depsgraph
Definition ED_view3d.hh:72
ListBase chanbase
const c_style_mat & ptr() const
const bke::greasepencil::Drawing & drawing
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))