Geometry Nodes: deterministic anonymous attribute lifetimes

Previously, the lifetimes of anonymous attributes were determined by
reference counts which were non-deterministic when multiple threads
are used. Now the lifetimes of anonymous attributes are handled
more explicitly and deterministically. This is a prerequisite for any kind
of caching, because caching the output of nodes that do things
non-deterministically and have "invisible inputs" (reference counts)
doesn't really work.

For more details for how deterministic lifetimes are achieved, see D16858.

No functional changes are expected. Small performance changes are expected
as well (within few percent, anything larger regressions should be reported as
bugs).

Differential Revision: https://developer.blender.org/D16858
This commit is contained in:
Jacques Lucke 2023-01-05 14:05:30 +01:00
parent 4813c37ae2
commit 2ffd08e952
Notes: blender-bot 2023-05-31 15:21:39 +02:00
Referenced by commit c006ba83e0, Fix: execution graph for geometry nodes contained cycles leading to crash
Referenced by issue #104223, Regression: Geometry Node: Radius input don't propageted on input curve for Set Curve Radius node
Referenced by issue #103747, Regression: Crash when using Rotation Output of Curve To Points node (node_geo_curve_to_points.cc)
Referenced by issue #103756, Regression: Geometry Node: Anonymous attribute tooltip display wrong dependencies names
Referenced by issue #106497, Geometry Nodes: Freeze Node
Referenced by issue #108456, Regressino: Geometry Node: Edge Path to Curves does not work with anonymous attributes
Referenced by pull request #108460, Fix #108456: Missed declaration function in Shortest Edge Paths node
Referenced by commit 1706ce032f, Fix #108456: Missed declaration function in Shortest Edge Paths node
90 changed files with 3003 additions and 822 deletions

View File

@ -1,29 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*
* An #AnonymousAttributeID is used to identify attributes that are not explicitly named.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AnonymousAttributeID AnonymousAttributeID;
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name);
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name);
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id);
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id);
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id);
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id);
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id);
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id);
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id);
#ifdef __cplusplus
}
#endif

View File

@ -1,155 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <atomic>
#include <string>
#include "BLI_hash.hh"
#include "BLI_string_ref.hh"
#include "BKE_anonymous_attribute.h"
namespace blender::bke {
/**
* Wrapper for #AnonymousAttributeID with RAII semantics.
* This class should typically not be used directly. Instead use #StrongAnonymousAttributeID or
* #WeakAnonymousAttributeID.
*/
template<bool IsStrongReference> class OwnedAnonymousAttributeID {
private:
const AnonymousAttributeID *data_ = nullptr;
template<bool OtherIsStrongReference> friend class OwnedAnonymousAttributeID;
public:
OwnedAnonymousAttributeID() = default;
/** Create a new anonymous attribute id. */
explicit OwnedAnonymousAttributeID(StringRefNull debug_name)
{
if constexpr (IsStrongReference) {
data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str());
}
else {
data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str());
}
}
/**
* This transfers ownership, so no incref is necessary.
* The caller has to make sure that it owned the anonymous id.
*/
explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id)
: data_(anonymous_id)
{
}
template<bool OtherIsStrong>
OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
{
data_ = other.data_;
this->incref();
}
template<bool OtherIsStrong>
OwnedAnonymousAttributeID(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
{
data_ = other.data_;
this->incref();
other.decref();
other.data_ = nullptr;
}
~OwnedAnonymousAttributeID()
{
this->decref();
}
template<bool OtherIsStrong>
OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID<OtherIsStrong> &other)
{
if (this == &other) {
return *this;
}
this->~OwnedAnonymousAttributeID();
new (this) OwnedAnonymousAttributeID(other);
return *this;
}
template<bool OtherIsStrong>
OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID<OtherIsStrong> &&other)
{
if (this == &other) {
return *this;
}
this->~OwnedAnonymousAttributeID();
new (this) OwnedAnonymousAttributeID(std::move(other));
return *this;
}
operator bool() const
{
return data_ != nullptr;
}
StringRefNull debug_name() const
{
BLI_assert(data_ != nullptr);
return BKE_anonymous_attribute_id_debug_name(data_);
}
bool has_strong_references() const
{
BLI_assert(data_ != nullptr);
return BKE_anonymous_attribute_id_has_strong_references(data_);
}
/** Extract the ownership of the currently wrapped anonymous id. */
const AnonymousAttributeID *extract()
{
const AnonymousAttributeID *extracted_data = data_;
/* Don't decref because the caller becomes the new owner. */
data_ = nullptr;
return extracted_data;
}
/** Get the wrapped anonymous id, without taking ownership. */
const AnonymousAttributeID *get() const
{
return data_;
}
private:
void incref()
{
if (data_ == nullptr) {
return;
}
if constexpr (IsStrongReference) {
BKE_anonymous_attribute_id_increment_strong(data_);
}
else {
BKE_anonymous_attribute_id_increment_weak(data_);
}
}
void decref()
{
if (data_ == nullptr) {
return;
}
if constexpr (IsStrongReference) {
BKE_anonymous_attribute_id_decrement_strong(data_);
}
else {
BKE_anonymous_attribute_id_decrement_weak(data_);
}
}
};
using StrongAnonymousAttributeID = OwnedAnonymousAttributeID<true>;
using WeakAnonymousAttributeID = OwnedAnonymousAttributeID<false>;
} // namespace blender::bke

View File

@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <atomic>
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
#include "BLI_user_counter.hh"
namespace blender::bke {
/**
* An #AnonymousAttributeID contains information about a specific anonymous attribute.
* Like normal attributes, anonymous attributes are also identified by their name, so one should
* not have to compare #AnonymousAttributeID pointers.
*
* Anonymous attributes don't need additional information besides their name, with a few
* exceptions:
* - The name of anonymous attributes is generated automatically, so it is generally not human
* readable (just random characters). #AnonymousAttributeID can provide more context as where a
* specific anonymous attribute was created which can simplify debugging.
* - [Not yet supported.] When anonymous attributes are contained in on-disk caches, we have to map
* those back to anonymous attributes at run-time. The issue is that (for various reasons) we
* might change how anonymous attribute names are generated in the future, which would lead to a
* mis-match between stored and new attribute names. To work around it, we should cache
* additional information for anonymous attributes on disk (like which node created it). This
* information can then be used to map stored attributes to their run-time counterpart.
*
* Once created, #AnonymousAttributeID is immutable. Also it is intrinsicly reference counted so
* that it can have shared ownership. `std::shared_ptr` can't be used for that purpose here,
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
* should be used to avoid manual reference counting in C++ code.
*/
class AnonymousAttributeID {
private:
mutable std::atomic<int> users_ = 1;
protected:
std::string name_;
public:
virtual ~AnonymousAttributeID() = default;
StringRefNull name() const
{
return name_;
}
void user_add() const
{
users_.fetch_add(1);
}
void user_remove() const
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
MEM_delete(this);
}
}
};
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
using AutoAnonymousAttributeID = UserCounter<const AnonymousAttributeID>;
/**
* A set of anonymous attribute names that is passed around in geometry nodes.
*/
class AnonymousAttributeSet {
public:
/**
* This uses `std::shared_ptr` because attributes sets are passed around by value during geometry
* nodes evaluation, and this makes it very small if there is no name. Also it makes copying very
* cheap.
*/
std::shared_ptr<Set<std::string>> names;
};
/**
* Can be passed to algorithms which propagate attributes. It can tell the algorithm which
* anonymous attributes should be propagated and can be skipped.
*/
class AnonymousAttributePropagationInfo {
public:
/**
* This uses `std::shared_ptr` because it's usually initialized from an #AnonymousAttributeSet
* and then the set doesn't have to be copied.
*/
std::shared_ptr<Set<std::string>> names;
/**
* Propagate all anonymous attributes even if the set above is empty.
*/
bool propagate_all = true;
/**
* Return true when the anonymous attribute should be propagated and false otherwise.
*/
bool propagate(const AnonymousAttributeID &anonymous_id) const;
};
} // namespace blender::bke

View File

@ -11,7 +11,7 @@
#include "BLI_math_vec_types.hh"
#include "BLI_set.hh"
#include "BKE_anonymous_attribute.hh"
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_attribute.h"
struct Mesh;
@ -24,7 +24,7 @@ class GField;
namespace blender::bke {
/**
* Identifies an attribute that is either named or anonymous.
* Identifies an attribute with optional anonymous attribute information.
* It does not own the identifier, so it is just a reference.
*/
class AttributeIDRef {
@ -38,15 +38,14 @@ class AttributeIDRef {
AttributeIDRef(StringRefNull name);
AttributeIDRef(const char *name);
AttributeIDRef(const std::string &name);
AttributeIDRef(const AnonymousAttributeID &anonymous_id);
AttributeIDRef(const AnonymousAttributeID *anonymous_id);
operator bool() const;
uint64_t hash() const;
bool is_named() const;
bool is_anonymous() const;
StringRef name() const;
const AnonymousAttributeID &anonymous_id() const;
bool should_be_kept() const;
friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b);
friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id);
@ -749,6 +748,7 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes,
eAttrDomainMask domain_mask,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip = {});
/**
@ -762,6 +762,7 @@ void copy_attribute_domain(AttributeAccessor src_attributes,
MutableAttributeAccessor dst_attributes,
IndexMask selection,
eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip = {});
bool allow_procedural_attribute_access(StringRef attribute_name);
@ -852,29 +853,31 @@ inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name)
}
/* The anonymous id is only borrowed, the caller has to keep a reference to it. */
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
: anonymous_id_(anonymous_id)
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID &anonymous_id)
: AttributeIDRef(anonymous_id.name())
{
anonymous_id_ = &anonymous_id;
}
inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id)
: AttributeIDRef(anonymous_id ? anonymous_id->name() : "")
{
anonymous_id_ = anonymous_id;
}
inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b)
{
return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_;
return a.name_ == b.name_;
}
inline AttributeIDRef::operator bool() const
{
return this->is_named() || this->is_anonymous();
return !name_.is_empty();
}
inline uint64_t AttributeIDRef::hash() const
{
return get_default_hash_2(name_, anonymous_id_);
}
inline bool AttributeIDRef::is_named() const
{
return !name_.is_empty();
return get_default_hash(name_);
}
inline bool AttributeIDRef::is_anonymous() const
@ -884,7 +887,6 @@ inline bool AttributeIDRef::is_anonymous() const
inline StringRef AttributeIDRef::name() const
{
BLI_assert(this->is_named());
return name_;
}
@ -894,14 +896,4 @@ inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const
return *anonymous_id_;
}
/**
* \return True if the attribute should not be removed automatically as an optimization during
* processing or copying. Anonymous attributes can be removed when they no longer have any
* references.
*/
inline bool AttributeIDRef::should_be_kept() const
{
return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_);
}
} // namespace blender::bke

View File

@ -11,6 +11,8 @@ struct Mesh;
namespace blender::bke {
class AnonymousAttributePropagationInfo;
/**
* Extrude all splines in the profile curve along the path of every spline in the curve input.
* Transfer curve attributes to the mesh.
@ -23,11 +25,13 @@ namespace blender::bke {
*/
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
const CurvesGeometry &profile,
bool fill_caps);
bool fill_caps,
const AnonymousAttributePropagationInfo &propagation_info);
/**
* Create a loose-edge mesh based on the evaluated path of the curve's splines.
* Transfer curve attributes to the mesh.
*/
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve);
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
const AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::bke

View File

@ -404,8 +404,10 @@ class CurvesGeometry : public ::CurvesGeometry {
void calculate_bezier_auto_handles();
void remove_points(IndexMask points_to_delete);
void remove_curves(IndexMask curves_to_delete);
void remove_points(IndexMask points_to_delete,
const AnonymousAttributePropagationInfo &propagation_info = {});
void remove_curves(IndexMask curves_to_delete,
const AnonymousAttributePropagationInfo &propagation_info = {});
/**
* Change the direction of selected curves (switch the start and end) without changing their

View File

@ -23,7 +23,6 @@
extern "C" {
#endif
struct AnonymousAttributeID;
struct BMesh;
struct BlendDataReader;
struct BlendWriter;
@ -227,7 +226,7 @@ void *CustomData_add_layer_anonymous(struct CustomData *data,
eCDAllocType alloctype,
void *layer,
int totelem,
const struct AnonymousAttributeID *anonymous_id);
const AnonymousAttributeIDHandle *anonymous_id);
/**
* Frees the active or first data layer with the give type.
@ -275,8 +274,6 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data,
int type,
const char *name,
int totelem);
void *CustomData_duplicate_referenced_layer_anonymous(
CustomData *data, int type, const struct AnonymousAttributeID *anonymous_id, int totelem);
bool CustomData_is_referenced_layer(struct CustomData *data, int type);
/**

View File

@ -261,18 +261,14 @@ class NormalFieldInput : public GeometryFieldInput {
class AnonymousAttributeFieldInput : public GeometryFieldInput {
private:
/**
* A strong reference is required to make sure that the referenced attribute is not removed
* automatically.
*/
StrongAnonymousAttributeID anonymous_id_;
AutoAnonymousAttributeID anonymous_id_;
std::string producer_name_;
public:
AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id,
AnonymousAttributeFieldInput(AutoAnonymousAttributeID anonymous_id,
const CPPType &type,
std::string producer_name)
: GeometryFieldInput(type, anonymous_id.debug_name()),
: GeometryFieldInput(type, anonymous_id->name()),
anonymous_id_(std::move(anonymous_id)),
producer_name_(producer_name)
{
@ -280,7 +276,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
}
template<typename T>
static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id, std::string producer_name)
static fn::Field<T> Create(AutoAnonymousAttributeID anonymous_id, std::string producer_name)
{
const CPPType &type = CPPType::get<T>();
auto field_input = std::make_shared<AnonymousAttributeFieldInput>(
@ -288,6 +284,11 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
return fn::Field<T>{field_input};
}
const AutoAnonymousAttributeID &anonymous_id() const
{
return anonymous_id_;
}
GVArray get_varray_for_context(const GeometryFieldContext &context,
IndexMask mask) const override;

View File

@ -19,7 +19,7 @@
#include "BLI_user_counter.hh"
#include "BLI_vector_set.hh"
#include "BKE_anonymous_attribute.hh"
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_attribute.hh"
#include "BKE_geometry_set.h"
@ -213,6 +213,7 @@ struct GeometrySet {
blender::Span<GeometryComponentType> component_types,
GeometryComponentType dst_component_type,
bool include_instances,
const blender::bke::AnonymousAttributePropagationInfo &propagation_info,
blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const;
blender::Vector<GeometryComponentType> gather_component_types(bool include_instances,

View File

@ -155,7 +155,8 @@ class Instances {
* Remove the indices that are not contained in the mask input, and remove unused instance
* references afterwards.
*/
void remove(const blender::IndexMask mask);
void remove(const blender::IndexMask mask,
const blender::bke::AnonymousAttributePropagationInfo &propagation_info);
/**
* Get an id for every instance. These can be used for e.g. motion blur.
*/

View File

@ -7,6 +7,7 @@
#include "BLI_cache_mutex.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_resource_scope.hh"
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
@ -24,6 +25,10 @@ namespace blender::nodes {
struct FieldInferencingInterface;
class NodeDeclaration;
struct GeometryNodesLazyFunctionGraphInfo;
namespace anonymous_attribute_lifetime {
struct RelationsInNode;
}
namespace aal = anonymous_attribute_lifetime;
} // namespace blender::nodes
namespace blender {
@ -106,6 +111,8 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
/** Information about how inputs and outputs of the node group interact with fields. */
std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface;
/** Information about usage of anonymous attributes within the group. */
std::unique_ptr<nodes::aal::RelationsInNode> anonymous_attribute_relations;
/**
* For geometry nodes, a lazy function graph with some additional info is cached. This is used to
@ -330,7 +337,11 @@ inline bool topology_cache_is_available(const bNodeSocket &socket)
namespace node_field_inferencing {
bool update_field_inferencing(const bNodeTree &tree);
}
namespace anonymous_attribute_inferencing {
Array<const nodes::aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
ResourceScope &scope);
bool update_anonymous_attribute_relations(bNodeTree &tree);
} // namespace anonymous_attribute_inferencing
} // namespace blender::bke
/* -------------------------------------------------------------------- */

View File

@ -64,7 +64,7 @@ set(SRC
intern/anim_path.c
intern/anim_sys.c
intern/anim_visualization.c
intern/anonymous_attribute.cc
intern/anonymous_attribute_id.cc
intern/appdir.c
intern/armature.c
intern/armature_deform.c
@ -229,6 +229,7 @@ set(SRC
intern/nla.c
intern/node.cc
intern/node_runtime.cc
intern/node_tree_anonymous_attributes.cc
intern/node_tree_field_inferencing.cc
intern/node_tree_update.cc
intern/object.cc
@ -315,8 +316,7 @@ set(SRC
BKE_anim_path.h
BKE_anim_visualization.h
BKE_animsys.h
BKE_anonymous_attribute.h
BKE_anonymous_attribute.hh
BKE_anonymous_attribute_id.hh
BKE_appdir.h
BKE_armature.h
BKE_armature.hh

View File

@ -1,105 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_anonymous_attribute.hh"
using namespace blender::bke;
/**
* A struct that identifies an attribute. It's lifetime is managed by an atomic reference count.
*
* Additionally, this struct can be strongly or weakly owned. The difference is that strong
* ownership means that attributes with this id will be kept around. Weak ownership just makes sure
* that the struct itself stays alive, but corresponding attributes might still be removed
* automatically.
*/
struct AnonymousAttributeID {
/**
* Total number of references to this attribute id. Once this reaches zero, the struct can be
* freed. This includes strong and weak references.
*/
mutable std::atomic<int> refcount_tot = 0;
/**
* Number of strong references to this attribute id. When this is zero, the corresponding
* attributes can be removed from geometries automatically.
*/
mutable std::atomic<int> refcount_strong = 0;
/**
* Only used to identify this struct in a debugging session.
*/
std::string debug_name;
/**
* Unique name of the this attribute id during the current session.
*/
std::string internal_name;
};
/** Every time this function is called, it outputs a different name. */
static std::string get_new_internal_name()
{
static std::atomic<int> index = 0;
const int next_index = index.fetch_add(1);
return ".a_" + std::to_string(next_index);
}
AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name)
{
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
anonymous_id->debug_name = debug_name;
anonymous_id->internal_name = get_new_internal_name();
anonymous_id->refcount_tot.store(1);
return anonymous_id;
}
AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name)
{
AnonymousAttributeID *anonymous_id = new AnonymousAttributeID();
anonymous_id->debug_name = debug_name;
anonymous_id->internal_name = get_new_internal_name();
anonymous_id->refcount_tot.store(1);
anonymous_id->refcount_strong.store(1);
return anonymous_id;
}
bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id)
{
return anonymous_id->refcount_strong.load() >= 1;
}
void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id)
{
anonymous_id->refcount_tot.fetch_add(1);
}
void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id)
{
anonymous_id->refcount_tot.fetch_add(1);
anonymous_id->refcount_strong.fetch_add(1);
}
void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id)
{
const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1;
if (new_refcount == 0) {
BLI_assert(anonymous_id->refcount_strong == 0);
delete anonymous_id;
}
}
void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id)
{
anonymous_id->refcount_strong.fetch_sub(1);
BKE_anonymous_attribute_id_decrement_weak(anonymous_id);
}
const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id)
{
return anonymous_id->debug_name.c_str();
}
const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id)
{
return anonymous_id->internal_name.c_str();
}

