diff --git a/scripts/startup/bl_ui/properties_data_mesh.py b/scripts/startup/bl_ui/properties_data_mesh.py index a1b1cfd5cb4..2ab74162e81 100644 --- a/scripts/startup/bl_ui/properties_data_mesh.py +++ b/scripts/startup/bl_ui/properties_data_mesh.py @@ -226,7 +226,7 @@ class DATA_PT_vertex_groups(MeshButtonsPanel, Panel): def poll(cls, context): engine = context.engine obj = context.object - return (obj and obj.type in {'MESH', 'LATTICE'} and (engine in cls.COMPAT_ENGINES)) + return (obj and obj.type in {'MESH', 'LATTICE', 'GREASEPENCIL'} and (engine in cls.COMPAT_ENGINES)) def draw(self, context): layout = self.layout diff --git a/source/blender/blenkernel/BKE_grease_pencil_vertex_groups.hh b/source/blender/blenkernel/BKE_grease_pencil_vertex_groups.hh new file mode 100644 index 00000000000..774d8442ac6 --- /dev/null +++ b/source/blender/blenkernel/BKE_grease_pencil_vertex_groups.hh @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bke + * \brief Utility functions for vertex groups in grease pencil objects + */ + +#include "DNA_grease_pencil_types.h" + +namespace blender::bke::greasepencil { + +/** Make sure drawings only contain vertex groups of the #GreasePencil. */ +void validate_drawing_vertex_groups(GreasePencil &grease_pencil); + +/** Assign selected vertices to the vertex group. */ +void assign_to_vertex_group(GreasePencil &grease_pencil, StringRef name, float weight); + +/** + * Remove selected vertices from the vertex group. + * \return True if at least one vertex was removed from the group. + */ +bool remove_from_vertex_group(GreasePencil &grease_pencil, StringRef name, bool use_selection); + +/** Remove vertices from all vertex groups. */ +void clear_vertex_groups(GreasePencil &grease_pencil); + +/** Select or deselect vertices assigned to this group. */ +void select_from_group(GreasePencil &grease_pencil, StringRef name, bool select); + +} // namespace blender::bke::greasepencil \ No newline at end of file diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 8c993638fe0..7c487a29b05 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -150,6 +150,7 @@ set(SRC intern/gpencil_update_cache_legacy.cc intern/grease_pencil.cc intern/grease_pencil_convert_legacy.cc + intern/grease_pencil_vertex_groups.cc intern/icons.cc intern/icons_rasterize.cc intern/idprop.cc @@ -403,6 +404,7 @@ set(SRC BKE_gpencil_update_cache_legacy.h BKE_grease_pencil.h BKE_grease_pencil.hh + BKE_grease_pencil_vertex_groups.hh BKE_icons.h BKE_idprop.h BKE_idprop.hh diff --git a/source/blender/blenkernel/intern/deform.cc b/source/blender/blenkernel/intern/deform.cc index 69be1b39d18..35ae7115e98 100644 --- a/source/blender/blenkernel/intern/deform.cc +++ b/source/blender/blenkernel/intern/deform.cc @@ -15,6 +15,7 @@ #include "MEM_guardedalloc.h" #include "DNA_gpencil_legacy_types.h" +#include "DNA_grease_pencil_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -33,6 +34,8 @@ #include "BKE_customdata.hh" #include "BKE_data_transfer.h" #include "BKE_deform.hh" /* own include */ +#include "BKE_grease_pencil.hh" +#include "BKE_grease_pencil_vertex_groups.hh" #include "BKE_mesh.hh" #include "BKE_mesh_mapping.hh" #include "BKE_object.hh" @@ -57,6 +60,11 @@ bDeformGroup *BKE_object_defgroup_new(Object *ob, const char *name) BLI_addtail(defbase, defgroup); BKE_object_defgroup_unique_name(defgroup, ob); + if (ob->type == OB_GREASE_PENCIL) { + blender::bke::greasepencil::validate_drawing_vertex_groups( + *static_cast(ob->data)); + } + BKE_object_batch_cache_dirty_tag(ob); return defgroup; @@ -440,7 +448,7 @@ bool BKE_id_supports_vertex_groups(const ID *id) if (id == nullptr) { return false; } - return ELEM(GS(id->name), ID_ME, ID_LT, ID_GD_LEGACY); + return ELEM(GS(id->name), ID_ME, ID_LT, ID_GD_LEGACY, ID_GP); } bool BKE_object_supports_vertex_groups(const Object *ob) @@ -465,6 +473,10 @@ const ListBase *BKE_id_defgroup_list_get(const ID *id) const bGPdata *gpd = (const bGPdata *)id; return &gpd->vertex_group_names; } + case ID_GP: { + const GreasePencil *grease_pencil = (const GreasePencil *)id; + return &grease_pencil->vertex_group_names; + } default: { BLI_assert_unreachable(); } @@ -488,6 +500,10 @@ static const int *object_defgroup_active_index_get_p(const Object *ob) const bGPdata *gpd = (const bGPdata *)ob->data; return &gpd->vertex_group_active_index; } + case OB_GREASE_PENCIL: { + const GreasePencil *grease_pencil = (const GreasePencil *)ob->data; + return &grease_pencil->vertex_group_active_index; + } } return nullptr; } diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 8dc07d9af65..0a06002de66 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -11,6 +11,7 @@ #include "BKE_anim_data.h" #include "BKE_curves.hh" #include "BKE_customdata.hh" +#include "BKE_deform.hh" #include "BKE_geometry_set.hh" #include "BKE_grease_pencil.h" #include "BKE_grease_pencil.hh" @@ -116,6 +117,9 @@ static void grease_pencil_copy_data(Main * /*bmain*/, CD_MASK_ALL, grease_pencil_dst->layers().size()); + BKE_defgroup_copy_list(&grease_pencil_dst->vertex_group_names, + &grease_pencil_src->vertex_group_names); + /* Make sure the runtime pointer exists. */ grease_pencil_dst->runtime = MEM_new(__func__); } @@ -132,6 +136,8 @@ static void grease_pencil_free_data(ID *id) free_drawing_array(*grease_pencil); MEM_delete(&grease_pencil->root_group()); + BLI_freelistN(&grease_pencil->vertex_group_names); + BKE_grease_pencil_batch_cache_free(grease_pencil); MEM_delete(grease_pencil->runtime); @@ -179,6 +185,8 @@ static void grease_pencil_blend_write(BlendWriter *writer, ID *id, const void *i /* Write materials. */ BLO_write_pointer_array( writer, grease_pencil->material_array_num, grease_pencil->material_array); + /* Write vertex group names. */ + BKE_defbase_blend_write(writer, &grease_pencil->vertex_group_names); } static void grease_pencil_blend_read_data(BlendDataReader *reader, ID *id) @@ -195,6 +203,8 @@ static void grease_pencil_blend_read_data(BlendDataReader *reader, ID *id) /* Read materials. */ BLO_read_pointer_array(reader, reinterpret_cast(&grease_pencil->material_array)); + /* Read vertex group names. */ + BLO_read_list(reader, &grease_pencil->vertex_group_names); grease_pencil->runtime = MEM_new(__func__); } diff --git a/source/blender/blenkernel/intern/grease_pencil_vertex_groups.cc b/source/blender/blenkernel/intern/grease_pencil_vertex_groups.cc new file mode 100644 index 00000000000..236a65bb879 --- /dev/null +++ b/source/blender/blenkernel/intern/grease_pencil_vertex_groups.cc @@ -0,0 +1,196 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "DNA_meshdata_types.h" + +#include "BLI_listbase.h" +#include "BLI_set.hh" +#include "BLI_string.h" +#include "BLI_string_utils.hh" + +#include "BKE_curves.hh" +#include "BKE_deform.hh" +#include "BKE_grease_pencil.hh" +#include "BKE_grease_pencil_vertex_groups.hh" + +#include "BLT_translation.h" + +namespace blender::bke::greasepencil { + +/* ------------------------------------------------------------------- */ +/** \name Vertex groups in drawings + * \{ */ + +void validate_drawing_vertex_groups(GreasePencil &grease_pencil) +{ + Set valid_names; + LISTBASE_FOREACH (const bDeformGroup *, defgroup, &grease_pencil.vertex_group_names) { + valid_names.add_new(defgroup->name); + } + + for (GreasePencilDrawingBase *base : grease_pencil.drawings()) { + if (base->type != GP_DRAWING) { + continue; + } + Drawing &drawing = reinterpret_cast(base)->wrap(); + + /* Remove unknown vertex groups. */ + CurvesGeometry &curves = drawing.strokes_for_write(); + int defgroup_index = 0; + LISTBASE_FOREACH_MUTABLE (bDeformGroup *, defgroup, &curves.vertex_group_names) { + if (!valid_names.contains(defgroup->name)) { + remove_defgroup_index(curves.deform_verts_for_write(), defgroup_index); + + BLI_remlink(&curves.vertex_group_names, defgroup); + MEM_SAFE_FREE(defgroup); + } + + ++defgroup_index; + } + } +} + +void assign_to_vertex_group(GreasePencil &grease_pencil, const StringRef name, const float weight) +{ + for (GreasePencilDrawingBase *base : grease_pencil.drawings()) { + if (base->type != GP_DRAWING) { + continue; + } + Drawing &drawing = reinterpret_cast(base)->wrap(); + bke::CurvesGeometry &curves = drawing.strokes_for_write(); + ListBase &vertex_group_names = curves.vertex_group_names; + + const bke::AttributeAccessor attributes = curves.attributes(); + const VArray selection = *attributes.lookup_or_default( + ".selection", bke::AttrDomain::Point, true); + + /* Look for existing group, otherwise lazy-initialize if any vertex is selected. */ + int def_nr = BLI_findstringindex( + &vertex_group_names, name.data(), offsetof(bDeformGroup, name)); + + const MutableSpan dverts = curves.deform_verts_for_write(); + for (const int i : dverts.index_range()) { + if (selection[i]) { + /* Lazily add the vertex group if any vertex is selected. */ + if (def_nr < 0) { + bDeformGroup *defgroup = MEM_cnew(__func__); + STRNCPY(defgroup->name, name.data()); + + BLI_addtail(&vertex_group_names, defgroup); + def_nr = BLI_listbase_count(&vertex_group_names) - 1; + BLI_assert(def_nr >= 0); + } + + MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[i], def_nr); + if (dw) { + dw->weight = weight; + } + } + } + } +} + +/** Remove selected vertices from the vertex group. */ +bool remove_from_vertex_group(GreasePencil &grease_pencil, + const StringRef name, + const bool use_selection) +{ + bool changed = false; + for (GreasePencilDrawingBase *base : grease_pencil.drawings()) { + if (base->type != GP_DRAWING) { + continue; + } + Drawing &drawing = reinterpret_cast(base)->wrap(); + bke::CurvesGeometry &curves = drawing.strokes_for_write(); + ListBase &vertex_group_names = curves.vertex_group_names; + + const int def_nr = BLI_findstringindex( + &vertex_group_names, name.data(), offsetof(bDeformGroup, name)); + if (def_nr < 0) { + /* No vertices assigned to the group in this drawing. */ + continue; + } + + const MutableSpan dverts = curves.deform_verts_for_write(); + const bke::AttributeAccessor attributes = curves.attributes(); + const VArray selection = *attributes.lookup_or_default( + ".selection", bke::AttrDomain::Point, true); + for (const int i : dverts.index_range()) { + if (!use_selection || selection[i]) { + MDeformVert *dv = &dverts[i]; + MDeformWeight *dw = BKE_defvert_find_index(dv, def_nr); + BKE_defvert_remove_group(dv, dw); + + /* Adjust remaining vertex group indices. */ + for (const int j : IndexRange(dv->totweight)) { + if (dv->dw[j].def_nr > def_nr) { + dv->dw[j].def_nr--; + } + } + + changed = true; + } + } + } + return changed; +} + +void clear_vertex_groups(GreasePencil &grease_pencil) +{ + for (GreasePencilDrawingBase *base : grease_pencil.drawings()) { + if (base->type != GP_DRAWING) { + continue; + } + Drawing &drawing = reinterpret_cast(base)->wrap(); + bke::CurvesGeometry &curves = drawing.strokes_for_write(); + + for (MDeformVert &dvert : curves.deform_verts_for_write()) { + BKE_defvert_clear(&dvert); + } + } +} + +void select_from_group(GreasePencil &grease_pencil, const StringRef name, const bool select) +{ + for (GreasePencilDrawingBase *base : grease_pencil.drawings()) { + if (base->type != GP_DRAWING) { + continue; + } + Drawing &drawing = reinterpret_cast(base)->wrap(); + bke::CurvesGeometry &curves = drawing.strokes_for_write(); + ListBase &vertex_group_names = curves.vertex_group_names; + + const int def_nr = BLI_findstringindex( + &vertex_group_names, name.data(), offsetof(bDeformGroup, name)); + if (def_nr < 0) { + /* No vertices assigned to the group in this drawing. */ + continue; + } + + const Span dverts = curves.deform_verts_for_write(); + if (!dverts.is_empty()) { + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + SpanAttributeWriter selection = attributes.lookup_or_add_for_write_span( + ".selection", + bke::AttrDomain::Point, + AttributeInitVArray(VArray::ForSingle(true, curves.point_num))); + + for (const int i : selection.span.index_range()) { + if (BKE_defvert_find_index(&dverts[i], def_nr)) { + selection.span[i] = select; + } + } + + selection.finish(); + } + } +} + +/** \} */ + +} // namespace blender::bke::greasepencil diff --git a/source/blender/blenkernel/intern/object_deform.cc b/source/blender/blenkernel/intern/object_deform.cc index 851fe0f64c1..19109cfc5ee 100644 --- a/source/blender/blenkernel/intern/object_deform.cc +++ b/source/blender/blenkernel/intern/object_deform.cc @@ -22,6 +22,7 @@ #include "DNA_cloth_types.h" #include "DNA_curve_types.h" #include "DNA_gpencil_legacy_types.h" +#include "DNA_grease_pencil_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -35,6 +36,7 @@ #include "BKE_deform.hh" #include "BKE_editmesh.hh" #include "BKE_gpencil_legacy.h" +#include "BKE_grease_pencil_vertex_groups.hh" #include "BKE_mesh.hh" #include "BKE_modifier.hh" #include "BKE_object.hh" @@ -197,6 +199,11 @@ bool BKE_object_defgroup_clear(Object *ob, bDeformGroup *dg, const bool use_sele } } } + else if (ob->type == OB_GREASE_PENCIL) { + GreasePencil *grease_pencil = static_cast(ob->data); + changed = blender::bke::greasepencil::remove_from_vertex_group( + *grease_pencil, dg->name, use_selection); + } return changed; } @@ -264,6 +271,10 @@ static void object_defgroup_remove_common(Object *ob, bDeformGroup *dg, const in Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); MEM_SAFE_FREE(lt->dvert); } + else if (ob->type == OB_GREASE_PENCIL) { + GreasePencil *grease_pencil = static_cast(ob->data); + blender::bke::greasepencil::clear_vertex_groups(*grease_pencil); + } } else if (BKE_object_defgroup_active_index_get(ob) < 1) { /* Keep a valid active index if we still have some vgroups. */ @@ -281,24 +292,30 @@ static void object_defgroup_remove_object_mode(Object *ob, bDeformGroup *dg) BLI_assert(def_nr != -1); - BKE_object_defgroup_array_get(static_cast(ob->data), &dvert_array, &dvert_tot); + if (ob->type == OB_GREASE_PENCIL) { + GreasePencil *grease_pencil = static_cast(ob->data); + blender::bke::greasepencil::remove_from_vertex_group(*grease_pencil, dg->name, false); + } + else { + BKE_object_defgroup_array_get(static_cast(ob->data), &dvert_array, &dvert_tot); - if (dvert_array) { - int i, j; - MDeformVert *dv; - for (i = 0, dv = dvert_array; i < dvert_tot; i++, dv++) { - MDeformWeight *dw; + if (dvert_array) { + int i, j; + MDeformVert *dv; + for (i = 0, dv = dvert_array; i < dvert_tot; i++, dv++) { + MDeformWeight *dw; - dw = BKE_defvert_find_index(dv, def_nr); - BKE_defvert_remove_group(dv, dw); /* dw can be nullptr */ + dw = BKE_defvert_find_index(dv, def_nr); + BKE_defvert_remove_group(dv, dw); /* dw can be nullptr */ - /* inline, make into a function if anything else needs to do this */ - for (j = 0; j < dv->totweight; j++) { - if (dv->dw[j].def_nr > def_nr) { - dv->dw[j].def_nr--; + /* inline, make into a function if anything else needs to do this */ + for (j = 0; j < dv->totweight; j++) { + if (dv->dw[j].def_nr > def_nr) { + dv->dw[j].def_nr--; + } } + /* done */ } - /* done */ } } @@ -357,6 +374,10 @@ static void object_defgroup_remove_edit_mode(Object *ob, bDeformGroup *dg) } } } + else if (ob->type == OB_GREASE_PENCIL) { + GreasePencil *grease_pencil = static_cast(ob->data); + blender::bke::greasepencil::remove_from_vertex_group(*grease_pencil, dg->name, false); + } object_defgroup_remove_common(ob, dg, def_nr); } @@ -374,6 +395,11 @@ void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup) object_defgroup_remove_object_mode(ob, defgroup); } + if (ob->type == OB_GREASE_PENCIL) { + blender::bke::greasepencil::validate_drawing_vertex_groups( + *static_cast(ob->data)); + } + BKE_object_batch_cache_dirty_tag(ob); } } @@ -411,6 +437,10 @@ void BKE_object_defgroup_remove_all_ex(Object *ob, bool only_unlocked) Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data)); MEM_SAFE_FREE(lt->dvert); } + else if (ob->type == OB_GREASE_PENCIL) { + GreasePencil *grease_pencil = static_cast(ob->data); + blender::bke::greasepencil::clear_vertex_groups(*grease_pencil); + } /* Fix counters/indices */ BKE_object_defgroup_active_index_set(ob, 0); } @@ -506,6 +536,10 @@ bool BKE_object_defgroup_array_get(ID *id, MDeformVert **dvert_arr, int *dvert_t *dvert_tot = lt->pntsu * lt->pntsv * lt->pntsw; return true; } + case ID_GP: + /* Should not be used with grease pencil objects.*/ + BLI_assert_unreachable(); + break; default: break; } diff --git a/source/blender/editors/object/object_vgroup.cc b/source/blender/editors/object/object_vgroup.cc index 01b03a36a66..1af599161f1 100644 --- a/source/blender/editors/object/object_vgroup.cc +++ b/source/blender/editors/object/object_vgroup.cc @@ -35,6 +35,7 @@ #include "BKE_customdata.hh" #include "BKE_deform.hh" #include "BKE_editmesh.hh" +#include "BKE_grease_pencil_vertex_groups.hh" #include "BKE_lattice.hh" #include "BKE_layer.hh" #include "BKE_mesh.hh" @@ -1024,7 +1025,8 @@ static void vgroup_select_verts(Object *ob, int select) const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; const ListBase *defbase = BKE_object_defgroup_list(ob); - if (!BLI_findlink(defbase, def_nr)) { + const bDeformGroup *def_group = static_cast(BLI_findlink(defbase, def_nr)); + if (!def_group) { return; } @@ -1107,6 +1109,11 @@ static void vgroup_select_verts(Object *ob, int select) } } } + else if (ob->type == OB_GREASE_PENCIL) { + GreasePencil *grease_pencil = static_cast(ob->data); + blender::bke::greasepencil::select_from_group(*grease_pencil, def_group->name, bool(select)); + DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY); + } } static void vgroup_duplicate(Object *ob) @@ -2309,6 +2316,12 @@ static void vgroup_assign_verts(Object *ob, const float weight) } } } + else if (ob->type == OB_GREASE_PENCIL) { + GreasePencil *grease_pencil = static_cast(ob->data); + const bDeformGroup *defgroup = static_cast( + BLI_findlink(BKE_object_defgroup_list(ob), def_nr)); + blender::bke::greasepencil::assign_to_vertex_group(*grease_pencil, defgroup->name, weight); + } } /** \} */ diff --git a/source/blender/makesdna/DNA_grease_pencil_types.h b/source/blender/makesdna/DNA_grease_pencil_types.h index d1c36637eb1..8b4e551649b 100644 --- a/source/blender/makesdna/DNA_grease_pencil_types.h +++ b/source/blender/makesdna/DNA_grease_pencil_types.h @@ -51,6 +51,7 @@ struct GreasePencil; struct BlendDataReader; struct BlendWriter; struct Object; +struct bDeformGroup; typedef enum GreasePencilStrokeCapType { GP_STROKE_CAP_TYPE_ROUND = 0, @@ -433,6 +434,11 @@ typedef struct GreasePencil { * Global flag on the data-block. */ uint32_t flag; + + ListBase vertex_group_names; + int vertex_group_active_index; + char _pad4[4]; + /** * Onion skinning settings. */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 10db885e52c..db110cbb2c0 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -491,7 +491,8 @@ typedef enum ObjectType { OB_POINTCLOUD, \ OB_VOLUME, \ OB_GREASE_PENCIL)) -#define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL_LEGACY)) +#define OB_TYPE_SUPPORT_VGROUP(_type) \ + (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL_LEGACY, OB_GREASE_PENCIL)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ (ELEM(_type, \ OB_MESH, \