CurvesGeometry: Add initial vertex group support #106944

Merged
Falk David merged 24 commits from filedescriptor/blender:curves-deform-verts into main 2023-09-27 10:26:16 +02:00
7 changed files with 312 additions and 116 deletions

View File

@ -25,6 +25,8 @@
#include "BKE_attribute_math.hh"
#include "BKE_curves.h"
struct MDeformVert;
filedescriptor marked this conversation as resolved Outdated

Does it work to forward declare MDeformVert? It would be nice not to include DNA_meshdata_types.h in the curves header.

Does it work to forward declare `MDeformVert`? It would be nice not to include `DNA_meshdata_types.h` in the curves header.
namespace blender::bke {
namespace curves::nurbs {
@ -257,6 +259,13 @@ class CurvesGeometry : public ::CurvesGeometry {
Span<float2> surface_uv_coords() const;
MutableSpan<float2> surface_uv_coords_for_write();
/**
* Vertex group data, encoded as an array of indices and weights for every vertex.
* \warning: May be empty.
*/
Span<MDeformVert> deform_verts() const;
MutableSpan<MDeformVert> deform_verts_for_write();
/**
* The largest and smallest position values of evaluated points.
*/

View File

@ -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<float> varray_for_deform_verts(Span<MDeformVert> dverts, int defgroup_index);
filedescriptor marked this conversation as resolved Outdated
  • const int -> int
  • dvert_index -> defgroup_index/vgroup_index
- `const int` -> `int` - `dvert_index` -> `defgroup_index`/`vgroup_index`
VMutableArray<float> varray_for_mutable_deform_verts(MutableSpan<MDeformVert> dverts,
int defgroup_index);
void remove_defgroup_index(MutableSpan<MDeformVert> dverts, int defgroup_index);
} // namespace blender::bke
#endif
void BKE_defvert_weight_to_rgb(float r_rgb[3], float weight);

View File

@ -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);
filedescriptor marked this conversation as resolved Outdated

Not sure this comment is really necessary, it's just the same thing as the CustomData_reset calls above.

Not sure this comment is really necessary, it's just the same thing as the `CustomData_reset` calls above.
this->attributes_for_write().add<float3>(
"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);
filedescriptor marked this conversation as resolved
Review

src.vertex_group_active_index = 0; That part is unnecessary I think

`src.vertex_group_active_index = 0;` That part is unnecessary I think
std::swap(dst.runtime, src.runtime);
filedescriptor marked this conversation as resolved Outdated

Can just keep it simpler by swapping everything.

std::swap(dst.vertex_group_names, src.vertex_group_names);
std::swap(dst.vertex_group_active_index, src.vertex_group_active_index);
Can just keep it simpler by swapping everything. ``` std::swap(dst.vertex_group_names, src.vertex_group_names); std::swap(dst.vertex_group_active_index, src.vertex_group_active_index); ```
}
@ -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<float2> CurvesGeometry::surface_uv_coords_for_write()
return get_mutable_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_UV_COORDINATE);
}
Span<MDeformVert> CurvesGeometry::deform_verts() const
{
const MDeformVert *dverts = static_cast<const MDeformVert *>(
CustomData_get_layer(&this->point_data, CD_MDEFORMVERT));
if (dverts == nullptr) {
return {};
}
return {dverts, this->point_num};
}
MutableSpan<MDeformVert> CurvesGeometry::deform_verts_for_write()
{
MDeformVert *dvert = static_cast<MDeformVert *>(
CustomData_get_layer_for_write(&this->point_data, CD_MDEFORMVERT, this->point_num));
if (dvert != nullptr) {
return {dvert, this->point_num};
}
return {static_cast<MDeformVert *>(CustomData_add_layer(
&this->point_data, CD_MDEFORMVERT, CD_SET_DEFAULT, this->point_num)),
filedescriptor marked this conversation as resolved Outdated

Casting style

Casting style
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);
}
/** \} */

View File

@ -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<float> {
private:
MDeformVert *dverts_;
const int dvert_index_;
public:
VArrayImpl_For_VertexWeights(MutableSpan<MDeformVert> dverts, const int dvert_index)
: VMutableArrayImpl<float>(dverts.size()), dverts_(dverts.data()), dvert_index_(dvert_index)
{
}
VArrayImpl_For_VertexWeights(Span<MDeformVert> dverts, const int dvert_index)
: VMutableArrayImpl<float>(dverts.size()),
dverts_(const_cast<MDeformVert *>(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<float> 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<int64_t>([&](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<float> varray_for_deform_verts(Span<MDeformVert> dverts, const int defgroup_index)
{
return VArray<float>::For<VArrayImpl_For_VertexWeights>(dverts, defgroup_index);
}
VMutableArray<float> varray_for_mutable_deform_verts(MutableSpan<MDeformVert> dverts,
const int defgroup_index)
{
return VMutableArray<float>::For<VArrayImpl_For_VertexWeights>(dverts, defgroup_index);
}
void remove_defgroup_index(MutableSpan<MDeformVert> 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
/** \} */

View File

@ -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<const CurvesGeometry *>(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<MDeformVert> dverts = curves->deform_verts();
if (dverts.is_empty()) {
static const float default_value = 0.0f;
return {VArray<float>::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<CurvesGeometry *>(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<MDeformVert> 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<CurvesGeometry *>(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<MDeformVert> 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
filedescriptor marked this conversation as resolved Outdated

It would be nice to not duplicate this code between meshes and curves too, could that be shared in BKE_deform.h too?

It would be nice to not duplicate this code between meshes and curves too, could that be shared in `BKE_deform.h` too?
{
const CurvesGeometry *curves = static_cast<const CurvesGeometry *>(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<void(eAttrDomain)> 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});
}
/** \} */

View File

@ -858,103 +858,10 @@ static void tag_component_positions_changed(void *owner)
}
}
class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
private:
MDeformVert *dverts_;
const int dvert_index_;
public:
VArrayImpl_For_VertexWeights(MutableSpan<MDeformVert> dverts, const int dvert_index)
: VMutableArrayImpl<float>(dverts.size()), dverts_(dverts.data()), dvert_index_(dvert_index)
{
}
VArrayImpl_For_VertexWeights(Span<MDeformVert> dverts, const int dvert_index)
: VMutableArrayImpl<float>(dverts.size()),
dverts_(const_cast<MDeformVert *>(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<float> 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<float>::ForSingle(default_value, mesh->totvert), ATTR_DOMAIN_POINT};
}
return {VArray<float>::For<VArrayImpl_For_VertexWeights>(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<MDeformVert> dverts = mesh->deform_verts_for_write();
return {VMutableArray<float>::For<VArrayImpl_For_VertexWeights>(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<MDeformVert> 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);

View File

@ -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.
*/