View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_anonymous_attribute_id.hh"
namespace blender::bke {
bool AnonymousAttributePropagationInfo::propagate(const AnonymousAttributeID &anonymous_id) const
{
if (this->propagate_all) {
return true;
}
if (!this->names) {
return false;
}
return this->names->contains_as(anonymous_id.name());
}
} // namespace blender::bke

View File

@ -40,13 +40,9 @@ namespace blender::bke {
std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
if (attribute_id) {
stream << attribute_id.name();
}
else if (attribute_id.is_anonymous()) {
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">";
}
else {
stream << "<none>";
}
@ -153,7 +149,7 @@ eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains)
static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer)
{
if (layer.anonymous_id != nullptr) {
return layer.anonymous_id;
return *layer.anonymous_id;
}
return layer.name;
}
@ -207,7 +203,7 @@ static void *add_generic_custom_data_layer(CustomData &custom_data,
const int domain_num,
const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
char attribute_name_c[MAX_NAME];
attribute_id.name().copy(attribute_name_c);
return CustomData_add_layer_named(
@ -261,9 +257,6 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
if (!attribute_id) {
return false;
}
if (attribute_id.is_anonymous()) {
return layer.anonymous_id == &attribute_id.anonymous_id();
}
return layer.name == attribute_id.name();
}
@ -451,14 +444,8 @@ GAttributeWriter CustomDataAttributeProvider::try_get_for_write(
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
continue;
}
if (attribute_id.is_named()) {
CustomData_duplicate_referenced_layer_named(
custom_data, layer.type, layer.name, element_num);
}
else {
CustomData_duplicate_referenced_layer_anonymous(
custom_data, layer.type, &attribute_id.anonymous_id(), element_num);
}
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, element_num);
const CPPType *type = custom_data_type_to_cpp_type((eCustomDataType)layer.type);
if (type == nullptr) {
continue;
@ -854,7 +841,7 @@ void MutableAttributeAccessor::remove_anonymous()
}
while (!anonymous_ids.is_empty()) {
this->remove(anonymous_ids.pop_last());
this->remove(*anonymous_ids.pop_last());
}
}
@ -883,12 +870,7 @@ GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef
#ifdef DEBUG
if (attribute) {
auto checker = std::make_shared<FinishCallChecker>();
if (attribute_id.is_named()) {
checker->name = attribute_id.name();
}
else {
checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id());
}
checker->name = attribute_id.name();
checker->real_finish_fn = attribute.tag_modified_fn;
attribute.tag_modified_fn = [checker]() {
if (checker->real_finish_fn) {
@ -968,6 +950,7 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes,
const eAttrDomainMask domain_mask,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip)
{
Vector<AttributeTransferData> attributes;
@ -976,10 +959,10 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) {
return true;
}
if (id.is_named() && skip.contains(id.name())) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return true;
}
if (!id.should_be_kept()) {
if (skip.contains(id.name())) {
return true;
}
@ -999,6 +982,7 @@ void copy_attribute_domain(const AttributeAccessor src_attributes,
MutableAttributeAccessor dst_attributes,
const IndexMask selection,
const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info,
const Set<std::string> &skip)
{
src_attributes.for_all(
@ -1006,10 +990,10 @@ void copy_attribute_domain(const AttributeAccessor src_attributes,
if (meta_data.domain != domain) {
return true;
}
if (id.is_named() && skip.contains(id.name())) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return true;
}
if (!id.should_be_kept()) {
if (skip.contains(id.name())) {
return true;
}

View File

@ -336,7 +336,7 @@ namespace attribute_accessor_functions {
template<const ComponentAttributeProviders &providers>
inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_id)
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return false;
}
const StringRef name = attribute_id.name();
@ -346,7 +346,7 @@ inline bool is_builtin(const void * /*owner*/, const AttributeIDRef &attribute_i
template<const ComponentAttributeProviders &providers>
inline GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
@ -396,7 +396,7 @@ template<const ComponentAttributeProviders &providers>
inline AttributeValidator lookup_validator(const void * /*owner*/,
const blender::bke::AttributeIDRef &attribute_id)
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return {};
}
const BuiltinAttributeProvider *provider =
@ -443,7 +443,7 @@ inline std::optional<AttributeMetaData> lookup_meta_data(const void *owner,
template<const ComponentAttributeProviders &providers>
inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
@ -462,7 +462,7 @@ inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attr
template<const ComponentAttributeProviders &providers>
inline bool remove(void *owner, const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {
@ -487,7 +487,7 @@ inline bool add(void *owner,
if (contains<providers>(owner, attribute_id)) {
return false;
}
if (attribute_id.is_named()) {
if (!attribute_id.is_anonymous()) {
const StringRef name = attribute_id.name();
if (const BuiltinAttributeProvider *provider =
providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) {

View File

@ -28,6 +28,8 @@ BLI_CPP_TYPE_MAKE(Material *, CPPTypeFlags::BasicType)
BLI_CPP_TYPE_MAKE(MStringProperty, CPPTypeFlags::None);
BLI_CPP_TYPE_MAKE(blender::bke::AnonymousAttributeSet, CPPTypeFlags::None);
void BKE_cpp_types_init()
{
blender::register_cpp_types();
@ -45,4 +47,6 @@ void BKE_cpp_types_init()
BLI_CPP_TYPE_REGISTER(Material *);
BLI_CPP_TYPE_REGISTER(MStringProperty);
BLI_CPP_TYPE_REGISTER(blender::bke::AnonymousAttributeSet);
}

View File

@ -331,18 +331,19 @@ static eAttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_a
static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes,
const AttributeAccessor &mesh_attributes,
const AttributeIDRef &id,
const AttributeMetaData &meta_data)
const AttributeMetaData &meta_data,
const AnonymousAttributePropagationInfo &propagation_info)
{
/* The position attribute has special non-generic evaluation. */
if (id.is_named() && id.name() == "position") {
if (id.name() == "position") {
return false;
}
/* Don't propagate built-in curves attributes that are not built-in on meshes. */
if (curve_attributes.is_builtin(id) && !mesh_attributes.is_builtin(id)) {
return false;
}
if (!id.should_be_kept()) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return false;
}
if (meta_data.data_type == CD_PROP_STRING) {
@ -629,7 +630,8 @@ static void copy_curve_domain_attribute_to_mesh(const ResultOffsets &mesh_offset
Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
const CurvesGeometry &profile,
const bool fill_caps)
const bool fill_caps,
const AnonymousAttributePropagationInfo &propagation_info)
{
const CurvesInfo curves_info = get_curves_info(main, profile);
@ -716,7 +718,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
MutableAttributeAccessor mesh_attributes = mesh->attributes_for_write();
main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id, meta_data)) {
if (!should_add_attribute_to_mesh(
main_attributes, mesh_attributes, id, meta_data, propagation_info)) {
return true;
}
main_attributes_set.add_new(id);
@ -753,7 +756,8 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
if (main_attributes.contains(id)) {
return true;
}
if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id, meta_data)) {
if (!should_add_attribute_to_mesh(
profile_attributes, mesh_attributes, id, meta_data, propagation_info)) {
return true;
}
const eAttrDomain src_domain = meta_data.domain;
@ -797,10 +801,11 @@ static CurvesGeometry get_curve_single_vert()
return curves;
}
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve)
Mesh *curve_to_wire_mesh(const CurvesGeometry &curve,
const AnonymousAttributePropagationInfo &propagation_info)
{
static const CurvesGeometry vert_curve = get_curve_single_vert();
return curve_to_mesh_sweep(curve, vert_curve, false);
return curve_to_mesh_sweep(curve, vert_curve, false, propagation_info);
}
} // namespace blender::bke

View File

@ -1047,8 +1047,10 @@ static Array<int> build_point_to_curve_map(const CurvesGeometry &curves)
return point_to_curve_map;
}
static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
const IndexMask points_to_delete)
static CurvesGeometry copy_with_removed_points(
const CurvesGeometry &curves,
const IndexMask points_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
/* Use a map from points to curves to facilitate using an #IndexMask input. */
const Array<int> point_to_curve_map = build_point_to_curve_map(curves);
@ -1093,9 +1095,15 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
CurvesGeometry new_curves{new_point_count, new_curve_count};
Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT);
curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_POINT,
propagation_info);
Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE);
curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_CURVE,
propagation_info);
threading::parallel_invoke(
256 < new_point_count * new_curve_count,
@ -1144,7 +1152,8 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
return new_curves;
}
void CurvesGeometry::remove_points(const IndexMask points_to_delete)
void CurvesGeometry::remove_points(const IndexMask points_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (points_to_delete.is_empty()) {
return;
@ -1152,11 +1161,13 @@ void CurvesGeometry::remove_points(const IndexMask points_to_delete)
if (points_to_delete.size() == this->points_num()) {
*this = {};
}
*this = copy_with_removed_points(*this, points_to_delete);
*this = copy_with_removed_points(*this, points_to_delete, propagation_info);
}
static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
const IndexMask curves_to_delete)
static CurvesGeometry copy_with_removed_curves(
const CurvesGeometry &curves,
const IndexMask curves_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Span<int> old_offsets = curves.offsets();
const Vector<IndexRange> old_curve_ranges = curves_to_delete.extract_ranges_invert(
@ -1178,9 +1189,15 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
CurvesGeometry new_curves{new_tot_points, new_tot_curves};
Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT);
curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_POINT,
propagation_info);
Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer(
curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE);
curves.attributes(),
new_curves.attributes_for_write(),
ATTR_DOMAIN_MASK_CURVE,
propagation_info);
threading::parallel_invoke(
256 < new_tot_points * new_tot_curves,
@ -1251,7 +1268,8 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
return new_curves;
}
void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
void CurvesGeometry::remove_curves(const IndexMask curves_to_delete,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (curves_to_delete.is_empty()) {
return;
@ -1260,7 +1278,7 @@ void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
*this = {};
return;
}
*this = copy_with_removed_curves(*this, curves_to_delete);
*this = copy_with_removed_curves(*this, curves_to_delete, propagation_info);
}
template<typename T>
@ -1315,7 +1333,7 @@ void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
if (meta_data.data_type == CD_PROP_STRING) {
return true;
}
if (id.is_named() && bezier_handle_names.contains(id.name())) {
if (bezier_handle_names.contains(id.name())) {
return true;
}

View File

@ -39,7 +39,7 @@
#include "BLT_translation.h"
#include "BKE_anonymous_attribute.h"
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_customdata.h"
#include "BKE_customdata_file.h"
#include "BKE_deform.h"
@ -2356,7 +2356,7 @@ bool CustomData_merge(const CustomData *source,
layer->anonymous_id = nullptr;
}
else {
BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id);
layer->anonymous_id->user_add();
}
}
if (alloctype == CD_ASSIGN) {
@ -2460,7 +2460,7 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to
const LayerTypeInfo *typeInfo;
if (layer->anonymous_id != nullptr) {
BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id);
layer->anonymous_id->user_remove();
layer->anonymous_id = nullptr;
}
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
@ -2957,9 +2957,9 @@ void *CustomData_add_layer_anonymous(CustomData *data,
const eCDAllocType alloctype,
void *layerdata,
const int totelem,
const AnonymousAttributeID *anonymous_id)
const AnonymousAttributeIDHandle *anonymous_id)
{
const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id);
const char *name = anonymous_id->name().c_str();
CustomDataLayer *layer = customData_add_layer__internal(
data, type, alloctype, layerdata, totelem, name);
CustomData_update_typemap(data);
@ -2968,7 +2968,7 @@ void *CustomData_add_layer_anonymous(CustomData *data,
return nullptr;
}
BKE_anonymous_attribute_id_increment_weak(anonymous_id);
anonymous_id->user_add();
layer->anonymous_id = anonymous_id;
return layer->data;
}
@ -3147,20 +3147,6 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data,
return customData_duplicate_referenced_layer_index(data, layer_index, totelem);
}
void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data,
const int /*type*/,
const AnonymousAttributeID *anonymous_id,
const int totelem)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].anonymous_id == anonymous_id) {
return customData_duplicate_referenced_layer_index(data, i, totelem);
}
}
BLI_assert_unreachable();
return nullptr;
}
void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem)
{
for (int i = 0; i < data->totlayer; i++) {

View File

@ -1055,7 +1055,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
GAttributeReader try_get_for_read(const void *owner,
const AttributeIDRef &attribute_id) const final
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return {};
}
const Mesh *mesh = static_cast<const Mesh *>(owner);
@ -1079,7 +1079,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return {};
}
Mesh *mesh = static_cast<Mesh *>(owner);
@ -1100,7 +1100,7 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return false;
}
Mesh *mesh = static_cast<Mesh *>(owner);

