From c95aa8dbd11c418cd490aef3095864cccaf479bb Mon Sep 17 00:00:00 2001 From: Janne Nylander Date: Tue, 12 Aug 2025 10:25:47 +0200 Subject: [PATCH] Fix #139258: Grease Pencil: Strokes lose deform groups when moved to another layer Strokes would lose their deform group values after moving them to a different layer, because `vertex_group_names` weren't transferred to the newly created `CurvesGeometry` inside `execute_realize_curve_tasks`. This commit adds another version of `copy_vertex_group_names` for Curves, and separates the duplicate code into `copy_vertex_group_name`, which is used by both meshes and curves. This also fixed strokes losing deform weights in multiple situations, such as: - When performing a layer merge. - When applying generative modifiers like Mirror or Array Pull Request: https://projects.blender.org/blender/blender/pulls/142881 --- .../intern/grease_pencil_utils.cc | 31 +++++++++- .../geometry/intern/realize_instances.cc | 59 ++++++++++++++----- 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc index a83179eb383..dbd85258455 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_utils.cc @@ -21,6 +21,7 @@ #include "BLI_array_utils.hh" #include "BLI_bounds.hh" +#include "BLI_listbase.h" #include "BLI_math_geom.h" #include "BLI_math_numbers.hh" #include "BLI_math_vector.hh" @@ -1947,15 +1948,32 @@ void apply_eval_grease_pencil_data(const GreasePencil &eval_grease_pencil, } } + /* Gather the original vertex group names. */ + Set orig_vgroup_names; + LISTBASE_FOREACH (bDeformGroup *, dg, &orig_grease_pencil.vertex_group_names) { + orig_vgroup_names.add(dg->name); + } + /* Update the drawings. */ VectorSet all_updated_drawings; + + Set new_vgroup_names; for (auto [layer_eval, layer_orig] : eval_to_orig_layer_map.items()) { - const Drawing *drawing_eval = merged_layers_grease_pencil.get_drawing_at(*layer_eval, - eval_frame); + Drawing *drawing_eval = merged_layers_grease_pencil.get_drawing_at(*layer_eval, eval_frame); Drawing *drawing_orig = orig_grease_pencil.get_drawing_at(*layer_orig, eval_frame); + if (drawing_orig && drawing_eval) { + CurvesGeometry &eval_strokes = drawing_eval->strokes_for_write(); + + /* Check for new vertex groups in CurvesGeometry. */ + LISTBASE_FOREACH (bDeformGroup *, dg, &eval_strokes.vertex_group_names) { + if (!orig_vgroup_names.contains(dg->name)) { + new_vgroup_names.add(dg->name); + } + } + /* Write the data to the original drawing. */ - drawing_orig->strokes_for_write() = std::move(drawing_eval->strokes()); + drawing_orig->strokes_for_write() = std::move(eval_strokes); /* Anonymous attributes shouldn't be available on original geometry. */ drawing_orig->strokes_for_write().attributes_for_write().remove_anonymous(); drawing_orig->tag_topology_changed(); @@ -1963,6 +1981,13 @@ void apply_eval_grease_pencil_data(const GreasePencil &eval_grease_pencil, } } + /* Add new vertex groups to GreasePencil object. */ + for (StringRef new_vgroup_name : new_vgroup_names) { + bDeformGroup *dst = MEM_callocN(__func__); + new_vgroup_name.copy_utf8_truncated(dst->name); + BLI_addtail(&orig_grease_pencil.vertex_group_names, dst); + } + /* Get the original material pointers from the result geometry. */ VectorSet original_materials; const Span eval_materials = Span{eval_grease_pencil.material_array, diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index bae4c3bbc12..23ae80d5a82 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -1663,6 +1663,26 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options, domain_to_range, dst_attribute_writers); } +static void copy_vertex_group_name(ListBase *dst_deform_group, + const OrderedAttributes &ordered_attributes, + const bDeformGroup &src_deform_group) +{ + const StringRef src_name = src_deform_group.name; + const int attribute_index = ordered_attributes.ids.index_of_try(src_name); + if (attribute_index == -1) { + /* The attribute is not propagated to the result (possibly because the mesh isn't included + * in the realized output because of the #VariedDepthOptions input). */ + return; + } + const bke::AttributeDomainAndType kind = ordered_attributes.kinds[attribute_index]; + if (kind.domain != bke::AttrDomain::Point || kind.data_type != bke::AttrType::Float) { + /* Skip if the source attribute can't possibly contain vertex weights. */ + return; + } + bDeformGroup *dst = MEM_callocN(__func__); + src_name.copy_utf8_truncated(dst->name); + BLI_addtail(dst_deform_group, dst); +} static void copy_vertex_group_names(Mesh &dst_mesh, const OrderedAttributes &ordered_attributes, @@ -1674,24 +1694,10 @@ static void copy_vertex_group_names(Mesh &dst_mesh, } for (const Mesh *mesh : src_meshes) { LISTBASE_FOREACH (const bDeformGroup *, src, &mesh->vertex_group_names) { - const StringRef src_name = src->name; - const int attribute_index = ordered_attributes.ids.index_of_try(src_name); - if (attribute_index == -1) { - /* The attribute is not propagated to the result (possibly because the mesh isn't included - * in the realized output because of the #VariedDepthOptions input). */ + if (existing_names.contains(src->name)) { continue; } - const bke::AttributeDomainAndType kind = ordered_attributes.kinds[attribute_index]; - if (kind.domain != bke::AttrDomain::Point || kind.data_type != bke::AttrType::Float) { - /* Prefer using the highest priority domain and type from all input meshes. */ - continue; - } - if (existing_names.contains(src_name)) { - continue; - } - bDeformGroup *dst = MEM_callocN(__func__); - src_name.copy_utf8_truncated(dst->name); - BLI_addtail(&dst_mesh.vertex_group_names, dst); + copy_vertex_group_name(&dst_mesh.vertex_group_names, ordered_attributes, *src); } } } @@ -2060,6 +2066,25 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, dst_attribute_writers); } +static void copy_vertex_group_names(CurvesGeometry &dst_curve, + const OrderedAttributes &ordered_attributes, + const Span src_curves) +{ + Set existing_names; + LISTBASE_FOREACH (const bDeformGroup *, defgroup, &dst_curve.vertex_group_names) { + existing_names.add(defgroup->name); + } + for (const Curves *src_curve : src_curves) { + LISTBASE_FOREACH (const bDeformGroup *, src, &src_curve->geometry.vertex_group_names) { + if (existing_names.contains(src->name)) { + continue; + } + copy_vertex_group_name(&dst_curve.vertex_group_names, ordered_attributes, *src); + existing_names.add(src->name); + } + } +} + static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, const AllCurvesInfo &all_curves_info, const Span tasks, @@ -2105,6 +2130,8 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, const Curves &first_curves_id = *first_task.curve_info->curves; bke::curves_copy_parameters(first_curves_id, *dst_curves_id); + copy_vertex_group_names(dst_curves, ordered_attributes, all_curves_info.order); + /* Prepare id attribute. */ SpanAttributeWriter point_ids; if (all_curves_info.create_id_attribute) {