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
6 changed files with 271 additions and 8 deletions
Showing only changes of commit 66a2e04159 - Show all commits

View File

@ -49,6 +49,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,

View File

@ -0,0 +1,119 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. */
#pragma once
/** \file
* \ingroup bke
*
* Low-level deform utilities.
*/
#include "BKE_deform.h"
#include "BLI_index_mask.hh"
#include "BLI_index_range.hh"
#include "BLI_span.hh"
#include "BLI_task.hh"
#include "BLI_virtual_array.hh"
#include "DNA_meshdata_types.h"
namespace blender::bke {
class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
filedescriptor marked this conversation as resolved Outdated

This entire implementation shouldn't be in a header. Since virtual arrays use virtual inheritance, none of it needs to be in a header actually.

This entire implementation shouldn't be in a header. Since virtual arrays use virtual inheritance, none of it needs to be in a header actually.

I'm not sure I understand. Should I move this entire code to a .cc file? Why are all other classes inheriting from VMutableArrayImpl defined in a header then?

I'm not sure I understand. Should I move this entire code to a `.cc` file? Why are all other classes inheriting from `VMutableArrayImpl` defined in a header then?

Why are all other classes inheriting from VMutableArrayImpl defined in a header then?

They are templated to work on any type, this is not. You can move this to a cc file and just expose a function that returns a virtual array (VArray) for a vertex group.

> Why are all other classes inheriting from `VMutableArrayImpl` defined in a header then? They are templated to work on any type, this is not. You can move this to a cc file and just expose a function that returns a virtual array (`VArray`) for a vertex group.
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(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) {
for (const int64_t i : mask.slice(range)) {
if (const MDeformWeight *weight = this->find_weight_at_index(i)) {
dst[i] = weight->weight;
}
else {
dst[i] = 0.0f;
}
}
});
}
void materialize_to_uninitialized(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;
}
};
} // namespace blender::bke

View File

@ -378,6 +378,7 @@ set(SRC
BKE_customdata_file.h
BKE_data_transfer.h
BKE_deform.h
BKE_deform.hh
BKE_displist.h
BKE_duplilist.h
BKE_dynamicpaint.h

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));

View File

@ -10,6 +10,7 @@
#include "BKE_attribute_math.hh"
#include "BKE_curve.h"
#include "BKE_curves.hh"
#include "BKE_deform.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
@ -331,6 +332,114 @@ 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 {VArray<float>::For<VArrayImpl_For_VertexWeights>(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 {VMutableArray<float>::For<VArrayImpl_For_VertexWeights>(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();
threading::parallel_for(dverts.index_range(), 1024, [&](IndexRange range) {
for (MDeformVert &dvert : dverts.slice(range)) {
MDeformWeight *weight = BKE_defvert_find_index(&dvert, index);
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?
BKE_defvert_remove_group(&dvert, weight);
for (MDeformWeight &weight : MutableSpan(dvert.dw, dvert.totweight)) {
if (weight.def_nr > index) {
weight.def_nr--;
}
}
}
});
return true;
}
bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final
{
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 +647,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 +666,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

@ -10,7 +10,7 @@
#include "DNA_object_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_deform.h"
#include "BKE_deform.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
@ -951,10 +951,20 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
}
};
static float get_crease(const float &crease)
{
return crease;
}
static void set_crease(float &crease, const float value)
{
crease = std::clamp(value, 0.0f, 1.0f);
}
/**
* 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
@ -1191,6 +1201,18 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
edge_access,
nullptr);
static BuiltinCustomDataLayerProvider crease(
"crease",
ATTR_DOMAIN_EDGE,
CD_PROP_FLOAT,
CD_CREASE,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
edge_access,
make_array_read_attribute<float>,
make_derived_write_attribute<float, float, get_crease, set_crease>,
nullptr);
static VertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);