View File

@ -332,7 +332,7 @@ GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryField
const IndexMask /*mask*/) const
{
const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_);
return context.attributes()->lookup(anonymous_id_.get(), context.domain(), data_type);
return context.attributes()->lookup(*anonymous_id_, context.domain(), data_type);
}
std::string AnonymousAttributeFieldInput::socket_inspection_name() const
@ -363,8 +363,7 @@ std::optional<eAttrDomain> AnonymousAttributeFieldInput::preferred_domain(
if (!attributes.has_value()) {
return std::nullopt;
}
const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(
anonymous_id_.get());
const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(*anonymous_id_);
if (!meta_data.has_value()) {
return std::nullopt;
}

View File

@ -581,6 +581,7 @@ void GeometrySet::gather_attributes_for_propagation(
const Span<GeometryComponentType> component_types,
const GeometryComponentType dst_component_type,
bool include_instances,
const blender::bke::AnonymousAttributePropagationInfo &propagation_info,
blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const
{
using namespace blender;
@ -605,7 +606,8 @@ void GeometrySet::gather_attributes_for_propagation(
/* Propagating string attributes is not supported yet. */
return;
}
if (!attribute_id.should_be_kept()) {
if (attribute_id.is_anonymous() &&
!propagation_info.propagate(attribute_id.anonymous_id())) {
return;
}

View File

@ -105,7 +105,8 @@ blender::Span<InstanceReference> Instances::references() const
return references_;
}
void Instances::remove(const IndexMask mask)
void Instances::remove(const IndexMask mask,
const AnonymousAttributePropagationInfo &propagation_info)
{
using namespace blender;
if (mask.is_range() && mask.as_range().start() == 0) {
@ -132,7 +133,7 @@ void Instances::remove(const IndexMask mask)
src_attributes.foreach_attribute(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) {
if (!id.should_be_kept()) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return true;
}

View File

@ -839,7 +839,9 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o
return BKE_mesh_copy_for_eval(mesh, false);
}
if (const Curves *curves = get_evaluated_curves_from_object(evaluated_object)) {
return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry));
const blender::bke::AnonymousAttributePropagationInfo propagation_info;
return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry),
propagation_info);
}
return nullptr;
}

View File

@ -207,6 +207,11 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons
dst_runtime.field_inferencing_interface = std::make_unique<FieldInferencingInterface>(
*ntree_src->runtime->field_inferencing_interface);
}
if (ntree_src->runtime->anonymous_attribute_relations) {
dst_runtime.anonymous_attribute_relations =
std::make_unique<blender::nodes::anonymous_attribute_lifetime::RelationsInNode>(
*ntree_src->runtime->anonymous_attribute_relations);
}
if (flag & LIB_ID_COPY_NO_PREVIEW) {
ntree_dst->preview = nullptr;

View File

@ -0,0 +1,452 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "NOD_node_declaration.hh"
#include "BKE_node_runtime.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_resource_scope.hh"
#include "BLI_set.hh"
#include "BLI_stack.hh"
#include "BLI_timeit.hh"
namespace blender::bke::anonymous_attribute_inferencing {
namespace aal = nodes::aal;
using nodes::NodeDeclaration;
static const aal::RelationsInNode &get_relations_in_node(const bNode &node, ResourceScope &scope)
{
if (node.is_group()) {
if (const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node.id)) {
BLI_assert(group->runtime->anonymous_attribute_relations);
return *group->runtime->anonymous_attribute_relations;
}
}
if (const NodeDeclaration *node_decl = node.declaration()) {
if (const aal::RelationsInNode *relations = node_decl->anonymous_attribute_relations()) {
return *relations;
}
}
return scope.construct<aal::RelationsInNode>();
}
Array<const aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
ResourceScope &scope)
{
const Span<const bNode *> nodes = tree.all_nodes();
Array<const aal::RelationsInNode *> relations_by_node(nodes.size());
for (const int i : nodes.index_range()) {
relations_by_node[i] = &get_relations_in_node(*nodes[i], scope);
}
return relations_by_node;
}
static bool socket_is_field(const bNodeSocket &socket)
{
return socket.display_shape == SOCK_DISPLAY_SHAPE_DIAMOND;
}
/**
* Start at a group output socket and find all linked group inputs.
*/
static Vector<int> find_linked_group_inputs(
const bNodeTree &tree,
const bNodeSocket &group_output_socket,
const FunctionRef<Vector<int>(const bNodeSocket &)> get_linked_node_inputs)
{
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
Vector<int> input_indices;
found_sockets.add_new(&group_output_socket);
sockets_to_check.push(&group_output_socket);
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
for (const int input_index : get_linked_node_inputs(socket)) {
const bNodeSocket &input_socket = node.input_socket(input_index);
if (input_socket.is_available()) {
if (found_sockets.add(&input_socket)) {
sockets_to_check.push(&input_socket);
}
}
}
}
}
for (const bNode *node : tree.group_input_nodes()) {
for (const bNodeSocket *socket : node->output_sockets()) {
if (found_sockets.contains(socket)) {
input_indices.append_non_duplicates(socket->index());
}
}
}
return input_indices;
}
static void infer_propagate_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (group_output_socket->type != SOCK_GEOMETRY) {
continue;
}
const Vector<int> input_indices = find_linked_group_inputs(
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
Vector<int> indices;
for (const aal::PropagateRelation &relation :
relations_by_node[output_socket.owner_node().index()]->propagate_relations) {
if (relation.to_geometry_output == output_socket.index()) {
indices.append(relation.from_geometry_input);
}
}
return indices;
});
for (const int input_index : input_indices) {
aal::PropagateRelation relation;
relation.from_geometry_input = input_index;
relation.to_geometry_output = group_output_socket->index();
r_relations.propagate_relations.append(relation);
}
}
}
static void infer_reference_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (!socket_is_field(*group_output_socket)) {
continue;
}
const Vector<int> input_indices = find_linked_group_inputs(
tree, *group_output_socket, [&](const bNodeSocket &output_socket) {
Vector<int> indices;
for (const aal::ReferenceRelation &relation :
relations_by_node[output_socket.owner_node().index()]->reference_relations) {
if (relation.to_field_output == output_socket.index()) {
indices.append(relation.from_field_input);
}
}
return indices;
});
for (const int input_index : input_indices) {
if (tree.runtime->field_inferencing_interface->inputs[input_index] !=
nodes::InputSocketFieldType::None) {
aal::ReferenceRelation relation;
relation.from_field_input = input_index;
relation.to_field_output = group_output_socket->index();
r_relations.reference_relations.append(relation);
}
}
}
}
/**
* Find group output geometries that contain anonymous attributes referenced by the field.
* If `nullopt` is returned, the field does not depend on any anonymous attribute created in this
* node tree.
*/
static std::optional<Vector<int>> find_available_on_outputs(
const bNodeSocket &initial_group_output_socket,
const bNode &group_output_node,
const Span<const aal::RelationsInNode *> relations_by_node)
{
Set<const bNodeSocket *> geometry_sockets;
{
/* Find the nodes that added anonymous attributes to the field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
found_sockets.add_new(&initial_group_output_socket);
sockets_to_check.push(&initial_group_output_socket);
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::AvailableRelation &relation : relations.available_relations) {
if (socket.index() == relation.field_output) {
const bNodeSocket &geometry_output = node.output_socket(relation.geometry_output);
if (geometry_output.is_available()) {
geometry_sockets.add(&geometry_output);
}
}
}
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
if (socket.index() == relation.to_field_output) {
const bNodeSocket &field_input = node.input_socket(relation.from_field_input);
if (field_input.is_available()) {
if (found_sockets.add(&field_input)) {
sockets_to_check.push(&field_input);
}
}
}
}
}
}
}
if (geometry_sockets.is_empty()) {
/* The field does not depend on any anonymous attribute created within this node tree. */
return std::nullopt;
}
/* Find the group output geometries that contain the anonymous attribute referenced by the field
* output. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
for (const bNodeSocket *socket : geometry_sockets) {
found_sockets.add_new(socket);
sockets_to_check.push(socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
if (socket.index() == relation.from_geometry_input) {
const bNodeSocket &output_socket = node.output_socket(relation.to_geometry_output);
if (output_socket.is_available()) {
if (found_sockets.add(&output_socket)) {
sockets_to_check.push(&output_socket);
}
}
}
}
}
else {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &to_socket = *link->tosock;
if (found_sockets.add(&to_socket)) {
sockets_to_check.push(&to_socket);
}
}
}
}
}
Vector<int> output_indices;
for (const bNodeSocket *socket : group_output_node.input_sockets().drop_back(1)) {
if (found_sockets.contains(socket)) {
output_indices.append(socket->index());
}
}
return output_indices;
}
static void infer_available_relations(const Span<const aal::RelationsInNode *> relations_by_node,
const bNode &group_output_node,
aal::RelationsInNode &r_relations)
{
for (const bNodeSocket *group_output_socket : group_output_node.input_sockets().drop_back(1)) {
if (!socket_is_field(*group_output_socket)) {
continue;
}
const std::optional<Vector<int>> output_indices = find_available_on_outputs(
*group_output_socket, group_output_node, relations_by_node);
if (output_indices.has_value()) {
if (output_indices->is_empty()) {
r_relations.available_on_none.append(group_output_socket->index());
}
else {
for (const int output_index : *output_indices) {
aal::AvailableRelation relation;
relation.field_output = group_output_socket->index();
relation.geometry_output = output_index;
r_relations.available_relations.append(relation);
}
}
}
}
}
/**
* Returns a list of all the geometry inputs that the field input may be evaluated on.
*/
static Vector<int> find_eval_on_inputs(const bNodeTree &tree,
const int field_input_index,
const Span<const aal::RelationsInNode *> relations_by_node)
{
const Span<const bNode *> group_input_nodes = tree.group_input_nodes();
Set<const bNodeSocket *> geometry_sockets;
{
/* Find all the nodes that evaluate the input field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
for (const bNode *node : group_input_nodes) {
const bNodeSocket &socket = node->output_socket(field_input_index);
found_sockets.add_new(&socket);
sockets_to_check.push(&socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
const bNode &node = socket.owner_node();
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::EvalRelation &relation : relations.eval_relations) {
if (socket.index() == relation.field_input) {
const bNodeSocket &geometry_input = node.input_socket(relation.geometry_input);
if (geometry_input.is_available()) {
geometry_sockets.add(&geometry_input);
}
}
}
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
if (socket.index() == relation.from_field_input) {
const bNodeSocket &field_output = node.output_socket(relation.to_field_output);
if (field_output.is_available()) {
if (found_sockets.add(&field_output)) {
sockets_to_check.push(&field_output);
}
}
}
}
}
else {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &to_socket = *link->tosock;
if (found_sockets.add(&to_socket)) {
sockets_to_check.push(&to_socket);
}
}
}
}
}
}
if (geometry_sockets.is_empty()) {
return {};
}
/* Find the group input geometries whose attributes are propagated to the nodes that evaluate the
* field. */
Set<const bNodeSocket *> found_sockets;
Stack<const bNodeSocket *> sockets_to_check;
Vector<int> geometry_input_indices;
for (const bNodeSocket *socket : geometry_sockets) {
found_sockets.add_new(socket);
sockets_to_check.push(socket);
}
while (!sockets_to_check.is_empty()) {
const bNodeSocket &socket = *sockets_to_check.pop();
if (socket.is_input()) {
for (const bNodeLink *link : socket.directly_linked_links()) {
if (link->is_used()) {
const bNodeSocket &from_socket = *link->fromsock;
if (found_sockets.add(&from_socket)) {
sockets_to_check.push(&from_socket);
}
}
}
}
else {
const bNode &node = socket.owner_node();
if (node.is_group_input()) {
geometry_input_indices.append_non_duplicates(socket.index());
}
else {
const aal::RelationsInNode &relations = *relations_by_node[node.index()];
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
if (socket.index() == relation.to_geometry_output) {
const bNodeSocket &input_socket = node.input_socket(relation.from_geometry_input);
if (input_socket.is_available()) {
if (found_sockets.add(&input_socket)) {
sockets_to_check.push(&input_socket);
}
}
}
}
}
}
}
return geometry_input_indices;
}
static void infer_eval_relations(const bNodeTree &tree,
const Span<const aal::RelationsInNode *> relations_by_node,
aal::RelationsInNode &r_relations)
{
for (const int input_index : tree.interface_inputs().index_range()) {
if (tree.runtime->field_inferencing_interface->inputs[input_index] ==
nodes::InputSocketFieldType::None) {
continue;
}
const Vector<int> geometry_input_indices = find_eval_on_inputs(
tree, input_index, relations_by_node);
for (const int geometry_input : geometry_input_indices) {
aal::EvalRelation relation;
relation.field_input = input_index;
relation.geometry_input = geometry_input;
r_relations.eval_relations.append(std::move(relation));
}
}
}
bool update_anonymous_attribute_relations(bNodeTree &tree)
{
tree.ensure_topology_cache();
ResourceScope scope;
Array<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
std::unique_ptr<aal::RelationsInNode> new_relations = std::make_unique<aal::RelationsInNode>();
if (!tree.has_available_link_cycle()) {
if (const bNode *group_output_node = tree.group_output_node()) {
infer_propagate_relations(tree, relations_by_node, *group_output_node, *new_relations);
infer_reference_relations(tree, relations_by_node, *group_output_node, *new_relations);
infer_available_relations(relations_by_node, *group_output_node, *new_relations);
}
infer_eval_relations(tree, relations_by_node, *new_relations);
}
const bool group_interface_changed = !tree.runtime->anonymous_attribute_relations ||
*tree.runtime->anonymous_attribute_relations !=
*new_relations;
tree.runtime->anonymous_attribute_relations = std::move(new_relations);
return group_interface_changed;
}
} // namespace blender::bke::anonymous_attribute_inferencing

View File

@ -473,6 +473,9 @@ class NodeTreeMainUpdater {
if (node_field_inferencing::update_field_inferencing(ntree)) {
result.interface_changed = true;
}
if (anonymous_attribute_inferencing::update_anonymous_attribute_relations(ntree)) {
result.interface_changed = true;
}
}
result.output_changed = this->check_if_output_changed(ntree);

View File

@ -1484,5 +1484,6 @@ void ED_mesh_split_faces(Mesh *mesh)
return;
}
geometry::split_edges(*mesh, split_mask);
const bke::AnonymousAttributePropagationInfo propagation_info;
geometry::split_edges(*mesh, split_mask, propagation_info);
}

View File

