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