diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index a6f60772f85..c26eae2739a 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -25,6 +25,8 @@ #include "BKE_attribute_math.hh" #include "BKE_curves.h" +struct MDeformVert; + namespace blender::bke { namespace curves::nurbs { @@ -257,6 +259,13 @@ class CurvesGeometry : public ::CurvesGeometry { Span surface_uv_coords() const; MutableSpan surface_uv_coords_for_write(); + /** + * Vertex group data, encoded as an array of indices and weights for every vertex. + * \warning: May be empty. + */ + Span deform_verts() const; + MutableSpan deform_verts_for_write(); + /** * The largest and smallest position values of evaluated points. */ diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index 6488da1eb93..00d061388ed 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -7,6 +7,7 @@ #ifdef __cplusplus # include "BLI_math_vector_types.hh" # include "BLI_offset_indices.hh" +# include "BLI_virtual_array.hh" #endif #ifdef __cplusplus @@ -49,6 +50,10 @@ void BKE_object_defgroup_active_index_set(struct Object *ob, int new_index); const struct ListBase *BKE_id_defgroup_list_get(const struct ID *id); struct ListBase *BKE_id_defgroup_list_get_mutable(struct ID *id); int BKE_id_defgroup_name_index(const struct ID *id, const char *name); +bool BKE_defgroup_listbase_name_find(const ListBase *defbase, + const char *name, + int *r_index, + struct bDeformGroup **r_group); bool BKE_id_defgroup_name_find(const struct ID *id, const char *name, int *r_index, @@ -292,6 +297,12 @@ void BKE_defvert_extract_vgroup_to_faceweights(const struct MDeformVert *dvert, bool invert_vgroup, float *r_weights); +namespace blender::bke { +VArray varray_for_deform_verts(Span dverts, int defgroup_index); +VMutableArray varray_for_mutable_deform_verts(MutableSpan dverts, + int defgroup_index); +void remove_defgroup_index(MutableSpan dverts, int defgroup_index); +} // namespace blender::bke #endif void BKE_defvert_weight_to_rgb(float r_rgb[3], float weight); diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index c175606d1bd..92a208cacd1 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -28,6 +28,7 @@ #include "BKE_curves.hh" #include "BKE_curves_utils.hh" #include "BKE_customdata.h" +#include "BKE_deform.h" namespace blender::bke { @@ -59,6 +60,7 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num) this->curve_num = curve_num; CustomData_reset(&this->point_data); CustomData_reset(&this->curve_data); + BLI_listbase_clear(&this->vertex_group_names); this->attributes_for_write().add( "position", ATTR_DOMAIN_POINT, AttributeInitConstruct()); @@ -97,6 +99,9 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, dst.point_num); CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, dst.curve_num); + dst.vertex_group_active_index = src.vertex_group_active_index; + BKE_defgroup_copy_list(&dst.vertex_group_names, &src.vertex_group_names); + implicit_sharing::copy_shared_pointer(src.curve_offsets, src.runtime->curve_offsets_sharing_info, &dst.curve_offsets, @@ -143,6 +148,10 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) std::swap(dst.curve_offsets, src.curve_offsets); + std::swap(dst.vertex_group_names.first, src.vertex_group_names.first); + std::swap(dst.vertex_group_names.last, src.vertex_group_names.last); + std::swap(dst.vertex_group_active_index, src.vertex_group_active_index); + std::swap(dst.runtime, src.runtime); } @@ -165,6 +174,7 @@ CurvesGeometry::~CurvesGeometry() CustomData_free(&this->curve_data, this->curve_num); implicit_sharing::free_shared_data(&this->curve_offsets, &this->runtime->curve_offsets_sharing_info); + BLI_freelistN(&this->vertex_group_names); MEM_delete(this->runtime); this->runtime = nullptr; } @@ -458,6 +468,28 @@ MutableSpan CurvesGeometry::surface_uv_coords_for_write() return get_mutable_attribute(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_UV_COORDINATE); } +Span CurvesGeometry::deform_verts() const +{ + const MDeformVert *dverts = static_cast( + CustomData_get_layer(&this->point_data, CD_MDEFORMVERT)); + if (dverts == nullptr) { + return {}; + } + return {dverts, this->point_num}; +} + +MutableSpan CurvesGeometry::deform_verts_for_write() +{ + MDeformVert *dvert = static_cast( + CustomData_get_layer_for_write(&this->point_data, CD_MDEFORMVERT, this->point_num)); + if (dvert != nullptr) { + return {dvert, this->point_num}; + } + return {static_cast(CustomData_add_layer( + &this->point_data, CD_MDEFORMVERT, CD_SET_DEFAULT, this->point_num)), + this->point_num}; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1473,6 +1505,8 @@ void CurvesGeometry::blend_read(BlendDataReader &reader) this->curve_offsets); } + BLO_read_list(&reader, &this->vertex_group_names); + /* Recalculate curve type count cache that isn't saved in files. */ this->update_curve_types(); } @@ -1495,6 +1529,8 @@ void CurvesGeometry::blend_write(BlendWriter &writer, &writer, &this->curve_data, write_data.curve_layers, this->curve_num, CD_MASK_ALL, &id); BLO_write_int32_array(&writer, this->curve_num + 1, this->curve_offsets); + + BKE_defbase_blend_write(&writer, &this->vertex_group_names); } /** \} */ diff --git a/source/blender/blenkernel/intern/deform.cc b/source/blender/blenkernel/intern/deform.cc index c30b7a157b1..ff5db0c3790 100644 --- a/source/blender/blenkernel/intern/deform.cc +++ b/source/blender/blenkernel/intern/deform.cc @@ -516,15 +516,14 @@ int BKE_id_defgroup_name_index(const ID *id, const char *name) return index; } -bool BKE_id_defgroup_name_find(const ID *id, - const char *name, - int *r_index, - bDeformGroup **r_group) +bool BKE_defgroup_listbase_name_find(const ListBase *defbase, + const char *name, + int *r_index, + bDeformGroup **r_group) { if (name == nullptr || name[0] == '\0') { return false; } - const ListBase *defbase = BKE_id_defgroup_list_get(id); int index; LISTBASE_FOREACH_INDEX (bDeformGroup *, group, defbase, index) { if (STREQ(name, group->name)) { @@ -540,6 +539,14 @@ bool BKE_id_defgroup_name_find(const ID *id, return false; } +bool BKE_id_defgroup_name_find(const ID *id, + const char *name, + int *r_index, + bDeformGroup **r_group) +{ + return BKE_defgroup_listbase_name_find(BKE_id_defgroup_list_get(id), name, r_index, r_group); +} + const ListBase *BKE_object_defgroup_list(const Object *ob) { BLI_assert(BKE_object_supports_vertex_groups(ob)); @@ -1645,3 +1652,133 @@ void BKE_defvert_blend_read(BlendDataReader *reader, int count, MDeformVert *mdv } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Virtual array implementation for vertex groups. + * \{ */ + +namespace blender::bke { + +class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl { + private: + MDeformVert *dverts_; + const int dvert_index_; + + public: + VArrayImpl_For_VertexWeights(MutableSpan dverts, const int dvert_index) + : VMutableArrayImpl(dverts.size()), dverts_(dverts.data()), dvert_index_(dvert_index) + { + } + + VArrayImpl_For_VertexWeights(Span dverts, const int dvert_index) + : VMutableArrayImpl(dverts.size()), + dverts_(const_cast(dverts.data())), + dvert_index_(dvert_index) + { + } + + float get(const int64_t index) const override + { + if (dverts_ == nullptr) { + return 0.0f; + } + if (const MDeformWeight *weight = this->find_weight_at_index(index)) { + return weight->weight; + } + return 0.0f; + } + + void set(const int64_t index, const float value) override + { + MDeformVert &dvert = dverts_[index]; + if (value == 0.0f) { + if (MDeformWeight *weight = this->find_weight_at_index(index)) { + weight->weight = 0.0f; + } + } + else { + MDeformWeight *weight = BKE_defvert_ensure_index(&dvert, dvert_index_); + weight->weight = value; + } + } + + void set_all(Span src) override + { + threading::parallel_for(src.index_range(), 4096, [&](const IndexRange range) { + for (const int64_t i : range) { + this->set(i, src[i]); + } + }); + } + + void materialize(const IndexMask &mask, float *dst) const override + { + if (dverts_ == nullptr) { + mask.foreach_index([&](const int i) { dst[i] = 0.0f; }); + } + threading::parallel_for(mask.index_range(), 4096, [&](const IndexRange range) { + mask.slice(range).foreach_index_optimized([&](const int64_t index) { + if (const MDeformWeight *weight = this->find_weight_at_index(index)) { + dst[index] = weight->weight; + } + else { + dst[index] = 0.0f; + } + }); + }); + } + + void materialize_to_uninitialized(const IndexMask &mask, float *dst) const override + { + this->materialize(mask, dst); + } + + private: + MDeformWeight *find_weight_at_index(const int64_t index) + { + for (MDeformWeight &weight : MutableSpan(dverts_[index].dw, dverts_[index].totweight)) { + if (weight.def_nr == dvert_index_) { + return &weight; + } + } + return nullptr; + } + const MDeformWeight *find_weight_at_index(const int64_t index) const + { + for (const MDeformWeight &weight : Span(dverts_[index].dw, dverts_[index].totweight)) { + if (weight.def_nr == dvert_index_) { + return &weight; + } + } + return nullptr; + } +}; + +VArray varray_for_deform_verts(Span dverts, const int defgroup_index) +{ + return VArray::For(dverts, defgroup_index); +} +VMutableArray varray_for_mutable_deform_verts(MutableSpan dverts, + const int defgroup_index) +{ + return VMutableArray::For(dverts, defgroup_index); +} + +void remove_defgroup_index(MutableSpan dverts, const int defgroup_index) +{ + threading::parallel_for(dverts.index_range(), 1024, [&](IndexRange range) { + for (MDeformVert &dvert : dverts.slice(range)) { + MDeformWeight *weight = BKE_defvert_find_index(&dvert, defgroup_index); + BKE_defvert_remove_group(&dvert, weight); + for (MDeformWeight &weight : MutableSpan(dvert.dw, dvert.totweight)) { + if (weight.def_nr > defgroup_index) { + weight.def_nr--; + } + } + } + }); +} + +} // namespace blender::bke + +/** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index ddf0118fac1..dfc5c0cb7e4 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -10,6 +10,7 @@ #include "BKE_attribute_math.hh" #include "BKE_curve.h" #include "BKE_curves.hh" +#include "BKE_deform.h" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" @@ -331,6 +332,102 @@ static void tag_component_normals_changed(void *owner) /** \name Attribute Provider Declaration * \{ */ +/** + * This provider makes vertex groups available as float attributes. + */ +class CurvesVertexGroupsAttributeProvider final : public DynamicAttributesProvider { + public: + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final + { + if (attribute_id.is_anonymous()) { + return {}; + } + const CurvesGeometry *curves = static_cast(owner); + if (curves == nullptr) { + return {}; + } + const std::string name = attribute_id.name(); + const int vertex_group_index = BLI_findstringindex( + &curves->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); + if (vertex_group_index < 0) { + return {}; + } + const Span dverts = curves->deform_verts(); + if (dverts.is_empty()) { + static const float default_value = 0.0f; + return {VArray::ForSingle(default_value, curves->points_num()), ATTR_DOMAIN_POINT}; + } + return {bke::varray_for_deform_verts(dverts, vertex_group_index), ATTR_DOMAIN_POINT}; + } + + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final + { + if (attribute_id.is_anonymous()) { + return {}; + } + CurvesGeometry *curves = static_cast(owner); + if (curves == nullptr) { + return {}; + } + const std::string name = attribute_id.name(); + const int vertex_group_index = BLI_findstringindex( + &curves->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); + if (vertex_group_index < 0) { + return {}; + } + MutableSpan dverts = curves->deform_verts_for_write(); + return {bke::varray_for_mutable_deform_verts(dverts, vertex_group_index), ATTR_DOMAIN_POINT}; + } + + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final + { + if (attribute_id.is_anonymous()) { + return false; + } + CurvesGeometry *curves = static_cast(owner); + if (curves == nullptr) { + return true; + } + const std::string name = attribute_id.name(); + + int index; + bDeformGroup *group; + if (!BKE_defgroup_listbase_name_find( + &curves->vertex_group_names, name.c_str(), &index, &group)) { + return false; + } + BLI_remlink(&curves->vertex_group_names, group); + MEM_freeN(group); + if (curves->deform_verts().is_empty()) { + return true; + } + + MutableSpan dverts = curves->deform_verts_for_write(); + bke::remove_defgroup_index(dverts, index); + return true; + } + + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final + { + const CurvesGeometry *curves = static_cast(owner); + if (curves == nullptr) { + return true; + } + LISTBASE_FOREACH (const bDeformGroup *, group, &curves->vertex_group_names) { + if (!callback(group->name, {ATTR_DOMAIN_POINT, CD_PROP_FLOAT})) { + return false; + } + } + return true; + } + + void foreach_domain(const FunctionRef callback) const final + { + callback(ATTR_DOMAIN_POINT); + } +}; + /** * In this function all the attribute providers for a curves component are created. * Most data in this function is statically allocated, because it does not change over time. @@ -538,6 +635,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() curve_access, tag_component_topology_changed); + static CurvesVertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider curve_custom_data(ATTR_DOMAIN_CURVE, curve_access); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); @@ -556,7 +654,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() &curve_type, &resolution, &cyclic}, - {&curve_custom_data, &point_custom_data}); + {&vertex_groups, &curve_custom_data, &point_custom_data}); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index a0205ce43c3..ace3d1ccd79 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -858,103 +858,10 @@ static void tag_component_positions_changed(void *owner) } } -class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl { - private: - MDeformVert *dverts_; - const int dvert_index_; - - public: - VArrayImpl_For_VertexWeights(MutableSpan dverts, const int dvert_index) - : VMutableArrayImpl(dverts.size()), dverts_(dverts.data()), dvert_index_(dvert_index) - { - } - - VArrayImpl_For_VertexWeights(Span dverts, const int dvert_index) - : VMutableArrayImpl(dverts.size()), - dverts_(const_cast(dverts.data())), - dvert_index_(dvert_index) - { - } - - float get(const int64_t index) const override - { - if (dverts_ == nullptr) { - return 0.0f; - } - if (const MDeformWeight *weight = this->find_weight_at_index(index)) { - return weight->weight; - } - return 0.0f; - } - - void set(const int64_t index, const float value) override - { - MDeformVert &dvert = dverts_[index]; - if (value == 0.0f) { - if (MDeformWeight *weight = this->find_weight_at_index(index)) { - weight->weight = 0.0f; - } - } - else { - MDeformWeight *weight = BKE_defvert_ensure_index(&dvert, dvert_index_); - weight->weight = value; - } - } - - void set_all(Span src) override - { - threading::parallel_for(src.index_range(), 4096, [&](const IndexRange range) { - for (const int64_t i : range) { - this->set(i, src[i]); - } - }); - } - - void materialize(const IndexMask &mask, float *dst) const override - { - if (dverts_ == nullptr) { - mask.foreach_index([&](const int i) { dst[i] = 0.0f; }); - } - mask.foreach_index(GrainSize(4096), [&](const int64_t i) { - if (const MDeformWeight *weight = this->find_weight_at_index(i)) { - dst[i] = weight->weight; - } - else { - dst[i] = 0.0f; - } - }); - } - - void materialize_to_uninitialized(const IndexMask &mask, float *dst) const override - { - this->materialize(mask, dst); - } - - private: - MDeformWeight *find_weight_at_index(const int64_t index) - { - for (MDeformWeight &weight : MutableSpan(dverts_[index].dw, dverts_[index].totweight)) { - if (weight.def_nr == dvert_index_) { - return &weight; - } - } - return nullptr; - } - const MDeformWeight *find_weight_at_index(const int64_t index) const - { - for (const MDeformWeight &weight : Span(dverts_[index].dw, dverts_[index].totweight)) { - if (weight.def_nr == dvert_index_) { - return &weight; - } - } - return nullptr; - } -}; - /** * This provider makes vertex groups available as float attributes. */ -class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { +class MeshVertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: GAttributeReader try_get_for_read(const void *owner, const AttributeIDRef &attribute_id) const final @@ -977,8 +884,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { static const float default_value = 0.0f; return {VArray::ForSingle(default_value, mesh->totvert), ATTR_DOMAIN_POINT}; } - return {VArray::For(dverts, vertex_group_index), - ATTR_DOMAIN_POINT}; + return {bke::varray_for_deform_verts(dverts, vertex_group_index), ATTR_DOMAIN_POINT}; } GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final @@ -998,8 +904,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { return {}; } MutableSpan dverts = mesh->deform_verts_for_write(); - return {VMutableArray::For(dverts, vertex_group_index), - ATTR_DOMAIN_POINT}; + return {bke::varray_for_mutable_deform_verts(dverts, vertex_group_index), ATTR_DOMAIN_POINT}; } bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final @@ -1026,17 +931,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { } MutableSpan dverts = mesh->deform_verts_for_write(); - threading::parallel_for(dverts.index_range(), 1024, [&](IndexRange range) { - for (MDeformVert &dvert : dverts.slice(range)) { - MDeformWeight *weight = BKE_defvert_find_index(&dvert, index); - BKE_defvert_remove_group(&dvert, weight); - for (MDeformWeight &weight : MutableSpan(dvert.dw, dvert.totweight)) { - if (weight.def_nr > index) { - weight.def_nr--; - } - } - } - }); + bke::remove_defgroup_index(dverts, index); return true; } @@ -1191,7 +1086,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() edge_access, nullptr); - static VertexGroupsAttributeProvider vertex_groups; + static MeshVertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access); diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index e209c5857b8..75c76962139 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -10,7 +10,9 @@ #include "DNA_ID.h" #include "DNA_customdata_types.h" +#include "DNA_object_types.h" +#include "BLI_listbase.h" #include "BLI_utildefines.h" #ifdef __cplusplus @@ -132,6 +134,14 @@ typedef struct CurvesGeometry { */ int curve_num; + /** + * List of vertex group (#bDeformGroup) names and flags only. + */ + ListBase vertex_group_names; + /** The active index in the #vertex_group_names list. */ + int vertex_group_active_index; + char _pad[4]; + /** * Runtime data for curves, stored as a pointer to allow defining this as a C++ class. */