@ -53,6 +53,10 @@ class Socket : NonCopyable, NonMovable {
* Index of the socket. E.g. 0 for the first input and the first output socket.
*/
int index_in_node_;
/**
* Index of the socket in the entire graph. Every socket has a different index.
*/
int index_in_graph_;
friend Graph;
@ -61,6 +65,7 @@ class Socket : NonCopyable, NonMovable {
bool is_output() const;
int index() const;
int index_in_graph() const;
InputSocket &as_input();
OutputSocket &as_output();
@ -179,6 +184,20 @@ class DummyDebugInfo {
virtual std::string output_name(const int i) const;
};
/**
* Just stores a string per socket in a dummy node.
*/
class SimpleDummyDebugInfo : public DummyDebugInfo {
public:
std::string name;
Vector<std::string> input_names;
Vector<std::string> output_names;
std::string node_name() const override;
std::string input_name(const int i) const override;
std::string output_name(const int i) const override;
};
/**
* A #Node that does *not* correspond to a #LazyFunction. Instead it can be used to indicate inputs
* and outputs of the entire graph. It can have an arbitrary number of inputs and outputs.
@ -205,6 +224,11 @@ class Graph : NonCopyable, NonMovable {
* Contains all nodes in the graph so that it is efficient to iterate over them.
*/
Vector<Node *> nodes_;
/**
* Number of sockets in the graph. Can be used as array size when indexing using
* `Socket::index_in_graph`.
*/
int socket_num_ = 0;
public:
~Graph();
@ -213,6 +237,7 @@ class Graph : NonCopyable, NonMovable {
* Get all nodes in the graph. The index in the span corresponds to #Node::index_in_graph.
*/
Span<const Node *> nodes() const;
Span<Node *> nodes();
/**
* Add a new function node with sockets that match the passed in #LazyFunction.
@ -232,10 +257,24 @@ class Graph : NonCopyable, NonMovable {
*/
void add_link(OutputSocket &from, InputSocket &to);
/**
* If the socket is linked, remove the link.
*/
void clear_origin(InputSocket &socket);
/**
* Make sure that #Node::index_in_graph is up to date.
*/
void update_node_indices();
/**
* Make sure that #Socket::index_in_graph is up to date.
*/
void update_socket_indices();
/**
* Number of sockets in the graph.
*/
int socket_num() const;
/**
* Can be used to assert that #update_node_indices has been called.
@ -280,6 +319,11 @@ inline int Socket::index() const
return index_in_node_;
}
inline int Socket::index_in_graph() const
{
return index_in_graph_;
}
inline InputSocket &Socket::as_input()
{
BLI_assert(this->is_input());
@ -445,6 +489,16 @@ inline Span<const Node *> Graph::nodes() const
return nodes_;
}
inline Span<Node *> Graph::nodes()
{
return nodes_;
}
inline int Graph::socket_num() const
{
return socket_num_;
}
/** \} */
} // namespace blender::fn::lazy_function

View File

@ -86,6 +86,14 @@ void Graph::add_link(OutputSocket &from, InputSocket &to)
from.targets_.append(&to);
}
void Graph::clear_origin(InputSocket &socket)
{
if (socket.origin_ != nullptr) {
socket.origin_->targets_.remove_first_occurrence_and_reorder(&socket);
socket.origin_ = nullptr;
}
}
void Graph::update_node_indices()
{
for (const int i : nodes_.index_range()) {
@ -93,6 +101,20 @@ void Graph::update_node_indices()
}
}
void Graph::update_socket_indices()
{
int socket_counter = 0;
for (const int i : nodes_.index_range()) {
for (InputSocket *socket : nodes_[i]->inputs()) {
socket->index_in_graph_ = socket_counter++;
}
for (OutputSocket *socket : nodes_[i]->outputs()) {
socket->index_in_graph_ = socket_counter++;
}
}
socket_num_ = socket_counter;
}
bool Graph::node_indices_are_valid() const
{
for (const int i : nodes_.index_range()) {
@ -152,6 +174,21 @@ std::string DummyDebugInfo::output_name(const int /*i*/) const
return fallback_name;
}
std::string SimpleDummyDebugInfo::node_name() const
{
return this->name;
}
std::string SimpleDummyDebugInfo::input_name(const int i) const
{
return this->input_names[i];
}
std::string SimpleDummyDebugInfo::output_name(const int i) const
{
return this->output_names[i];
}
std::string Graph::ToDotOptions::socket_name(const Socket &socket) const
{
return socket.name();

View File

@ -9,15 +9,19 @@
namespace blender::geometry {
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves,
IndexMask curve_selection,
const VArray<float> &radius,
const VArray<int> &counts,
bool limit_radius);
bke::CurvesGeometry fillet_curves_poly(
const bke::CurvesGeometry &src_curves,
IndexMask curve_selection,
const VArray<float> &radius,
const VArray<int> &counts,
bool limit_radius,
const bke::AnonymousAttributePropagationInfo &propagation_info);
bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves,
IndexMask curve_selection,
const VArray<float> &radius,
bool limit_radius);
bke::CurvesGeometry fillet_curves_bezier(
const bke::CurvesGeometry &src_curves,
IndexMask curve_selection,
const VArray<float> &radius,
bool limit_radius,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -4,10 +4,14 @@
#include "BLI_index_mask.hh"
#include "BKE_attribute.hh"
struct Mesh;
namespace blender::geometry {
void split_edges(Mesh &mesh, IndexMask mask);
void split_edges(Mesh &mesh,
IndexMask mask,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -19,11 +19,16 @@ namespace blender::geometry {
* intersections of more than three edges will become breaks in curves. Attributes that
* are not built-in on meshes and not curves are transferred to the result curve.
*/
bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection);
bke::CurvesGeometry mesh_to_curve_convert(
const Mesh &mesh,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info);
bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
Span<int> vert_indices,
Span<int> curve_offsets,
IndexRange cyclic_curves);
bke::CurvesGeometry create_curve_from_vert_indices(
const Mesh &mesh,
Span<int> vert_indices,
Span<int> curve_offsets,
IndexRange cyclic_curves,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -2,6 +2,8 @@
#include "BLI_index_mask.hh"
#include "BKE_attribute.hh"
#pragma once
struct PointCloud;
@ -17,8 +19,10 @@ namespace blender::geometry {
* Merge selected points into other selected points within the \a merge_distance. The merged
* indices favor speed over accuracy, since the results will depend on the order of the points.
*/
PointCloud *point_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const IndexMask selection);
PointCloud *point_merge_by_distance(
const PointCloud &src_points,
const float merge_distance,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -19,6 +19,8 @@ struct RealizeInstancesOptions {
* instances. Otherwise, instance attributes are ignored.
*/
bool realize_instance_attributes = true;
bke::AnonymousAttributePropagationInfo propagation_info;
};
/**

View File

@ -4,7 +4,7 @@
#include "FN_field.hh"
#include "BKE_anonymous_attribute.hh"
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_curves.hh"
namespace blender::geometry {

View File

@ -27,6 +27,7 @@ bool try_curves_conversion_in_place(IndexMask selection,
*/
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves,
IndexMask selection,
CurveType dst_type);
CurveType dst_type,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -17,8 +17,10 @@ namespace blender::geometry {
*
* \param selection: A selection of curves to consider when subdividing.
*/
bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
IndexMask selection,
const VArray<int> &cuts);
bke::CurvesGeometry subdivide_curves(
const bke::CurvesGeometry &src_curves,
IndexMask selection,
const VArray<int> &cuts,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -19,6 +19,7 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
IndexMask selection,
const VArray<float> &starts,
const VArray<float> &ends,
GeometryNodeCurveSampleMode mode);
GeometryNodeCurveSampleMode mode,
const bke::AnonymousAttributePropagationInfo &propagation_info);
} // namespace blender::geometry

View File

@ -372,7 +372,7 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
Set<std::string> attributes_to_skip{{"position", "curve_type", "surface_uv_coordinate"}};
attributes.for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
if (id.is_named() && attributes_to_skip.contains(id.name())) {
if (attributes_to_skip.contains(id.name())) {
return true;
}
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);

View File

@ -397,12 +397,14 @@ static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l,
});
}
static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius_input,
const VArray<int> &counts,
const bool limit_radius,
const bool use_bezier_mode)
static bke::CurvesGeometry fillet_curves(
const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius_input,
const VArray<int> &counts,
const bool limit_radius,
const bool use_bezier_mode,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Vector<IndexRange> unselected_ranges = curve_selection.extract_ranges_invert(
src_curves.curves_range());
@ -520,6 +522,7 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) {
duplicate_fillet_point_data(
src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span);
@ -528,7 +531,7 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
if (!unselected_ranges.is_empty()) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
bke::curves::copy_point_data(
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
attribute.dst.finish();
@ -538,26 +541,32 @@ static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
return dst_curves;
}
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius,
const VArray<int> &count,
const bool limit_radius)
bke::CurvesGeometry fillet_curves_poly(
const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius,
const VArray<int> &count,
const bool limit_radius,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false);
return fillet_curves(
src_curves, curve_selection, radius, count, limit_radius, false, propagation_info);
}
bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius,
const bool limit_radius)
bke::CurvesGeometry fillet_curves_bezier(
const bke::CurvesGeometry &src_curves,
const IndexMask curve_selection,
const VArray<float> &radius,
const bool limit_radius,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
return fillet_curves(src_curves,
curve_selection,
radius,
VArray<int>::ForSingle(1, src_curves.points_num()),
limit_radius,
true);
true,
propagation_info);
}
} // namespace blender::geometry

View File

@ -3,6 +3,7 @@
#include "BLI_array_utils.hh"
#include "BLI_devirtualize_parameters.hh"
#include "BLI_index_mask.hh"
#include "BLI_user_counter.hh"
#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
@ -59,35 +60,36 @@ static void add_new_vertices(Mesh &mesh, const Span<int> new_to_old_verts_map)
static void add_new_edges(Mesh &mesh,
const Span<MEdge> new_edges,
const Span<int> new_to_old_edges_map)
const Span<int> new_to_old_edges_map,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
/* Store a copy of the IDs locally since we will remove the existing attributes which
* can also free the names, since the API does not provide pointer stability. */
Vector<std::string> named_ids;
Vector<bke::WeakAnonymousAttributeID> anonymous_ids;
Vector<UserCounter<const bke::AnonymousAttributeID>> anonymous_ids;
for (const bke::AttributeIDRef &id : attributes.all_ids()) {
if (attributes.lookup_meta_data(id)->domain != ATTR_DOMAIN_EDGE) {
continue;
}
if (!id.should_be_kept()) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
continue;
}
if (id.is_named()) {
if (!id.is_anonymous()) {
named_ids.append(id.name());
}
else {
anonymous_ids.append(bke::WeakAnonymousAttributeID(&id.anonymous_id()));
BKE_anonymous_attribute_id_increment_weak(&id.anonymous_id());
anonymous_ids.append(&id.anonymous_id());
id.anonymous_id().user_add();
}
}
Vector<bke::AttributeIDRef> local_edge_ids;
for (const StringRef name : named_ids) {
local_edge_ids.append(name);
}
for (const bke::WeakAnonymousAttributeID &id : anonymous_ids) {
local_edge_ids.append(id.get());
for (const UserCounter<const bke::AnonymousAttributeID> &id : anonymous_ids) {
local_edge_ids.append(*id);
}
/* Build new arrays for the copied edge attributes. Unlike vertices, new edges aren't all at the
@ -348,7 +350,9 @@ static void split_edge_per_poly(const int edge_i,
edge_to_loop_map[edge_i].resize(1);
}
void split_edges(Mesh &mesh, const IndexMask mask)
void split_edges(Mesh &mesh,
const IndexMask mask,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
/* Flag vertices that need to be split. */
Array<bool> should_split_vert(mesh.totvert, false);
@ -483,7 +487,7 @@ void split_edges(Mesh &mesh, const IndexMask mask)
/* Step 5: Resize the mesh to add the new vertices and rebuild the edges. */
add_new_vertices(mesh, new_to_old_verts_map);
add_new_edges(mesh, new_edges, new_to_old_edges_map);
add_new_edges(mesh, new_edges, new_to_old_edges_map, propagation_info);
BKE_mesh_tag_edges_split(&mesh);
}

View File

@ -19,10 +19,12 @@
namespace blender::geometry {
bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
const Span<int> vert_indices,
const Span<int> curve_offsets,
const IndexRange cyclic_curves)
bke::CurvesGeometry create_curve_from_vert_indices(
const Mesh &mesh,
const Span<int> vert_indices,
const Span<int> curve_offsets,
const IndexRange cyclic_curves,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size());
curves.offsets_for_write().drop_back(1).copy_from(curve_offsets);
@ -43,7 +45,7 @@ bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
continue;
}
if (!attribute_id.should_be_kept()) {
if (attribute_id.is_anonymous() && !propagation_info.propagate(attribute_id.anonymous_id())) {
continue;
}
@ -209,14 +211,17 @@ static Vector<std::pair<int, int>> get_selected_edges(const Mesh &mesh, const In
return selected_edges;
}
bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection)
bke::CurvesGeometry mesh_to_curve_convert(
const Mesh &mesh,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
Vector<std::pair<int, int>> selected_edges = get_selected_edges(mesh, selection);
const Span<MVert> verts = mesh.verts();
CurveFromEdgesOutput output = edges_to_curve_point_indices(verts, selected_edges);
return create_curve_from_vert_indices(
mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves);
mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves, propagation_info);
}
} // namespace blender::geometry

View File

