CurvesGeometry: Add initial vertex group support #106944
@ -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,
|
||||
|
119
source/blender/blenkernel/BKE_deform.hh
Normal file
119
source/blender/blenkernel/BKE_deform.hh
Normal 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
|
||||
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
|
@ -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
|
||||
|
@ -516,7 +516,7 @@ int BKE_id_defgroup_name_index(const ID *id, const char *name)
|
||||
return index;
|
||||
}
|
||||
|
||||
bool BKE_id_defgroup_name_find(const ID *id,
|
||||
bool BKE_defgroup_listbase_name_find(const ListBase *defbase,
|
||||
const char *name,
|
||||
int *r_index,
|
||||
bDeformGroup **r_group)
|
||||
@ -524,7 +524,6 @@ bool BKE_id_defgroup_name_find(const ID *id,
|
||||
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));
|
||||
|
@ -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
Hans Goudey
commented
It would be nice to not duplicate this code between meshes and curves too, could that be shared in 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});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user
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 fromVMutableArrayImpl
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.