Blender V4.5
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 const Span<float3> src_positions = curves.positions();
344 /* Get all the positions in world space. */
345 Array<float3> positions(curves.points_num());
346 threading::parallel_for(positions.index_range(), 4096, [&](const IndexRange range) {
347 for (const int i : range) {
348 positions[i] = math::transform_point(layer_to_world, src_positions[i]);
349 }
350 });
351
352 for (const int bone_i : skinnable_bones.index_range()) {
353 const Bone *bone = skinnable_bones[bone_i];
354 const char *deform_group_name = deform_group_names[bone_i].c_str();
355 const float3 bone_root = roots[bone_i];
356 const float3 bone_tip = tips[bone_i];
357
358 const int def_nr = lookup_or_add_deform_group_index(curves, deform_group_name);
359
360 MutableSpan<MDeformVert> dverts = curves.deform_verts_for_write();
362 for (const int point_i : curves.points_range()) {
363 const float weight = distfactor_to_bone(positions[point_i],
364 bone_root,
365 bone_tip,
366 bone->rad_head * scale,
367 bone->rad_tail * scale,
368 bone->dist * scale);
369 if (weight != 0.0f) {
370 weights.set(point_i, weight);
371 }
372 }
373 }
374 });
375}
376
377void add_armature_automatic_weights(Scene &scene, Object &object, const Object &ob_armature)
378{
379 using namespace bke;
380 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
381 const bArmature &armature = *static_cast<const bArmature *>(ob_armature.data);
382 const float4x4 armature_to_world = ob_armature.object_to_world();
383 /* Note: These constant values are taken from the legacy grease pencil code. */
384 const float default_ratio = 0.1f;
385 const float default_decay = 0.8f;
386
387 Vector<const Bone *> skinnable_bones;
388 Vector<std::string> deform_group_names;
390 armature, object, skinnable_bones, deform_group_names))
391 {
392 return;
393 }
394
395 /* Get the roots and tips of the bones in world space. */
396 Array<float3> roots(skinnable_bones.size());
397 Array<float3> tips(skinnable_bones.size());
399 skinnable_bones, armature_to_world, roots.as_mutable_span(), tips.as_mutable_span());
400
401 /* Note: This is taken from the legacy grease pencil code. */
402 const auto get_weight = [](const float dist, const float decay_rad, const float diff_rad) {
403 return (dist < decay_rad) ? 1.0f :
404 math::interpolate(0.9f, 0.0f, (dist - decay_rad) / diff_rad);
405 };
406
407 Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
408 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
410 const bke::greasepencil::Layer &layer = *layers[info.layer_index];
411 const float4x4 layer_to_world = layer.to_world_space(object);
412
414 const Span<float3> src_positions = curves.positions();
415 /* Get all the positions in world space. */
416 Array<float3> positions(curves.points_num());
417 threading::parallel_for(positions.index_range(), 4096, [&](const IndexRange range) {
418 for (const int i : range) {
419 positions[i] = math::transform_point(layer_to_world, src_positions[i]);
420 }
421 });
422
423 for (const int bone_i : skinnable_bones.index_range()) {
424 const char *deform_group_name = deform_group_names[bone_i].c_str();
425 const float3 bone_root = roots[bone_i];
426 const float3 bone_tip = tips[bone_i];
427
428 const float radius_squared = math::distance_squared(bone_root, bone_tip) * default_ratio;
429 const float decay_rad = radius_squared - (radius_squared * default_decay);
430 const float diff_rad = radius_squared - decay_rad;
431
432 const int def_nr = lookup_or_add_deform_group_index(curves, deform_group_name);
433
434 MutableSpan<MDeformVert> dverts = curves.deform_verts_for_write();
436 for (const int point_i : curves.points_range()) {
437 const float3 position = positions[point_i];
438 const float dist_to_bone = dist_squared_to_line_segment_v3(position, bone_root, bone_tip);
439 const float weight = (dist_to_bone > radius_squared) ?
440 0.0f :
441 get_weight(dist_to_bone, decay_rad, diff_rad);
442 if (weight != 0.0f) {
443 weights.set(point_i, weight);
444 }
445 }
446 }
447 });
448}
449
455
457 wmOperator * /*op*/,
458 const wmEvent *event)
459{
462
463 /* Get the active vertex group. */
464 const int object_defgroup_nr = BKE_object_defgroup_active_index_get(vc.obact) - 1;
465 if (object_defgroup_nr == -1) {
466 return OPERATOR_CANCELLED;
467 }
468 const bDeformGroup *object_defgroup = static_cast<const bDeformGroup *>(
469 BLI_findlink(BKE_object_defgroup_list(vc.obact), object_defgroup_nr));
470
471 /* Collect visible drawings. */
472 const Object *ob_eval = DEG_get_evaluated(vc.depsgraph, vc.obact);
473 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(vc.obact->data);
474 const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*vc.scene, grease_pencil, false);
475
476 /* Find stroke points closest to mouse cursor position. */
478 drawings.index_range(),
479 1L,
481 [&](const IndexRange range, const ClosestGreasePencilDrawing &init) {
482 ClosestGreasePencilDrawing new_closest = init;
483 for (const int i : range) {
484 DrawingInfo info = drawings[i];
485 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
486
487 /* Skip drawing when it doesn't use the active vertex group. */
488 const int drawing_defgroup_nr = BKE_defgroup_name_index(
489 &info.drawing.strokes().vertex_group_names, object_defgroup->name);
490 if (drawing_defgroup_nr == -1) {
491 continue;
492 }
493
494 /* Get deformation by modifiers. */
497 ob_eval, *vc.obact, info.drawing);
498
499 IndexMaskMemory memory;
500 const IndexMask points = retrieve_visible_points(*vc.obact, info.drawing, memory);
501 if (points.is_empty()) {
502 continue;
503 }
504 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
506 layer_to_world);
508 std::optional<ed::curves::FindClosestData> new_closest_elem =
510 curves.points_by_curve(),
511 deformation.positions,
512 curves.cyclic(),
513 projection,
514 points,
516 event->mval,
517 new_closest.elem);
518 if (new_closest_elem) {
519 new_closest.elem = *new_closest_elem;
520 new_closest.drawing = &info.drawing;
521 new_closest.active_defgroup_index = drawing_defgroup_nr;
522 }
523 }
524 return new_closest;
525 },
527 return (a.elem.distance_sq < b.elem.distance_sq) ? a : b;
528 });
529
530 if (!closest.drawing) {
531 return OPERATOR_CANCELLED;
532 }
533
534 /* From the closest point found, get the vertex weight in the active vertex group. */
535 const VArray<float> point_weights = bke::varray_for_deform_verts(
536 closest.drawing->strokes().deform_verts(), closest.active_defgroup_index);
537 const float new_weight = math::clamp(point_weights[closest.elem.index], 0.0f, 1.0f);
538
539 /* Set the new brush weight. */
540 const ToolSettings *ts = vc.scene->toolsettings;
542 BKE_brush_weight_set(vc.scene, brush, new_weight);
543
544 /* Update brush settings in UI. */
546
547 return OPERATOR_FINISHED;
548}
549
551{
552 /* Identifiers. */
553 ot->name = "Weight Paint Sample Weight";
554 ot->idname = "GREASE_PENCIL_OT_weight_sample";
555 ot->description =
556 "Set the weight of the Draw tool to the weight of the vertex under the mouse cursor";
557
558 /* Callbacks. */
560 ot->invoke = weight_sample_invoke;
561
562 /* Flags. */
564}
565
567{
569 Brush *brush = BKE_paint_brush(paint);
570
571 /* Toggle direction flag. */
572 brush->flag ^= BRUSH_DIR_IN;
573
575 /* Update brush settings in UI. */
577
578 return OPERATOR_FINISHED;
579}
580
582{
584 return false;
585 }
586
588 if (paint == nullptr) {
589 return false;
590 }
591 Brush *brush = BKE_paint_brush(paint);
592 if (brush == nullptr) {
593 return false;
594 }
596}
597
599{
600 /* Identifiers. */
601 ot->name = "Weight Paint Toggle Direction";
602 ot->idname = "GREASE_PENCIL_OT_weight_toggle_direction";
603 ot->description = "Toggle Add/Subtract for the weight paint draw tool";
604
605 /* Callbacks. */
608
609 /* Flags. */
611}
612
614{
615 const Scene &scene = *CTX_data_scene(C);
617 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
618
619 /* Object vgroup index. */
620 const int active_index = BKE_object_defgroup_active_index_get(object) - 1;
621 if (active_index == -1) {
622 return OPERATOR_CANCELLED;
623 }
624
625 const bDeformGroup *active_defgroup = static_cast<const bDeformGroup *>(
626 BLI_findlink(BKE_object_defgroup_list(object), active_index));
627
628 if (active_defgroup->flag & DG_LOCK_WEIGHT) {
629 BKE_report(op->reports, RPT_WARNING, "Active Vertex Group is locked");
630 return OPERATOR_CANCELLED;
631 }
632
633 Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
634
637 /* Active vgroup index of drawing. */
638 const int drawing_vgroup_index = BKE_defgroup_name_index(&curves.vertex_group_names,
639 active_defgroup->name);
640 if (drawing_vgroup_index == -1) {
641 return;
642 }
643
645 curves.deform_verts_for_write(), drawing_vgroup_index);
646 if (weights.size() == 0) {
647 return;
648 }
649
650 for (const int i : weights.index_range()) {
651 const float invert_weight = 1.0f - weights[i];
652 weights.set(i, invert_weight);
653 }
654 });
655
656 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
657 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
658 return OPERATOR_FINISHED;
659}
660
662{
664 return false;
665 }
666
667 const Object *ob = CTX_data_active_object(C);
668 if (ob == nullptr || BLI_listbase_is_empty(BKE_object_defgroup_list(ob))) {
669 return false;
670 }
671
672 return true;
673}
674
676{
677 /* identifiers */
678 ot->name = "Invert Weight";
679 ot->idname = "GREASE_PENCIL_OT_weight_invert";
680 ot->description = "Invert the weight of active vertex group";
681
682 /* API callbacks. */
685
686 /* flags */
688}
689
691{
692 /* Get the active vertex group in the Grease Pencil object. */
694 const int object_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
695 if (object_defgroup_nr == -1) {
696 return OPERATOR_CANCELLED;
697 }
698 const bDeformGroup *object_defgroup = static_cast<const bDeformGroup *>(
699 BLI_findlink(BKE_object_defgroup_list(object), object_defgroup_nr));
700 if (object_defgroup->flag & DG_LOCK_WEIGHT) {
701 BKE_report(op->reports, RPT_WARNING, "Active vertex group is locked");
702 return OPERATOR_CANCELLED;
703 }
704
705 const float smooth_factor = RNA_float_get(op->ptr, "factor");
706 const int repeat = RNA_int_get(op->ptr, "repeat");
707
708 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
709 const Scene &scene = *CTX_data_scene(C);
710 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
711
712 /* Smooth weights in all editable drawings. */
713 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange drawing_range) {
714 for (const int drawing : drawing_range) {
715 bke::CurvesGeometry &curves = drawings[drawing].drawing.strokes_for_write();
716 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
717
718 /* Skip the drawing when it doesn't use the active vertex group. */
719 if (!attributes.contains(object_defgroup->name)) {
720 continue;
721 }
722
723 bke::SpanAttributeWriter<float> weights = attributes.lookup_for_write_span<float>(
724 object_defgroup->name);
725 geometry::smooth_curve_attribute(curves.curves_range(),
726 curves.points_by_curve(),
727 VArray<bool>::ForSingle(true, curves.points_num()),
728 curves.cyclic(),
729 repeat,
730 smooth_factor,
731 true,
732 false,
733 weights.span);
734 weights.finish();
735 }
736 });
737
738 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
739 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
740
741 return OPERATOR_FINISHED;
742}
743
745{
746 /* Identifiers. */
747 ot->name = "Smooth Vertex Group";
748 ot->idname = "GREASE_PENCIL_OT_vertex_group_smooth";
749 ot->description = "Smooth the weights of the active vertex group";
750
751 /* Callbacks. */
754
755 /* Flags. */
757
758 /* Operator properties. */
759 RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f);
760 RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200);
761}
762
764{
765 /* Get the active vertex group in the Grease Pencil object. */
767 const int object_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
768 if (object_defgroup_nr == -1) {
769 return OPERATOR_CANCELLED;
770 }
771 const bDeformGroup *object_defgroup = static_cast<const bDeformGroup *>(
772 BLI_findlink(BKE_object_defgroup_list(object), object_defgroup_nr));
773 if (object_defgroup->flag & DG_LOCK_WEIGHT) {
774 BKE_report(op->reports, RPT_WARNING, "Active vertex group is locked");
775 return OPERATOR_CANCELLED;
776 }
777
778 /* Get all editable drawings, grouped per frame. */
779 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
780 const Scene &scene = *CTX_data_scene(C);
781 Array<Vector<MutableDrawingInfo>> drawings_per_frame =
783
784 /* Per frame, normalize the weights in the active vertex group. */
785 bool changed = false;
786 for (const int frame_i : drawings_per_frame.index_range()) {
787 /* Get the maximum weight in the active vertex group for this frame. */
788 const Vector<MutableDrawingInfo> drawings = drawings_per_frame[frame_i];
789 const float max_weight_in_frame = threading::parallel_reduce(
790 drawings.index_range(),
791 1,
792 0.0f,
793 [&](const IndexRange drawing_range, const float &drawing_weight_init) {
794 float max_weight_in_drawing = drawing_weight_init;
795 for (const int drawing_i : drawing_range) {
796 const bke::CurvesGeometry &curves = drawings[drawing_i].drawing.strokes();
797 const bke::AttributeAccessor attributes = curves.attributes();
798
799 /* Skip the drawing when it doesn't use the active vertex group. */
800 if (!attributes.contains(object_defgroup->name)) {
801 continue;
802 }
803
804 /* Get the maximum weight in this drawing. */
805 const VArray<float> weights = *curves.attributes().lookup_or_default<float>(
806 object_defgroup->name, bke::AttrDomain::Point, 0.0f);
807 const float max_weight_in_points = threading::parallel_reduce(
808 weights.index_range(),
809 1024,
810 max_weight_in_drawing,
811 [&](const IndexRange point_range, const float &init) {
812 float max_weight = init;
813 for (const int point_i : point_range) {
814 max_weight = math::max(max_weight, weights[point_i]);
815 }
816 return max_weight;
817 },
818 [](const float a, const float b) { return math::max(a, b); });
819 max_weight_in_drawing = math::max(max_weight_in_drawing, max_weight_in_points);
820 }
821 return max_weight_in_drawing;
822 },
823 [](const float a, const float b) { return math::max(a, b); });
824
825 if (ELEM(max_weight_in_frame, 0.0f, 1.0f)) {
826 continue;
827 }
828
829 /* Normalize weights from 0.0 to 1.0, by dividing the weights in the active vertex group by the
830 * maximum weight in the frame. */
831 changed = true;
832 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange drawing_range) {
833 for (const int drawing_i : drawing_range) {
834 bke::CurvesGeometry &curves = drawings[drawing_i].drawing.strokes_for_write();
835 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
836
837 /* Skip the drawing when it doesn't use the active vertex group. */
838 if (!attributes.contains(object_defgroup->name)) {
839 continue;
840 }
841
842 bke::SpanAttributeWriter<float> weights = attributes.lookup_for_write_span<float>(
843 object_defgroup->name);
844 threading::parallel_for(
845 weights.span.index_range(), 1024, [&](const IndexRange point_range) {
846 for (const int point_i : point_range) {
847 weights.span[point_i] /= max_weight_in_frame;
848 }
849 });
850 weights.finish();
851 }
852 });
853 }
854
855 if (changed) {
856 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
857 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
858 }
859
860 return OPERATOR_FINISHED;
861}
862
864{
865 /* Identifiers. */
866 ot->name = "Normalize Vertex Group";
867 ot->idname = "GREASE_PENCIL_OT_vertex_group_normalize";
868 ot->description = "Normalize weights of the active vertex group";
869
870 /* Callbacks. */
873
874 /* Flags. */
876}
877
879{
880 /* Get the active vertex group in the Grease Pencil object. */
882 const int object_defgroup_nr = BKE_object_defgroup_active_index_get(object) - 1;
883 const bDeformGroup *object_defgroup = static_cast<const bDeformGroup *>(
884 BLI_findlink(BKE_object_defgroup_list(object), object_defgroup_nr));
885
886 /* Get the locked vertex groups in the object. */
887 Set<std::string> object_locked_defgroups;
888 const ListBase *defgroups = BKE_object_defgroup_list(object);
889 LISTBASE_FOREACH (bDeformGroup *, dg, defgroups) {
890 if ((dg->flag & DG_LOCK_WEIGHT) != 0) {
891 object_locked_defgroups.add(dg->name);
892 }
893 }
894 const bool lock_active_group = RNA_boolean_get(op->ptr, "lock_active");
895
896 /* Get all editable drawings. */
897 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
898 const Scene &scene = *CTX_data_scene(C);
899 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
900
901 /* Normalize weights in all drawings. */
902 threading::parallel_for(drawings.index_range(), 1, [&](const IndexRange drawing_range) {
903 for (const int drawing_i : drawing_range) {
904 bke::CurvesGeometry &curves = drawings[drawing_i].drawing.strokes_for_write();
905
906 /* Get the active vertex group in the drawing when it needs to be locked. */
907 int active_vertex_group = -1;
908 if (object_defgroup && lock_active_group) {
909 active_vertex_group = BKE_defgroup_name_index(&curves.vertex_group_names,
910 object_defgroup->name);
911 }
912
913 /* Put the lock state of every vertex group in a boolean array. */
914 Vector<bool> vertex_group_is_locked;
915 Vector<bool> vertex_group_is_included;
916 LISTBASE_FOREACH (bDeformGroup *, dg, &curves.vertex_group_names) {
917 vertex_group_is_locked.append(object_locked_defgroups.contains(dg->name));
918 /* Dummy, needed for the #normalize_vertex_weights() call. */
919 vertex_group_is_included.append(true);
920 }
921
922 /* For all points in the drawing, normalize the weights of all vertex groups to the sum
923 * of 1.0. */
924 MutableSpan<MDeformVert> deform_verts = curves.deform_verts_for_write();
925 threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange point_range) {
926 for (const int point_i : point_range) {
927 normalize_vertex_weights(deform_verts[point_i],
928 active_vertex_group,
929 vertex_group_is_locked,
930 vertex_group_is_included);
931 }
932 });
933 }
934 });
935
936 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
937 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
938
939 return OPERATOR_FINISHED;
940}
941
943{
944 /* Identifiers. */
945 ot->name = "Normalize All Vertex Groups";
946 ot->idname = "GREASE_PENCIL_OT_vertex_group_normalize_all";
947 ot->description =
948 "Normalize the weights of all vertex groups, so that for each vertex, the sum of all "
949 "weights is 1.0";
950
951 /* Callbacks. */
954
955 /* Flags. */
957
958 /* Operator properties. */
959 RNA_def_boolean(ot->srna,
960 "lock_active",
961 true,
962 "Lock Active",
963 "Keep the values of the active group while normalizing others");
964}
965
966} // namespace blender::ed::greasepencil
967
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:1283
void BKE_brush_tag_unsaved_changes(Brush *brush)
Definition brush.cc:720
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:596
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:529
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:574
bDeformGroup * BKE_object_defgroup_find_name(const Object *ob, blender::StringRef name)
Definition deform.cc:515
#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:467
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#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:982
@ 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:390
#define NC_BRUSH
Definition WM_types.hh:382
#define ND_DATA
Definition WM_types.hh:506
@ 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:581
BPy_StructRNA * depsgraph
bool closest(btVector3 &v)
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: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
void copy_utf8_truncated(char *dst, int64_t dst_size) const
Definition string_ref.cc:28
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:1787
VArray< float > varray_for_deform_verts(Span< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1783
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 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)
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:760
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:4225
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))