@ -15,7 +15,8 @@ namespace blender::geometry {
PointCloud *point_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const IndexMask selection)
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const bke::AttributeAccessor src_attributes = src_points.attributes();
VArraySpan<float3> positions = src_attributes.lookup_or_default<float3>(
@ -125,7 +126,7 @@ PointCloud *point_merge_by_distance(const PointCloud &src_points,
/* Transfer all other attributes. */
for (const bke::AttributeIDRef &id : attribute_ids) {
if (!id.should_be_kept()) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
continue;
}

View File

@ -636,8 +636,11 @@ static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate(
}
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
in_geometry_set.gather_attributes_for_propagation(
src_component_types, GEO_COMPONENT_TYPE_POINT_CLOUD, true, attributes_to_propagate);
in_geometry_set.gather_attributes_for_propagation(src_component_types,
GEO_COMPONENT_TYPE_POINT_CLOUD,
true,
options.propagation_info,
attributes_to_propagate);
attributes_to_propagate.remove("position");
r_create_id = attributes_to_propagate.pop_try("id").has_value();
r_create_radii = attributes_to_propagate.pop_try("radius").has_value();
@ -829,8 +832,11 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
}
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
in_geometry_set.gather_attributes_for_propagation(
src_component_types, GEO_COMPONENT_TYPE_MESH, true, attributes_to_propagate);
in_geometry_set.gather_attributes_for_propagation(src_component_types,
GEO_COMPONENT_TYPE_MESH,
true,
options.propagation_info,
attributes_to_propagate);
attributes_to_propagate.remove("position");
attributes_to_propagate.remove("normal");
attributes_to_propagate.remove("shade_smooth");
@ -1149,8 +1155,11 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate(
}
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
in_geometry_set.gather_attributes_for_propagation(
src_component_types, GEO_COMPONENT_TYPE_CURVE, true, attributes_to_propagate);
in_geometry_set.gather_attributes_for_propagation(src_component_types,
GEO_COMPONENT_TYPE_CURVE,
true,
options.propagation_info,
attributes_to_propagate);
attributes_to_propagate.remove("position");
attributes_to_propagate.remove("radius");
attributes_to_propagate.remove("resolution");

View File

@ -53,7 +53,7 @@ static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length
static bool interpolate_attribute_to_curves(const bke::AttributeIDRef &attribute_id,
const std::array<int, CURVE_TYPES_NUM> &type_counts)
{
if (!attribute_id.is_named()) {
if (attribute_id.is_anonymous()) {
return true;
}
if (ELEM(attribute_id.name(),
@ -81,7 +81,7 @@ static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attri
"handle_left",
"nurbs_weight",
}};
return !(attribute_id.is_named() && no_interpolation.contains(attribute_id.name()));
return !no_interpolation.contains(attribute_id.name());
}
/**

View File

@ -286,8 +286,10 @@ static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<
});
}
static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves,
const IndexMask selection)
static bke::CurvesGeometry convert_curves_to_bezier(
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
const VArray<int8_t> src_types = src_curves.curve_types();
@ -315,6 +317,7 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_type_left",
"handle_type_right",
@ -460,8 +463,10 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s
return dst_curves;
}
static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &src_curves,
const IndexMask selection)
static bke::CurvesGeometry convert_curves_to_nurbs(
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const VArray<int8_t> src_types = src_curves.curve_types();
const VArray<bool> src_cyclic = src_curves.cyclic();
@ -487,6 +492,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_type_left",
"handle_type_right",
@ -639,16 +645,17 @@ static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const CurveType dst_type)
const CurveType dst_type,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
switch (dst_type) {
case CURVE_TYPE_CATMULL_ROM:
case CURVE_TYPE_POLY:
return convert_curves_trivial(src_curves, selection, dst_type);
case CURVE_TYPE_BEZIER:
return convert_curves_to_bezier(src_curves, selection);
return convert_curves_to_bezier(src_curves, selection, propagation_info);
case CURVE_TYPE_NURBS:
return convert_curves_to_nurbs(src_curves, selection);
return convert_curves_to_nurbs(src_curves, selection, propagation_info);
}
BLI_assert_unreachable();
return {};

View File

@ -294,9 +294,11 @@ static void subdivide_bezier_positions(const Span<float3> src_positions,
cyclic, dst_types_l, dst_types_r, dst_positions, dst_handles_l, dst_handles_r);
}
bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const VArray<int> &cuts)
bke::CurvesGeometry subdivide_curves(
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const VArray<int> &cuts,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
src_curves.curves_range());
@ -338,7 +340,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
auto subdivide_catmull_rom = [&](IndexMask selection) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
subdivide_attribute_catmull_rom(src_curves,
dst_curves,
selection,
@ -352,7 +354,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
auto subdivide_poly = [&](IndexMask selection) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
subdivide_attribute_linear(
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
attribute.dst.finish();
@ -396,6 +398,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_type_left",
"handle_type_right",
@ -421,7 +424,7 @@ bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
if (!unselected_ranges.is_empty()) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, propagation_info)) {
bke::curves::copy_point_data(
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
attribute.dst.finish();

View File

@ -929,7 +929,8 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const VArray<float> &starts,
const VArray<float> &ends,
const GeometryNodeCurveSampleMode mode)
const GeometryNodeCurveSampleMode mode,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
BLI_assert(selection.size() > 0);
BLI_assert(selection.last() <= src_curves.curves_num());
@ -991,14 +992,19 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
transfer_curve_skip.remove("nurbs_order");
transfer_curve_skip.remove("knots_mode");
}
bke::copy_attribute_domain(
src_attributes, dst_attributes, selection, ATTR_DOMAIN_CURVE, transfer_curve_skip);
bke::copy_attribute_domain(src_attributes,
dst_attributes,
selection,
ATTR_DOMAIN_CURVE,
propagation_info,
transfer_curve_skip);
/* Fetch custom point domain attributes for transfer (copy). */
Vector<bke::AttributeTransferData> transfer_attributes = bke::retrieve_attributes_for_transfer(
src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
{"position",
"handle_left",
"handle_right",
@ -1063,8 +1069,12 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
/* Copy unselected */
if (!inverse_selection.is_empty()) {
transfer_curve_skip.remove("cyclic");
bke::copy_attribute_domain(
src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE, transfer_curve_skip);
bke::copy_attribute_domain(src_attributes,
dst_attributes,
inverse_selection,
ATTR_DOMAIN_CURVE,
propagation_info,
transfer_curve_skip);
/* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */
dst_curves.cyclic_for_write().fill_indices(selection, false);
@ -1075,8 +1085,11 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
}
/* Copy point domain. */
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, copy_point_skip)) {
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes,
dst_attributes,
ATTR_DOMAIN_MASK_POINT,
propagation_info,
copy_point_skip)) {
bke::curves::copy_point_data(
src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span);
attribute.dst.finish();

View File

@ -15,7 +15,15 @@
extern "C" {
#endif
struct AnonymousAttributeID;
/** Workaround to forward-declare C++ type in C header. */
#ifdef __cplusplus
namespace blender::bke {
class AnonymousAttributeID;
} // namespace blender::bke
using AnonymousAttributeIDHandle = blender::bke::AnonymousAttributeID;
#else
typedef struct AnonymousAttributeIDHandle AnonymousAttributeIDHandle;
#endif
/** Descriptor and storage for a custom data layer. */
typedef struct CustomDataLayer {
@ -40,12 +48,10 @@ typedef struct CustomDataLayer {
/** Layer data. */
void *data;
/**
* Run-time identifier for this layer. If no one has a strong reference to this id anymore,
* the layer can be removed. The custom data layer only has a weak reference to the id, because
* otherwise there will always be a strong reference and the attribute can't be removed
* automatically.
* Run-time identifier for this layer. Can be used to retrieve information about where this
* attribute was created.
*/
const struct AnonymousAttributeID *anonymous_id;
const AnonymousAttributeIDHandle *anonymous_id;
} CustomDataLayer;
#define MAX_CUSTOMDATA_LAYER_NAME 64

View File

@ -884,12 +884,12 @@ static void find_side_effect_nodes_for_viewer_path(
/* Not only mark the viewer node as having side effects, but also all group nodes it is contained
* in. */
r_side_effect_nodes.add(compute_context_builder.hash(),
&find_viewer_lf_node(*found_viewer_node));
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(),
&find_viewer_lf_node(*found_viewer_node));
compute_context_builder.pop();
while (!compute_context_builder.is_empty()) {
r_side_effect_nodes.add(compute_context_builder.hash(),
&find_group_lf_node(*group_node_stack.pop()));
r_side_effect_nodes.add_non_duplicates(compute_context_builder.hash(),
&find_group_lf_node(*group_node_stack.pop()));
compute_context_builder.pop();
}
}
@ -1124,12 +1124,11 @@ static GeometrySet compute_geometry(
{
const blender::nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
Span<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
Vector<const lf::InputSocket *> graph_outputs;
for (const bNodeSocket *bsocket : output_node.input_sockets().drop_back(1)) {
const lf::InputSocket &socket = mapping.dummy_socket_map.lookup(bsocket)->as_input();
graph_outputs.append(&socket);
}
Vector<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
graph_inputs.extend(mapping.group_output_used_sockets);
graph_inputs.extend(mapping.attribute_set_by_geometry_output.values().begin(),
mapping.attribute_set_by_geometry_output.values().end());
Vector<const lf::InputSocket *> graph_outputs = mapping.standard_group_output_sockets;
Array<GMutablePointer> param_inputs(graph_inputs.size());
Array<GMutablePointer> param_outputs(graph_outputs.size());
@ -1166,21 +1165,36 @@ static GeometrySet compute_geometry(
blender::LinearAllocator<> allocator;
Vector<GMutablePointer> inputs_to_destruct;
int input_index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, interface_socket, &btree.inputs, input_index) {
if (interface_socket->type == SOCK_GEOMETRY && input_index == 0) {
int input_index = -1;
for (const int i : btree.interface_inputs().index_range()) {
input_index++;
const bNodeSocket &interface_socket = *btree.interface_inputs()[i];
if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) {
param_inputs[input_index] = &input_geometry_set;
continue;
}
const CPPType *type = interface_socket->typeinfo->geometry_nodes_cpp_type;
const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type;
BLI_assert(type != nullptr);
void *value = allocator.allocate(type->size(), type->alignment());
initialize_group_input(*nmd, *interface_socket, input_index, value);
initialize_group_input(*nmd, interface_socket, i, value);
param_inputs[input_index] = {type, value};
inputs_to_destruct.append({type, value});
}
Array<bool> output_used_inputs(btree.interface_outputs().size(), true);
for (const int i : btree.interface_outputs().index_range()) {
input_index++;
param_inputs[input_index] = &output_used_inputs[i];
}
Array<blender::bke::AnonymousAttributeSet> attributes_to_propagate(
mapping.attribute_set_by_geometry_output.size());
for (const int i : attributes_to_propagate.index_range()) {
input_index++;
param_inputs[input_index] = &attributes_to_propagate[i];
}
for (const int i : graph_outputs.index_range()) {
const lf::InputSocket &socket = *graph_outputs[i];
const CPPType &type = socket.type();

View File

@ -19,6 +19,8 @@ struct ModifierData;
namespace blender::nodes {
using bke::AnonymousAttributeFieldInput;
using bke::AnonymousAttributeID;
using bke::AnonymousAttributePropagationInfo;
using bke::AttributeAccessor;
using bke::AttributeFieldInput;
using bke::AttributeIDRef;
@ -26,13 +28,12 @@ using bke::AttributeKind;
using bke::AttributeMetaData;
using bke::AttributeReader;
using bke::AttributeWriter;
using bke::AutoAnonymousAttributeID;
using bke::GAttributeReader;
using bke::GAttributeWriter;
using bke::GSpanAttributeWriter;
using bke::MutableAttributeAccessor;
using bke::SpanAttributeWriter;
using bke::StrongAnonymousAttributeID;
using bke::WeakAnonymousAttributeID;
using fn::Field;
using fn::FieldContext;
using fn::FieldEvaluator;
@ -43,15 +44,38 @@ using fn::ValueOrField;
using geo_eval_log::NamedAttributeUsage;
using geo_eval_log::NodeWarningType;
/**
* An anonymous attribute created by a node.
*/
class NodeAnonymousAttributeID : public AnonymousAttributeID {
std::string long_name_;
public:
NodeAnonymousAttributeID(const Object &object,
const ComputeContext &compute_context,
const bNode &bnode,
const StringRef identifier);
};
class GeoNodeExecParams {
private:
const bNode &node_;
lf::Params &params_;
const lf::Context &lf_context_;
const Map<StringRef, int> &lf_input_for_output_bsocket_usage_;
const Map<StringRef, int> &lf_input_for_attribute_propagation_to_output_;
public:
GeoNodeExecParams(const bNode &node, lf::Params &params, const lf::Context &lf_context)
: node_(node), params_(params), lf_context_(lf_context)
GeoNodeExecParams(const bNode &node,
lf::Params &params,
const lf::Context &lf_context,
const Map<StringRef, int> &lf_input_for_output_bsocket_usage,
const Map<StringRef, int> &lf_input_for_attribute_propagation_to_output)
: node_(node),
params_(params),
lf_context_(lf_context),
lf_input_for_output_bsocket_usage_(lf_input_for_output_bsocket_usage),
lf_input_for_attribute_propagation_to_output_(lf_input_for_attribute_propagation_to_output)
{
}
@ -245,6 +269,49 @@ class GeoNodeExecParams {
void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage);
/**
* Return true when the anonymous attribute referenced by the given output should be created.
*/
bool anonymous_attribute_output_is_required(const StringRef output_identifier)
{
const int lf_index = lf_input_for_output_bsocket_usage_.lookup(output_identifier);
return params_.get_input<bool>(lf_index);
}
/**
* Return a new anonymous attribute id for the given output. None is returned if the anonymous
* attribute is not needed.
*/
AutoAnonymousAttributeID get_output_anonymous_attribute_id_if_needed(
const StringRef output_identifier)
{
if (!this->anonymous_attribute_output_is_required(output_identifier)) {
return {};
}
const GeoNodesLFUserData &user_data = *this->user_data();
const ComputeContext &compute_context = *user_data.compute_context;
return MEM_new<NodeAnonymousAttributeID>(__func__,
*user_data.modifier_data->self_object,
compute_context,
node_,
output_identifier);
}
/**
* Get information about which anonymous attributes should be propagated to the given output.
*/
AnonymousAttributePropagationInfo get_output_propagation_info(
const StringRef output_identifier) const
{
const int lf_index = lf_input_for_attribute_propagation_to_output_.lookup(output_identifier);
const bke::AnonymousAttributeSet &set = params_.get_input<bke::AnonymousAttributeSet>(
lf_index);
AnonymousAttributePropagationInfo info;
info.names = set.names;
info.propagate_all = false;
return info;
}
private:
/* Utilities for detecting common errors at when using this class. */
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;

View File

@ -78,6 +78,28 @@ struct GeoNodesLFUserData : public lf::UserData {
bool log_socket_values = true;
};
/**
* In the general case, this is #DynamicSocket. That means that to determine if a node group will
* use a particular input, it has to be partially executed.
*
* In other cases, it's not necessary to look into the node group to determine if an input is
* necessary.
*/
enum class InputUsageHintType {
/** The input socket is never used. */
Never,
/** The input socket is used when a subset of the outputs is used. */
DependsOnOutput,
/** Can't determine statically if the input is used, check the corresponding output socket. */
DynamicSocket,
};
struct InputUsageHint {
InputUsageHintType type = InputUsageHintType::DependsOnOutput;
/** Used in depends-on-output mode. */
Vector<int> output_dependencies;
};
/**
* Contains the mapping between the #bNodeTree and the corresponding lazy-function graph.
* This is *not* a one-to-one mapping.
@ -91,7 +113,32 @@ struct GeometryNodeLazyFunctionGraphMapping {
* The inputs sockets in the graph. Multiple group input nodes are combined into one in the
* lazy-function graph.
*/
Vector<lf::OutputSocket *> group_input_sockets;
Vector<const lf::OutputSocket *> group_input_sockets;
/**
* Dummy output sockets that correspond to the active group output node. If there is no such
* node, defaulted fallback outputs are created.
*/
Vector<const lf::InputSocket *> standard_group_output_sockets;
/**
* Dummy boolean sockets that have to be passed in from the outside and indicate whether a
* specific output will be used.
*/
Vector<const lf::OutputSocket *> group_output_used_sockets;
/**
* Dummy boolean sockets that can be used as group output that indicate whether a specific input
* will be used (this may depend on the used outputs as well as other inputs).
*/
Vector<const lf::InputSocket *> group_input_usage_sockets;
/**
* This is an optimization to avoid partially evaluating a node group just to figure out which
* inputs are needed.
*/
Vector<InputUsageHint> group_input_usage_hints;
/**
* If the node group propagates attributes from an input geometry to the output, it has to know
* which attributes should be propagated and which can be removed (for optimization purposes).
*/
Map<int, const lf::OutputSocket *> attribute_set_by_geometry_output;
/**
* A mapping used for logging intermediate values.
*/

View File

@ -50,10 +50,10 @@ Mesh *create_grid_mesh(
int verts_x, int verts_y, float size_x, float size_y, const AttributeIDRef &uv_map_id);
struct ConeAttributeOutputs {
StrongAnonymousAttributeID top_id;
StrongAnonymousAttributeID bottom_id;
StrongAnonymousAttributeID side_id;
StrongAnonymousAttributeID uv_map_id;
AutoAnonymousAttributeID top_id;
AutoAnonymousAttributeID bottom_id;
AutoAnonymousAttributeID side_id;
AutoAnonymousAttributeID uv_map_id;
};
Mesh *create_cylinder_or_cone_mesh(float radius_top,
@ -81,6 +81,7 @@ void separate_geometry(GeometrySet &geometry_set,
eAttrDomain domain,
GeometryNodeDeleteGeometryMode mode,
const Field<bool> &selection_field,
const AnonymousAttributePropagationInfo &propagation_info,
bool &r_is_error);
void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,

View File

@ -142,9 +142,12 @@ static void node_geo_exec(GeoNodeExecParams params)
const eAttrDomain domain = eAttrDomain(storage.domain);
const std::string output_identifier = "Attribute" + identifier_suffix(data_type);
AutoAnonymousAttributeID attribute_id = params.get_output_anonymous_attribute_id_if_needed(
output_identifier);
if (!params.output_is_required(output_identifier)) {
if (!attribute_id) {
params.set_output("Geometry", geometry_set);
params.set_default_remaining_outputs();
return;
}
@ -171,7 +174,6 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
WeakAnonymousAttributeID anonymous_id{"Attribute"};
const CPPType &type = field.cpp_type();
/* Run on the instances component separately to only affect the top level of instances. */
@ -179,7 +181,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_instances()) {
GeometryComponent &component = geometry_set.get_component_for_write(
GEO_COMPONENT_TYPE_INSTANCES);
bke::try_capture_field_on_geometry(component, anonymous_id.get(), domain, field);
bke::try_capture_field_on_geometry(component, *attribute_id, domain, field);
}
}
else {
@ -190,14 +192,14 @@ static void node_geo_exec(GeoNodeExecParams params)
for (const GeometryComponentType type : types) {
if (geometry_set.has(type)) {
GeometryComponent &component = geometry_set.get_component_for_write(type);
bke::try_capture_field_on_geometry(component, anonymous_id.get(), domain, field);
bke::try_capture_field_on_geometry(component, *attribute_id, domain, field);
}
}
});
}
GField output_field{std::make_shared<bke::AnonymousAttributeFieldInput>(
std::move(anonymous_id), type, params.attribute_producer_name())};
std::move(attribute_id), type, params.attribute_producer_name())};
switch (data_type) {
case CD_PROP_FLOAT: {

View File

@ -30,7 +30,7 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
}
struct AttributeOutputs {
StrongAnonymousAttributeID intersecting_edges_id;
AutoAnonymousAttributeID intersecting_edges_id;
};
static void node_update(bNodeTree *ntree, bNode *node)
@ -125,9 +125,8 @@ static void node_geo_exec(GeoNodeExecParams params)
}
AttributeOutputs attribute_outputs;
if (params.output_is_required("Intersecting Edges")) {
attribute_outputs.intersecting_edges_id = StrongAnonymousAttributeID("Intersecting Edges");
}
attribute_outputs.intersecting_edges_id = params.get_output_anonymous_attribute_id_if_needed(
"Intersecting Edges");
Vector<int> intersecting_edges;
Mesh *result = blender::meshintersect::direct_mesh_boolean(

View File

@ -67,6 +67,9 @@ static void node_geo_exec(GeoNodeExecParams params)
count_field.emplace(params.extract_input<Field<int>>("Count"));
}
const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info(
"Curve");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (!geometry_set.has_curves()) {
return;
@ -82,7 +85,11 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_NODE_CURVE_FILLET_BEZIER: {
evaluator.evaluate();
bke::CurvesGeometry dst_curves = geometry::fillet_curves_bezier(
curves, curves.curves_range(), evaluator.get_evaluated<float>(0), limit_radius);
curves,
curves.curves_range(),
evaluator.get_evaluated<float>(0),
limit_radius,
propagation_info);
Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
bke::curves_copy_parameters(curves_id, *dst_curves_id);
geometry_set.replace_curves(dst_curves_id);
@ -96,7 +103,8 @@ static void node_geo_exec(GeoNodeExecParams params)
curves.curves_range(),
evaluator.get_evaluated<float>(0),
evaluator.get_evaluated<int>(1),
limit_radius);
limit_radius,
propagation_info);
Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
bke::curves_copy_parameters(curves_id, *dst_curves_id);
geometry_set.replace_curves(dst_curves_id);

View File

@ -59,10 +59,10 @@ static Curves *create_star_curve(const float inner_radius,
}
static void create_selection_output(CurveComponent &component,
StrongAnonymousAttributeID &r_attribute)
AutoAnonymousAttributeID &r_attribute)
{
SpanAttributeWriter<bool> selection =
component.attributes_for_write()->lookup_or_add_for_write_only_span<bool>(r_attribute.get(),
component.attributes_for_write()->lookup_or_add_for_write_only_span<bool>(*r_attribute,
ATTR_DOMAIN_POINT);
for (int i : selection.span.index_range()) {
selection.span[i] = i % 2 == 0;
@ -78,12 +78,12 @@ static void node_geo_exec(GeoNodeExecParams params)
std::max(params.extract_input<int>("Points"), 3));
GeometrySet output = GeometrySet::create_with_curves(curves);
if (params.output_is_required("Outer Points")) {
StrongAnonymousAttributeID attribute_output("Outer Points");
create_selection_output(output.get_component_for_write<CurveComponent>(), attribute_output);
if (AutoAnonymousAttributeID outer_points_id =
params.get_output_anonymous_attribute_id_if_needed("Outer Points")) {
create_selection_output(output.get_component_for_write<CurveComponent>(), outer_points_id);
params.set_output("Outer Points",
AnonymousAttributeFieldInput::Create<bool>(
std::move(attribute_output), params.attribute_producer_name()));
std::move(outer_points_id), params.attribute_producer_name()));
}
params.set_output("Curve", std::move(output));
}

View File

@ -67,7 +67,8 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
bke::CurvesGeometry dst_curves = geometry::convert_curves(src_curves, selection, dst_type);
bke::CurvesGeometry dst_curves = geometry::convert_curves(
src_curves, selection, dst_type, params.get_output_propagation_info("Curve"));
Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
bke::curves_copy_parameters(src_curves_id, *dst_curves_id);

View File

@ -49,7 +49,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
bke::CurvesGeometry dst_curves = geometry::subdivide_curves(
src_curves, src_curves.curves_range(), cuts);
src_curves, src_curves.curves_range(), cuts, params.get_output_propagation_info("Curve"));
Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
bke::curves_copy_parameters(src_curves_id, *dst_curves_id);

View File

@ -25,7 +25,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const GeometrySet &profile_set,
const bool fill_caps)
const bool fill_caps,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Curves &curves = *geometry_set.get_curves_for_read();
const Curves *profile_curves = profile_set.get_curves_for_read();
@ -33,13 +34,15 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
if (profile_curves == nullptr) {
Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry));
Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry),
propagation_info);
geometry_set.replace_mesh(mesh);
}
else {
Mesh *mesh = bke::curve_to_mesh_sweep(bke::CurvesGeometry::wrap(curves.geometry),
bke::CurvesGeometry::wrap(profile_curves->geometry),
fill_caps);
fill_caps,
propagation_info);
geometry_set.replace_mesh(mesh);
}
}
@ -52,7 +55,8 @@ static void node_geo_exec(GeoNodeExecParams params)
curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_curves()) {
geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps);
geometry_set_curve_to_mesh(
geometry_set, profile_set, fill_caps, params.get_output_propagation_info("Mesh"));
}
geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});

View File

@ -111,19 +111,12 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
StrongAnonymousAttributeID tangent_anonymous_id;
StrongAnonymousAttributeID normal_anonymous_id;
StrongAnonymousAttributeID rotation_anonymous_id;
const bool rotation_required = params.output_is_required("Rotation");
if (params.output_is_required("Tangent") || rotation_required) {
tangent_anonymous_id = StrongAnonymousAttributeID("Tangent");
}
if (params.output_is_required("Normal") || rotation_required) {
normal_anonymous_id = StrongAnonymousAttributeID("Normal");
}
if (rotation_required) {
rotation_anonymous_id = StrongAnonymousAttributeID("Rotation");
}
AutoAnonymousAttributeID tangent_anonymous_id =
params.get_output_anonymous_attribute_id_if_needed("Tangent");
AutoAnonymousAttributeID normal_anonymous_id =
params.get_output_anonymous_attribute_id_if_needed("Normal");
AutoAnonymousAttributeID rotation_anonymous_id =
params.get_output_anonymous_attribute_id_if_needed("Rotation");
geometry::ResampleCurvesOutputAttributeIDs resample_attributes;
resample_attributes.tangent_id = tangent_anonymous_id.get();

View File

@ -112,7 +112,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
const GeometryNodeCurveSampleMode mode,
Field<bool> &selection_field,
Field<float> &start_field,
Field<float> &end_field)
Field<float> &end_field,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (!geometry_set.has_curves()) {
return;
@ -139,7 +140,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
}
bke::CurvesGeometry dst_curves = geometry::trim_curves(
src_curves, selection, starts, ends, mode);
src_curves, selection, starts, ends, mode, propagation_info);
Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
geometry_set.replace_curves(dst_curves_id);
@ -153,19 +154,24 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info(
"Curve");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
Field<float> start_field = params.extract_input<Field<float>>("Start");
Field<float> end_field = params.extract_input<Field<float>>("End");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry_set_curve_trim(geometry_set, mode, selection_field, start_field, end_field);
geometry_set_curve_trim(
geometry_set, mode, selection_field, start_field, end_field, propagation_info);
});
}
else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
Field<float> start_field = params.extract_input<Field<float>>("Start_001");
Field<float> end_field = params.extract_input<Field<float>>("End_001");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry_set_curve_trim(geometry_set, mode, selection_field, start_field, end_field);
geometry_set_curve_trim(
geometry_set, mode, selection_field, start_field, end_field, propagation_info);
});
}

View File

@ -306,7 +306,8 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
static void delete_curves_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const eAttrDomain selection_domain)
const eAttrDomain selection_domain,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
@ -330,15 +331,17 @@ static void delete_curves_selection(GeometrySet &geometry_set,
bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
if (selection_domain == ATTR_DOMAIN_POINT) {
curves.remove_points(selection);
curves.remove_points(selection, propagation_info);
}
else if (selection_domain == ATTR_DOMAIN_CURVE) {
curves.remove_curves(selection);
curves.remove_curves(selection, propagation_info);
}
}
static void separate_point_cloud_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field)
static void separate_point_cloud_selection(
GeometrySet &geometry_set,
const Field<bool> &selection_field,
const AnonymousAttributePropagationInfo &propagation_info)
{
const PointCloud &src_pointcloud = *geometry_set.get_pointcloud_for_read();
@ -355,8 +358,11 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_POINT_CLOUD},
GEO_COMPONENT_TYPE_POINT_CLOUD,
false,
propagation_info,
attributes);
copy_attributes_based_on_mask(attributes,
src_pointcloud.attributes(),
@ -367,7 +373,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
}
static void delete_selected_instances(GeometrySet &geometry_set,
const Field<bool> &selection_field)
const Field<bool> &selection_field,
const AnonymousAttributePropagationInfo &propagation_info)
{
bke::Instances &instances = *geometry_set.get_instances_for_write();
bke::InstancesFieldContext field_context{instances};
@ -381,7 +388,7 @@ static void delete_selected_instances(GeometrySet &geometry_set,
return;
}
instances.remove(selection);
instances.remove(selection, propagation_info);
}
static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection,
@ -819,7 +826,8 @@ static void do_mesh_separation(GeometrySet &geometry_set,
const Mesh &mesh_in,
const Span<bool> selection,
const eAttrDomain domain,
const GeometryNodeDeleteGeometryMode mode)
const GeometryNodeDeleteGeometryMode mode,
const AnonymousAttributePropagationInfo &propagation_info)
{
/* Needed in all cases. */
Vector<int> selected_poly_indices;
@ -831,7 +839,7 @@ static void do_mesh_separation(GeometrySet &geometry_set,
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes);
{GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, propagation_info, attributes);
switch (mode) {
case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: {
@ -1059,7 +1067,8 @@ static void do_mesh_separation(GeometrySet &geometry_set,
static void separate_mesh_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const eAttrDomain selection_domain,
const GeometryNodeDeleteGeometryMode mode)
const GeometryNodeDeleteGeometryMode mode,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Mesh &src_mesh = *geometry_set.get_mesh_for_read();
bke::MeshFieldContext field_context{src_mesh, selection_domain};
@ -1074,7 +1083,8 @@ static void separate_mesh_selection(GeometrySet &geometry_set,
const VArraySpan<bool> selection_span{selection};
do_mesh_separation(geometry_set, src_mesh, selection_span, selection_domain, mode);
do_mesh_separation(
geometry_set, src_mesh, selection_span, selection_domain, mode, propagation_info);
}
} // namespace blender::nodes::node_geo_delete_geometry_cc
@ -1085,6 +1095,7 @@ void separate_geometry(GeometrySet &geometry_set,
const eAttrDomain domain,
const GeometryNodeDeleteGeometryMode mode,
const Field<bool> &selection_field,
const AnonymousAttributePropagationInfo &propagation_info,
bool &r_is_error)
{
namespace file_ns = blender::nodes::node_geo_delete_geometry_cc;
@ -1092,26 +1103,27 @@ void separate_geometry(GeometrySet &geometry_set,
bool some_valid_domain = false;
if (geometry_set.has_pointcloud()) {
if (domain == ATTR_DOMAIN_POINT) {
file_ns::separate_point_cloud_selection(geometry_set, selection_field);
file_ns::separate_point_cloud_selection(geometry_set, selection_field, propagation_info);
some_valid_domain = true;
}
}
if (geometry_set.has_mesh()) {
if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) {
file_ns::separate_mesh_selection(geometry_set, selection_field, domain, mode);
file_ns::separate_mesh_selection(
geometry_set, selection_field, domain, mode, propagation_info);
some_valid_domain = true;
}
}
if (geometry_set.has_curves()) {
if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
file_ns::delete_curves_selection(
geometry_set, fn::invert_boolean_field(selection_field), domain);
geometry_set, fn::invert_boolean_field(selection_field), domain, propagation_info);
some_valid_domain = true;
}
}
if (geometry_set.has_instances()) {
if (domain == ATTR_DOMAIN_INSTANCE) {
file_ns::delete_selected_instances(geometry_set, selection_field);
file_ns::delete_selected_instances(geometry_set, selection_field, propagation_info);
some_valid_domain = true;
}
}
@ -1171,15 +1183,18 @@ static void node_geo_exec(GeoNodeExecParams params)
const eAttrDomain domain = eAttrDomain(storage.domain);
const GeometryNodeDeleteGeometryMode mode = (GeometryNodeDeleteGeometryMode)storage.mode;
const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info(
"Geometry");
if (domain == ATTR_DOMAIN_INSTANCE) {
bool is_error;
separate_geometry(geometry_set, domain, mode, selection, is_error);
separate_geometry(geometry_set, domain, mode, selection, propagation_info, is_error);
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
bool is_error;
/* Invert here because we want to keep the things not in the selection. */
separate_geometry(geometry_set, domain, mode, selection, is_error);
separate_geometry(geometry_set, domain, mode, selection, propagation_info, is_error);
});
}

View File

@ -320,8 +320,8 @@ BLI_NOINLINE static void propagate_existing_attributes(
namespace {
struct AttributeOutputs {
StrongAnonymousAttributeID normal_id;
StrongAnonymousAttributeID rotation_id;
AutoAnonymousAttributeID normal_id;
AutoAnonymousAttributeID rotation_id;
};
} // namespace
@ -496,8 +496,11 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
geometry_set.replace_pointcloud(pointcloud);
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_MESH},
GEO_COMPONENT_TYPE_POINT_CLOUD,
false,
params.get_output_propagation_info("Points"),
attributes);
/* Position is set separately. */
attributes.remove("position");
@ -518,12 +521,8 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
AttributeOutputs attribute_outputs;
if (params.output_is_required("Normal")) {
attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
}
if (params.output_is_required("Rotation")) {
attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
}
attribute_outputs.normal_id = params.get_output_anonymous_attribute_id_if_needed("Normal");
attribute_outputs.rotation_id = params.get_output_anonymous_attribute_id_if_needed("Rotation");
lazy_threading::send_hint();

View File

@ -132,6 +132,7 @@ static void transfer_attributes(
const Span<int> new_to_old_edges_map,
const Span<int> new_to_old_face_corners_map,
const Span<std::pair<int, int>> boundary_vertex_to_relevant_face_map,
const AnonymousAttributePropagationInfo &propagation_info,
const AttributeAccessor src_attributes,
MutableAttributeAccessor dst_attributes)
{
@ -139,7 +140,9 @@ static void transfer_attributes(
* Remove anonymous attributes that don't need to be propagated. */
Set<AttributeIDRef> attribute_ids = src_attributes.all_ids();
attribute_ids.remove("position");
attribute_ids.remove_if([](const AttributeIDRef &id) { return !id.should_be_kept(); });
attribute_ids.remove_if([&](const AttributeIDRef &id) {
return id.is_anonymous() && !propagation_info.propagate(id.anonymous_id());
});
for (const AttributeIDRef &id : attribute_ids) {
GAttributeReader src_attribute = src_attributes.lookup(id);
@ -606,7 +609,9 @@ static void dissolve_redundant_verts(const Span<MEdge> edges,
*
* Some special cases are needed for boundaries and non-manifold geometry.
*/
static Mesh *calc_dual_mesh(const Mesh &src_mesh, const bool keep_boundaries)
static Mesh *calc_dual_mesh(const Mesh &src_mesh,
const bool keep_boundaries,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Span<MVert> src_verts = src_mesh.verts();
const Span<MEdge> src_edges = src_mesh.edges();
@ -887,6 +892,7 @@ static Mesh *calc_dual_mesh(const Mesh &src_mesh, const bool keep_boundaries)
new_to_old_edges_map,
new_to_old_face_corners_map,
boundary_vertex_to_relevant_face_map,
propagation_info,
src_mesh.attributes(),
mesh_out->attributes_for_write());
@ -918,7 +924,8 @@ static void node_geo_exec(GeoNodeExecParams params)
const bool keep_boundaries = params.extract_input<bool>("Keep Boundaries");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
Mesh *new_mesh = calc_dual_mesh(*mesh, keep_boundaries);
Mesh *new_mesh = calc_dual_mesh(
*mesh, keep_boundaries, params.get_output_propagation_info("Dual Mesh"));
geometry_set.replace_mesh(new_mesh);
}
});

View File

@ -56,7 +56,7 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
}
struct IndexAttributes {
StrongAnonymousAttributeID duplicate_index;
AutoAnonymousAttributeID duplicate_index;
};
/* -------------------------------------------------------------------- */
@ -64,11 +64,13 @@ struct IndexAttributes {
* \{ */
static Map<AttributeIDRef, AttributeKind> gather_attributes_without_id(
const GeometrySet &geometry_set, const GeometryComponentType component_type)
const GeometrySet &geometry_set,
const GeometryComponentType component_type,
const AnonymousAttributePropagationInfo &propagation_info)
{
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{component_type}, component_type, false, attributes);
{component_type}, component_type, false, propagation_info, attributes);
attributes.remove("id");
return attributes;
};
@ -181,11 +183,12 @@ static void copy_attributes_without_id(GeometrySet &geometry_set,
const eAttrDomain domain,
const Span<int> offsets,
const IndexMask selection,
const AnonymousAttributePropagationInfo &propagation_info,
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes)
{
const Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, component_type);
geometry_set, component_type, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
@ -221,14 +224,16 @@ static void copy_attributes_without_id(GeometrySet &geometry_set,
* Copies the attributes for curve duplicates. If copying the curve domain, the attributes are
* copied with an offset fill, otherwise a mapping is used.
*/
static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<int> curve_offsets,
bke::CurvesGeometry &dst_curves)
static void copy_curve_attributes_without_id(
const GeometrySet &geometry_set,
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<int> curve_offsets,
const AnonymousAttributePropagationInfo &propagation_info,
bke::CurvesGeometry &dst_curves)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_CURVE);
geometry_set, GEO_COMPONENT_TYPE_CURVE, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
@ -318,7 +323,8 @@ static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
static void duplicate_curves(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
const IndexAttributes &attribute_outputs,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (!geometry_set.has_curves()) {
geometry_set.remove_geometry_during_modify();
@ -373,7 +379,8 @@ static void duplicate_curves(GeometrySet &geometry_set,
});
all_dst_offsets.last() = dst_points_num;
copy_curve_attributes_without_id(geometry_set, curves, selection, curve_offsets, new_curves);
copy_curve_attributes_without_id(
geometry_set, curves, selection, curve_offsets, propagation_info, new_curves);
copy_stable_id_curves(curves, selection, curve_offsets, new_curves);
@ -398,17 +405,19 @@ static void duplicate_curves(GeometrySet &geometry_set,
* Copies the attributes for face duplicates. If copying the face domain, the attributes are
* copied with an offset fill, otherwise a mapping is used.
*/
static void copy_face_attributes_without_id(GeometrySet &geometry_set,
const Span<int> edge_mapping,
const Span<int> vert_mapping,
const Span<int> loop_mapping,
const Span<int> offsets,
const IndexMask selection,
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes)
static void copy_face_attributes_without_id(
GeometrySet &geometry_set,
const Span<int> edge_mapping,
const Span<int> vert_mapping,
const Span<int> loop_mapping,
const Span<int> offsets,
const IndexMask selection,
const AnonymousAttributePropagationInfo &propagation_info,
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_MESH);
geometry_set, GEO_COMPONENT_TYPE_MESH, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
@ -506,7 +515,8 @@ static void copy_stable_id_faces(const Mesh &mesh,
static void duplicate_faces(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
const IndexAttributes &attribute_outputs,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (!geometry_set.has_mesh()) {
geometry_set.remove_geometry_during_modify();
@ -588,6 +598,7 @@ static void duplicate_faces(GeometrySet &geometry_set,
loop_mapping,
offsets,
selection,
propagation_info,
mesh.attributes(),
new_mesh->attributes_for_write());
@ -612,15 +623,17 @@ static void duplicate_faces(GeometrySet &geometry_set,
* Copies the attributes for edge duplicates. If copying the edge domain, the attributes are
* copied with an offset fill, for point domain a mapping is used.
*/
static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
const Span<int> point_mapping,
const Span<int> offsets,
const IndexMask selection,
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes)
static void copy_edge_attributes_without_id(
GeometrySet &geometry_set,
const Span<int> point_mapping,
const Span<int> offsets,
const IndexMask selection,
const AnonymousAttributePropagationInfo &propagation_info,
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_MESH);
geometry_set, GEO_COMPONENT_TYPE_MESH, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
@ -704,7 +717,8 @@ static void copy_stable_id_edges(const Mesh &mesh,
static void duplicate_edges(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
const IndexAttributes &attribute_outputs,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (!geometry_set.has_mesh()) {
geometry_set.remove_geometry_during_modify();
@ -756,6 +770,7 @@ static void duplicate_edges(GeometrySet &geometry_set,
vert_orig_indices,
edge_offsets,
selection,
propagation_info,
mesh.attributes(),
new_mesh->attributes_for_write());
@ -782,7 +797,8 @@ static void duplicate_edges(GeometrySet &geometry_set,
static void duplicate_points_curve(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
const IndexAttributes &attribute_outputs,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
@ -819,7 +835,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
new_curve_offsets.last() = dst_num;
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_CURVE);
geometry_set, GEO_COMPONENT_TYPE_CURVE, propagation_info);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
@ -885,7 +901,8 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
static void duplicate_points_mesh(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
const IndexAttributes &attribute_outputs,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Mesh &mesh = *geometry_set.get_mesh_for_read();
const Span<MVert> src_verts = mesh.verts();
@ -910,6 +927,7 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
ATTR_DOMAIN_POINT,
offsets,
selection,
propagation_info,
mesh.attributes(),
new_mesh->attributes_for_write());
@ -935,7 +953,8 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
static void duplicate_points_pointcloud(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
const IndexAttributes &attribute_outputs,
const AnonymousAttributePropagationInfo &propagation_info)
{
const PointCloud &src_points = *geometry_set.get_pointcloud_for_read();
@ -956,6 +975,7 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
ATTR_DOMAIN_POINT,
offsets,
selection,
propagation_info,
src_points.attributes(),
pointcloud->attributes_for_write());
@ -980,7 +1000,8 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
static void duplicate_points(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
const IndexAttributes &attribute_outputs,
const AnonymousAttributePropagationInfo &propagation_info)
{
Vector<GeometryComponentType> component_types = geometry_set.gather_component_types(true, true);
for (const GeometryComponentType component_type : component_types) {
@ -988,17 +1009,19 @@ static void duplicate_points(GeometrySet &geometry_set,
case GEO_COMPONENT_TYPE_POINT_CLOUD:
if (geometry_set.has_pointcloud()) {
duplicate_points_pointcloud(
geometry_set, count_field, selection_field, attribute_outputs);
geometry_set, count_field, selection_field, attribute_outputs, propagation_info);
}
break;
case GEO_COMPONENT_TYPE_MESH:
if (geometry_set.has_mesh()) {
duplicate_points_mesh(geometry_set, count_field, selection_field, attribute_outputs);
duplicate_points_mesh(
geometry_set, count_field, selection_field, attribute_outputs, propagation_info);
}
break;
case GEO_COMPONENT_TYPE_CURVE:
if (geometry_set.has_curves()) {
duplicate_points_curve(geometry_set, count_field, selection_field, attribute_outputs);
duplicate_points_curve(
geometry_set, count_field, selection_field, attribute_outputs, propagation_info);
}
break;
default:
@ -1018,7 +1041,8 @@ static void duplicate_points(GeometrySet &geometry_set,
static void duplicate_instances(GeometrySet &geometry_set,
const Field<int> &count_field,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
const IndexAttributes &attribute_outputs,
const AnonymousAttributePropagationInfo &propagation_info)
{
if (!geometry_set.has_instances()) {
geometry_set.clear();
@ -1062,6 +1086,7 @@ static void duplicate_instances(GeometrySet &geometry_set,
ATTR_DOMAIN_INSTANCE,
offsets,
selection,
propagation_info,
src_instances.attributes(),
dst_instances->attributes_for_write());
@ -1092,27 +1117,34 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<int> count_field = params.extract_input<Field<int>>("Amount");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
IndexAttributes attribute_outputs;
if (params.output_is_required("Duplicate Index")) {
attribute_outputs.duplicate_index = StrongAnonymousAttributeID("duplicate_index");
}
attribute_outputs.duplicate_index = params.get_output_anonymous_attribute_id_if_needed(
"Duplicate Index");
const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info(
"Geometry");
if (duplicate_domain == ATTR_DOMAIN_INSTANCE) {
duplicate_instances(geometry_set, count_field, selection_field, attribute_outputs);
duplicate_instances(
geometry_set, count_field, selection_field, attribute_outputs, propagation_info);
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
switch (duplicate_domain) {
case ATTR_DOMAIN_CURVE:
duplicate_curves(geometry_set, count_field, selection_field, attribute_outputs);
duplicate_curves(
geometry_set, count_field, selection_field, attribute_outputs, propagation_info);
break;
case ATTR_DOMAIN_FACE:
duplicate_faces(geometry_set, count_field, selection_field, attribute_outputs);
duplicate_faces(
geometry_set, count_field, selection_field, attribute_outputs, propagation_info);
break;
case ATTR_DOMAIN_EDGE:
duplicate_edges(geometry_set, count_field, selection_field, attribute_outputs);
duplicate_edges(
geometry_set, count_field, selection_field, attribute_outputs, propagation_info);
break;
case ATTR_DOMAIN_POINT:
duplicate_points(geometry_set, count_field, selection_field, attribute_outputs);
duplicate_points(
geometry_set, count_field, selection_field, attribute_outputs, propagation_info);
break;
default:
BLI_assert_unreachable();

View File

@ -18,9 +18,11 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curves")).propagate_all();
}
static Curves *edge_paths_to_curves_convert(const Mesh &mesh,
const IndexMask start_verts_mask,
const Span<int> next_indices)
static Curves *edge_paths_to_curves_convert(
const Mesh &mesh,
const IndexMask start_verts_mask,
const Span<int> next_indices,
const AnonymousAttributePropagationInfo &propagation_info)
{
Vector<int> vert_indices;
Vector<int> curve_offsets;
@ -58,8 +60,8 @@ static Curves *edge_paths_to_curves_convert(const Mesh &mesh,
if (vert_indices.is_empty()) {
return nullptr;
}
Curves *curves_id = bke::curves_new_nomain(
geometry::create_curve_from_vert_indices(mesh, vert_indices, curve_offsets, IndexRange(0)));
Curves *curves_id = bke::curves_new_nomain(geometry::create_curve_from_vert_indices(
mesh, vert_indices, curve_offsets, IndexRange(0), propagation_info));
return curves_id;
}
@ -87,7 +89,8 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
geometry_set.replace_curves(edge_paths_to_curves_convert(*mesh, start_verts, next_vert));
geometry_set.replace_curves(edge_paths_to_curves_convert(
*mesh, start_verts, next_vert, params.get_output_propagation_info("Curves")));
geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
});

View File

@ -33,7 +33,8 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
geometry::split_edges(*geometry_set.get_mesh_for_write(), mask);
geometry::split_edges(
*geometry_set.get_mesh_for_write(), mask, params.get_output_propagation_info("Mesh"));
}
});

View File

@ -27,7 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Bool>(N_("Selection")).default_value(true).field_on_all().hide_value();
b.add_input<decl::Vector>(N_("Offset"))
.subtype(PROP_TRANSLATION)
.implicit_field(implicit_field_inputs::normal)
.implicit_field_on_all(implicit_field_inputs::normal)
.hide_value();
b.add_input<decl::Float>(N_("Offset Scale")).default_value(1.0f).field_on_all();
b.add_input<decl::Bool>(N_("Individual")).default_value(true);
@ -61,8 +61,8 @@ static void node_update(bNodeTree *ntree, bNode *node)
}
struct AttributeOutputs {
StrongAnonymousAttributeID top_id;
StrongAnonymousAttributeID side_id;
AutoAnonymousAttributeID top_id;
AutoAnonymousAttributeID side_id;
};
static void save_selection_as_attribute(Mesh &mesh,
@ -1334,12 +1334,8 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<float3> final_offset{std::move(multiply_op)};
AttributeOutputs attribute_outputs;
if (params.output_is_required("Top")) {
attribute_outputs.top_id = StrongAnonymousAttributeID("Top");
}
if (params.output_is_required("Side")) {
attribute_outputs.side_id = StrongAnonymousAttributeID("Side");
}
attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top");
attribute_outputs.side_id = params.get_output_anonymous_attribute_id_if_needed("Side");
const bool extrude_individual = mode == GEO_NODE_EXTRUDE_MESH_FACES &&
params.extract_input<bool>("Individual");

View File

@ -198,8 +198,11 @@ static void node_geo_exec(GeoNodeExecParams params)
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
geometry_set.gather_attributes_for_propagation(
types, GEO_COMPONENT_TYPE_INSTANCES, false, attributes_to_propagate);
geometry_set.gather_attributes_for_propagation(types,
GEO_COMPONENT_TYPE_INSTANCES,
false,
params.get_output_propagation_info("Instances"),
attributes_to_propagate);
attributes_to_propagate.remove("position");
for (const GeometryComponentType type : types) {

View File

@ -26,7 +26,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void convert_instances_to_points(GeometrySet &geometry_set,
Field<float3> position_field,
Field<float> radius_field,
const Field<bool> selection_field)
const Field<bool> selection_field,
const AnonymousAttributePropagationInfo &propagation_info)
{
const bke::Instances &instances = *geometry_set.get_instances_for_read();
@ -62,6 +63,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES},
GEO_COMPONENT_TYPE_POINT_CLOUD,
false,
propagation_info,
attributes_to_propagate);
/* These two attributes are added by the implicit inputs above. */
attributes_to_propagate.remove("position");
@ -91,7 +93,8 @@ static void node_geo_exec(GeoNodeExecParams params)
convert_instances_to_points(geometry_set,
params.extract_input<Field<float3>>("Position"),
params.extract_input<Field<float>>("Radius"),
params.extract_input<Field<bool>>("Selection"));
params.extract_input<Field<bool>>("Selection"),
params.get_output_propagation_info("Points"));
geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_EDIT});
params.set_output("Points", std::move(geometry_set));
}

View File

@ -28,7 +28,7 @@ static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
for (const GeometryComponent *component : components) {
component->attributes()->for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) {
if (ignored_attributes.contains(attribute_id.name())) {
return true;
}
if (meta_data.data_type == CD_PROP_STRING) {
@ -143,7 +143,9 @@ static void join_components(Span<const VolumeComponent *> /*src_components*/,
}
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
static void join_component_type(Span<GeometrySet> src_geometry_sets,
GeometrySet &result,
const AnonymousAttributePropagationInfo &propagation_info)
{
Vector<const Component *> components;
for (const GeometrySet &geometry_set : src_geometry_sets) {
@ -176,6 +178,7 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet
geometry::RealizeInstancesOptions options;
options.keep_original_ids = true;
options.realize_instance_attributes = false;
options.propagation_info = propagation_info;
GeometrySet joined_components = geometry::realize_instances(
GeometrySet::create_with_instances(instances.release()), options);
result.add(joined_components.get_component_for_write<Component>());
@ -186,13 +189,17 @@ static void node_geo_exec(GeoNodeExecParams params)
{
Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Geometry");
const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info(
"Geometry");
GeometrySet geometry_set_result;
join_component_type<MeshComponent>(geometry_sets, geometry_set_result);
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
join_component_type<CurveComponent>(geometry_sets, geometry_set_result);
join_component_type<GeometryComponentEditData>(geometry_sets, geometry_set_result);
join_component_type<MeshComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<CurveComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<GeometryComponentEditData>(
geometry_sets, geometry_set_result, propagation_info);
params.set_output("Geometry", std::move(geometry_set_result));
}

View File

@ -38,9 +38,11 @@ static void node_init(bNodeTree * /*tree*/, bNode *node)
node->storage = data;
}
static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const Field<bool> &selection_field)
static PointCloud *pointcloud_merge_by_distance(
const PointCloud &src_points,
const float merge_distance,
const Field<bool> &selection_field,
const AnonymousAttributePropagationInfo &propagation_info)
{
bke::PointCloudFieldContext context{src_points};
FieldEvaluator evaluator{context, src_points.totpoint};
@ -52,7 +54,8 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points,
return nullptr;
}
return geometry::point_merge_by_distance(src_points, merge_distance, selection);
return geometry::point_merge_by_distance(
src_points, merge_distance, selection, propagation_info);
}
static std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
@ -97,7 +100,8 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) {
PointCloud *result = pointcloud_merge_by_distance(*pointcloud, merge_distance, selection);
PointCloud *result = pointcloud_merge_by_distance(
*pointcloud, merge_distance, selection, params.get_output_propagation_info("Geometry"));
if (result) {
geometry_set.replace_pointcloud(result);
}

View File

@ -814,18 +814,10 @@ static void node_geo_exec(GeoNodeExecParams params)
const float depth = params.extract_input<float>("Depth");
ConeAttributeOutputs attribute_outputs;
if (params.output_is_required("Top")) {
attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection");
}
if (params.output_is_required("Bottom")) {
attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection");
}
if (params.output_is_required("Side")) {
attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection");
}
if (params.output_is_required("UV Map")) {
attribute_outputs.uv_map_id = StrongAnonymousAttributeID("uv_map");
}
attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top");
attribute_outputs.bottom_id = params.get_output_anonymous_attribute_id_if_needed("Bottom");
attribute_outputs.side_id = params.get_output_anonymous_attribute_id_if_needed("Side");
attribute_outputs.uv_map_id = params.get_output_anonymous_attribute_id_if_needed("UV Map");
Mesh *mesh = create_cylinder_or_cone_mesh(radius_top,
radius_bottom,

View File

@ -107,10 +107,8 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
StrongAnonymousAttributeID uv_map_id;
if (params.output_is_required("UV Map")) {
uv_map_id = StrongAnonymousAttributeID("uv_map");
}
AutoAnonymousAttributeID uv_map_id = params.get_output_anonymous_attribute_id_if_needed(
"UV Map");
Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z, uv_map_id.get());

View File

@ -107,18 +107,10 @@ static void node_geo_exec(GeoNodeExecParams params)
}
ConeAttributeOutputs attribute_outputs;
if (params.output_is_required("Top")) {
attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection");
}
if (params.output_is_required("Bottom")) {
attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection");
}
if (params.output_is_required("Side")) {
attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection");
}
if (params.output_is_required("UV Map")) {
attribute_outputs.uv_map_id = StrongAnonymousAttributeID("uv_map");
}
attribute_outputs.top_id = params.get_output_anonymous_attribute_id_if_needed("Top");
attribute_outputs.bottom_id = params.get_output_anonymous_attribute_id_if_needed("Bottom");
attribute_outputs.side_id = params.get_output_anonymous_attribute_id_if_needed("Side");
attribute_outputs.uv_map_id = params.get_output_anonymous_attribute_id_if_needed("UV Map");
/* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */
Mesh *mesh = create_cylinder_or_cone_mesh(radius,

View File

@ -194,10 +194,8 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
StrongAnonymousAttributeID uv_map_id;
if (params.output_is_required("UV Map")) {
uv_map_id = StrongAnonymousAttributeID("uv_map");
}
AutoAnonymousAttributeID uv_map_id = params.get_output_anonymous_attribute_id_if_needed(
"UV Map");
Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y, uv_map_id.get());
BKE_id_material_eval_ensure_default_slot(&mesh->id);

View File

@ -77,10 +77,8 @@ static void node_geo_exec(GeoNodeExecParams params)
const int subdivisions = std::min(params.extract_input<int>("Subdivisions"), 10);
const float radius = params.extract_input<float>("Radius");
StrongAnonymousAttributeID uv_map_id;
if (params.output_is_required("UV Map")) {
uv_map_id = StrongAnonymousAttributeID("uv_map");
}
AutoAnonymousAttributeID uv_map_id = params.get_output_anonymous_attribute_id_if_needed(
"UV Map");
Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius, uv_map_id.get());
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));

View File

@ -362,10 +362,8 @@ static void node_geo_exec(GeoNodeExecParams params)
const float radius = params.extract_input<float>("Radius");
StrongAnonymousAttributeID uv_map_id;
if (params.output_is_required("UV Map")) {
uv_map_id = StrongAnonymousAttributeID("uv_map");
}
AutoAnonymousAttributeID uv_map_id = params.get_output_anonymous_attribute_id_if_needed(
"UV Map");
Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num, uv_map_id.get());
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));

View File

@ -36,7 +36,8 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(*mesh, selection);
bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(
*mesh, selection, params.get_output_propagation_info("Curve"));
geometry_set.replace_curves(bke::curves_new_nomain(std::move(curves)));
geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE});
});

View File

@ -24,7 +24,7 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
b.add_input<decl::Bool>(N_("Selection")).default_value(true).field_on_all().hide_value();
b.add_input<decl::Vector>(N_("Position")).implicit_field(implicit_field_inputs::position);
b.add_input<decl::Vector>(N_("Position")).implicit_field_on_all(implicit_field_inputs::position);
b.add_input<decl::Float>(N_("Radius"))
.default_value(0.05f)
.min(0.0f)
@ -49,7 +49,8 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
Field<float3> &position_field,
Field<float> &radius_field,
Field<bool> &selection_field,
const eAttrDomain domain)
const eAttrDomain domain,
const AnonymousAttributePropagationInfo &propagation_info)
{
const Mesh *mesh = geometry_set.get_mesh_for_read();
if (mesh == nullptr) {
@ -87,8 +88,11 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
radius.finish();
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_MESH},
GEO_COMPONENT_TYPE_POINT_CLOUD,
false,
propagation_info,
attributes);
attributes.remove("position");
const AttributeAccessor src_attributes = mesh->attributes();
@ -128,23 +132,42 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeGeometryMeshToPoints &storage = node_storage(params.node());
const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode;
const AnonymousAttributePropagationInfo &propagation_info = params.get_output_propagation_info(
"Points");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
switch (mode) {
case GEO_NODE_MESH_TO_POINTS_VERTICES:
geometry_set_mesh_to_points(
geometry_set, position, positive_radius, selection, ATTR_DOMAIN_POINT);
geometry_set_mesh_to_points(geometry_set,
position,
positive_radius,
selection,
ATTR_DOMAIN_POINT,
propagation_info);
break;
case GEO_NODE_MESH_TO_POINTS_EDGES:
geometry_set_mesh_to_points(
geometry_set, position, positive_radius, selection, ATTR_DOMAIN_EDGE);
geometry_set_mesh_to_points(geometry_set,
position,
positive_radius,
selection,
ATTR_DOMAIN_EDGE,
propagation_info);
break;
case GEO_NODE_MESH_TO_POINTS_FACES:
geometry_set_mesh_to_points(
geometry_set, position, positive_radius, selection, ATTR_DOMAIN_FACE);
geometry_set_mesh_to_points(geometry_set,
position,
positive_radius,
selection,
ATTR_DOMAIN_FACE,
propagation_info);
break;
case GEO_NODE_MESH_TO_POINTS_CORNERS:
geometry_set_mesh_to_points(
geometry_set, position, positive_radius, selection, ATTR_DOMAIN_CORNER);
geometry_set_mesh_to_points(geometry_set,
position,
positive_radius,
selection,
ATTR_DOMAIN_CORNER,
propagation_info);
break;
}
});

View File

@ -21,8 +21,10 @@ static void node_declare(NodeDeclarationBuilder &b)
}
/* One improvement would be to move the attribute arrays directly to the mesh when possible. */
static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
Field<bool> &selection_field)
static void geometry_set_points_to_vertices(
GeometrySet &geometry_set,
Field<bool> &selection_field,
const AnonymousAttributePropagationInfo &propagation_info)
{
const PointCloud *points = geometry_set.get_pointcloud_for_read();
if (points == nullptr) {
@ -41,8 +43,11 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_MESH, false, attributes);
geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_POINT_CLOUD},
GEO_COMPONENT_TYPE_MESH,
false,
propagation_info,
attributes);
Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0);
geometry_set.replace_mesh(mesh);
@ -73,7 +78,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry_set_points_to_vertices(geometry_set, selection_field);
geometry_set_points_to_vertices(
geometry_set, selection_field, params.get_output_propagation_info("Mesh"));
});
params.set_output("Mesh", std::move(geometry_set));

View File

@ -37,6 +37,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry::RealizeInstancesOptions options;
options.keep_original_ids = legacy_behavior;
options.realize_instance_attributes = !legacy_behavior;
options.propagation_info = params.get_output_propagation_info("Geometry");
geometry_set = geometry::realize_instances(geometry_set, options);
params.set_output("Geometry", std::move(geometry_set));
}

View File

@ -47,29 +47,42 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeGeometrySeparateGeometry &storage = node_storage(params.node());
const eAttrDomain domain = eAttrDomain(storage.domain);
auto separate_geometry_maybe_recursively = [&](GeometrySet &geometry_set,
const Field<bool> &selection) {
bool is_error;
if (domain == ATTR_DOMAIN_INSTANCE) {
/* Only delete top level instances. */
separate_geometry(
geometry_set, domain, GEO_NODE_DELETE_GEOMETRY_MODE_ALL, selection, is_error);
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
separate_geometry(
geometry_set, domain, GEO_NODE_DELETE_GEOMETRY_MODE_ALL, selection, is_error);
});
}
};
auto separate_geometry_maybe_recursively =
[&](GeometrySet &geometry_set,
const Field<bool> &selection,
const AnonymousAttributePropagationInfo &propagation_info) {
bool is_error;
if (domain == ATTR_DOMAIN_INSTANCE) {
/* Only delete top level instances. */
separate_geometry(geometry_set,
domain,
GEO_NODE_DELETE_GEOMETRY_MODE_ALL,
selection,
propagation_info,
is_error);
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
separate_geometry(geometry_set,
domain,
GEO_NODE_DELETE_GEOMETRY_MODE_ALL,
selection,
propagation_info,
is_error);
});
}
};
GeometrySet second_set(geometry_set);
if (params.output_is_required("Selection")) {
separate_geometry_maybe_recursively(geometry_set, selection_field);
separate_geometry_maybe_recursively(
geometry_set, selection_field, params.get_output_propagation_info("Selection"));
params.set_output("Selection", std::move(geometry_set));
}
if (params.output_is_required("Inverted")) {
separate_geometry_maybe_recursively(second_set, fn::invert_boolean_field(selection_field));
separate_geometry_maybe_recursively(second_set,
fn::invert_boolean_field(selection_field),
params.get_output_propagation_info("Inverted"));
params.set_output("Inverted", std::move(second_set));
}
}

View File

@ -249,7 +249,7 @@ static std::optional<TextLayout> get_text_layout(GeoNodeExecParams &params)
}
}
if (params.output_is_required("Line")) {
if (params.anonymous_attribute_output_is_required("Line")) {
layout.line_numbers.reinitialize(layout.positions.size());
for (const int i : layout.positions.index_range()) {
CharTrans &ct = chartransdata[i];
@ -278,7 +278,7 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
{
VFont *vfont = reinterpret_cast<VFont *>(params.node().id);
Map<int, int> handles;
bool pivot_required = params.output_is_required("Pivot Point");
bool pivot_required = params.anonymous_attribute_output_is_required("Pivot Point");
for (int i : layout.char_codes.index_range()) {
if (handles.contains(layout.char_codes[i])) {
@ -341,10 +341,10 @@ static void create_attributes(GeoNodeExecParams &params,
{
MutableAttributeAccessor attributes = instances.attributes_for_write();
if (params.output_is_required("Line")) {
StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line");
if (AutoAnonymousAttributeID line_id = params.get_output_anonymous_attribute_id_if_needed(
"Line")) {
SpanAttributeWriter<int> line_attribute = attributes.lookup_or_add_for_write_only_span<int>(
line_id.get(), ATTR_DOMAIN_INSTANCE);
*line_id, ATTR_DOMAIN_INSTANCE);
line_attribute.span.copy_from(layout.line_numbers);
line_attribute.finish();
params.set_output("Line",
@ -352,10 +352,10 @@ static void create_attributes(GeoNodeExecParams &params,
params.attribute_producer_name()));
}
if (params.output_is_required("Pivot Point")) {
StrongAnonymousAttributeID pivot_id = StrongAnonymousAttributeID("Pivot");
if (AutoAnonymousAttributeID pivot_id = params.get_output_anonymous_attribute_id_if_needed(
"Pivot Point")) {
SpanAttributeWriter<float3> pivot_attribute =
attributes.lookup_or_add_for_write_only_span<float3>(pivot_id.get(), ATTR_DOMAIN_INSTANCE);
attributes.lookup_or_add_for_write_only_span<float3>(*pivot_id, ATTR_DOMAIN_INSTANCE);
for (const int i : layout.char_codes.index_range()) {
pivot_attribute.span[i] = layout.pivot_points.lookup(layout.char_codes[i]);

File diff suppressed because it is too large Load Diff

View File

@ -70,7 +70,7 @@ GeometryInfoLog::GeometryInfoLog(const GeometrySet &geometry_set)
[&](const bke::AttributeIDRef &attribute_id,
const bke::AttributeMetaData &meta_data,
const GeometryComponent & /*component*/) {
if (attribute_id.is_named() && names.add(attribute_id.name())) {
if (!attribute_id.is_anonymous() && names.add(attribute_id.name())) {
this->attributes.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
}
});

View File

@ -9,10 +9,33 @@
#include "NOD_geometry_exec.hh"
#include "BLI_hash_md5.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
NodeAnonymousAttributeID::NodeAnonymousAttributeID(const Object &object,
const ComputeContext &compute_context,
const bNode &bnode,
const StringRef identifier)
{
const ComputeContextHash &hash = compute_context.hash();
{
std::stringstream ss;
ss << hash << "_" << object.id.name << "_" << bnode.identifier << "_" << identifier;
long_name_ = ss.str();
}
{
uint64_t hash_result[2];
BLI_hash_md5_buffer(long_name_.data(), long_name_.size(), hash_result);
std::stringstream ss;
ss << ".a_" << std::hex << hash_result[0] << hash_result[1];
name_ = ss.str();
BLI_assert(name_.size() < MAX_CUSTOMDATA_LAYER_NAME);
}
}
void GeoNodeExecParams::error_message_add(const NodeWarningType type,
const StringRef message) const
{